Skip to content

codefordayton/new_monty_lots

Repository files navigation

Monty Lots

A civic tech platform for exploring property, parcel, and election data in Montgomery County, Ohio

Monty Lots is an open-source geospatial data platform built by Code for Dayton to make property, parcel, and election information accessible, searchable, and useful for residents, researchers, civic organizations, and Democracy Fellows.

License: ISC Node.js

🌟 Features

Core Capabilities

  • πŸ” Intelligent Search - Search properties by address or parcel ID with real-time filtering
  • πŸ—³οΈ Election Data Visualization - Interactive maps of 2024 and 2025 election results by precinct
  • πŸ“Š Large Dataset Handling - Automatic clustering for datasets with 1000+ features
  • 🎯 Manual Layer Loading - User-controlled layer loading to optimize performance
  • πŸ—ΊοΈ Interactive Mapping - Full-screen Leaflet interface with dynamic layer discovery
  • πŸ”— Shareable URLs - Search results can be bookmarked and shared via URL parameters
  • πŸ“± Responsive Design - Works seamlessly on desktop, tablet, and mobile devices

Election Features

  • πŸ“ˆ Voter Turnout Choropleth - Visualize turnout rates with 8-tier color scale (red to green)
  • βš–οΈ Comparison Mode - Side-by-side comparison of 2024 vs 2025 elections with diverging colors
  • πŸ”Ž Advanced Filtering - Filter precincts by name, turnout range, or race winner
  • πŸ“‹ Full Election History - View complete race results for any precinct
  • 🎨 Dynamic Race Selection - Browse and visualize any race from 180+ available contests
  • πŸ“Š Summary Statistics - Average, min, and max turnout across all precincts

Technical Features

  • Dynamic Service Discovery - Automatically catalogs all GeoJSON files in the data directory
  • GeoJSON Validation - Automatic validation of GeoJSON syntax with detailed error reporting
  • S3-Compatible Storage - Optional sync from DigitalOcean Spaces, AWS S3, Backblaze B2, or any S3-compatible storage
  • Koop FeatureServer API - Industry-standard GIS REST API for data access
  • Layer Visibility Controls - Toggle layers on/off without reloading
  • Real-time Status Indicators - Visual feedback for loading states and feature counts
  • Health Check Endpoint - Monitoring-ready /health endpoint for production deployments

πŸš€ Quick Start

Prerequisites

  • Node.js v14 or higher
  • npm (comes with Node.js)

Installation

  1. Clone the repository

    git clone https://github.com/codefordayton/new_monty_lots.git
    cd new_monty_lots
  2. Install dependencies

    npm install
  3. Start the server

    npm start
  4. Open in your browser

Development Mode

For active development with auto-restart on file changes:

npm run dev

πŸ“– Usage Guide

Viewing Property Data

  1. Load Layers - Click the "Load" button next to any layer in the sidebar to display it on the map
  2. Search Properties - Use the search box to filter by address or parcel ID
  3. View Details - Click any feature on the map to see its properties in a popup
  4. Share Results - Copy the URL to share specific search results with others

Exploring Election Data

  1. Select Election Year - Choose between 2024 and 2025 elections
  2. Choose a Race - Select from dropdown of 180+ races or view voter turnout
  3. View Results - Precinct boundaries are colored by vote percentage or turnout
  4. Compare Years - Toggle "Comparison Mode" to see changes between 2024 and 2025
  5. Filter Precincts - Use advanced filters to search by name, turnout, or winner
  6. View History - Click any precinct and select "View Full Election History"

Search Capabilities

The search function supports:

  • Address search - Enter full or partial street addresses
  • Parcel ID search - Search by tax parcel numbers
  • Real-time filtering - Results update as you type (300ms debounce)
  • Result counts - Shows matching features out of total dataset

Performance Optimization

The application automatically optimizes for dataset size:

  • < 1,000 features - Standard Leaflet layers
  • > 1,000 features - Marker clustering enabled
  • > 5,000 features - Warning displayed, clustering highly optimized
  • Manual loading - Users control which layers load to manage memory

πŸ—οΈ Architecture

Technology Stack

Backend:

Frontend:

  • Leaflet v1.9.4 - Interactive mapping library
  • Leaflet.markercluster - Marker clustering
  • Vanilla JavaScript - No framework dependencies
  • Responsive CSS - Mobile-first design

System Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Client Browser                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Leaflet Map Interface (index.html)                β”‚ β”‚
β”‚  β”‚  β€’ Dynamic layer loading                           β”‚ β”‚
β”‚  β”‚  β€’ Search & filter UI                              β”‚ β”‚
β”‚  β”‚  β€’ Marker clustering                               β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚ HTTP/JSON
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Koop Server (index.js)                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Custom Endpoints:                                 β”‚ β”‚
β”‚  β”‚  β€’ GET /              β†’ Serve frontend             β”‚ β”‚
β”‚  β”‚  β€’ GET /catalog       β†’ List all services          β”‚ β”‚
β”‚  β”‚  β€’ GET /health        β†’ Health check               β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  Koop FeatureServer API:                           β”‚ β”‚
β”‚  β”‚  β€’ /file-geojson/rest/services/{layer}/FeatureServer β”‚
β”‚  β”‚  β€’ /file-geojson/rest/services/{layer}/FeatureServer/0/query β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚ File I/O
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           provider-data/ Directory                       β”‚
β”‚  β€’ housing.geojson    (47 MB)                           β”‚
β”‚  β€’ registry.geojson   (19 MB)                           β”‚
β”‚  β€’ [any .geojson files are auto-discovered]            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Decisions

  1. Manual Layer Loading - Users explicitly load layers to prevent overwhelming browser memory with large datasets
  2. Automatic Clustering - Point features in large datasets (>1000 features) automatically use clustering
  3. Zero Configuration - Drop any .geojson file in provider-data/ and it's automatically served
  4. Service Catalog Pattern - Custom /catalog endpoint provides metadata about all available layers
  5. Progressive Enhancement - Core functionality works without JavaScript, enhanced with interactivity

πŸ”Œ API Reference

Catalog Endpoint

GET /catalog

Returns metadata about all available GeoJSON services.

Response:

{
  "services": [
    {
      "id": "housing",
      "name": "Housing",
      "type": "FeatureServer",
      "url": "/file-geojson/rest/services/housing/FeatureServer",
      "queryUrl": "/file-geojson/rest/services/housing/FeatureServer/0/query"
    }
  ],
  "count": 2
}

FeatureServer Endpoints

GET /file-geojson/rest/services/{layer}/FeatureServer

Returns service metadata for a specific layer.

GET /file-geojson/rest/services/{layer}/FeatureServer/0/query

Query features from a layer.

Query Parameters:

  • f=geojson - Return format (geojson, json)
  • where=1=1 - SQL-style where clause
  • outFields=* - Fields to return
  • returnCountOnly=true - Return only feature count

Example:

# Get all features as GeoJSON
curl "http://localhost:8080/file-geojson/rest/services/housing/FeatureServer/0/query?f=geojson&where=1=1&outFields=*"

# Get feature count only
curl "http://localhost:8080/file-geojson/rest/services/housing/FeatureServer/0/query?f=json&where=1=1&returnCountOnly=true"

Health Check

GET /health

Returns server health status for monitoring.

Response:

{
  "status": "healthy",
  "timestamp": "2025-11-10T12:34:56.789Z"
}

S3 Sync Endpoints

GET /api/sync/status

Returns current S3 sync configuration status.

Response:

{
  "enabled": true,
  "bucket": "my-geojson-data",
  "endpoint": "https://nyc3.digitaloceanspaces.com",
  "autoSync": true,
  "syncInterval": 0
}

POST /api/sync

Manually trigger a sync from S3 storage. Only available when S3_ENABLED=true.

Response:

{
  "success": true,
  "filesSync": 2,
  "totalFiles": 2
}

πŸ“ Project Structure

new_monty_lots/
β”œβ”€β”€ index.js                    # Koop server with custom endpoints
β”œβ”€β”€ index.html                  # Frontend map interface
β”œβ”€β”€ static/                     # Frontend assets
β”‚   β”œβ”€β”€ scripts/
β”‚   β”‚   β”œβ”€β”€ components/         # UI components (ElectionUI, Filters, etc.)
β”‚   β”‚   β”œβ”€β”€ services/           # Data services (Catalog, Layer, Style)
β”‚   β”‚   β”œβ”€β”€ state/              # State management (MapState, LayerState)
β”‚   β”‚   β”œβ”€β”€ utils/              # Utilities (popupBuilder, fieldAnalyzer)
β”‚   β”‚   └── main.js            # Application entry point
β”‚   └── styles/
β”‚       └── main.css           # Application styles
β”œβ”€β”€ lib/
β”‚   └── s3-sync.js             # S3-compatible storage sync module
β”œβ”€β”€ config/                     # Layer styling configuration
β”‚   β”œβ”€β”€ styles.json            # Style configuration index
β”‚   └── layers/                # Layer-specific styling rules
β”œβ”€β”€ data/                       # Election and property data
β”‚   β”œβ”€β”€ README.md              # Data organization documentation
β”‚   β”œβ”€β”€ raw/                   # Raw source data (PDFs, CSVs)
β”‚   └── elections/             # Processed election JSON files
β”œβ”€β”€ docs/
β”‚   └── DIGITALOCEAN_SPACES_SETUP.md  # S3 storage setup guide
β”œβ”€β”€ provider-data/              # GeoJSON files served via Koop
β”‚   β”œβ”€β”€ housing.geojson        # Housing/property data (47MB)
β”‚   β”œβ”€β”€ registry.geojson       # Registry data (19MB)
β”‚   β”œβ”€β”€ precincts.geojson      # Base precinct boundaries
β”‚   β”œβ”€β”€ precincts_2024.geojson # 2024 election results (13MB)
β”‚   └── precincts_2025.geojson # 2025 election results (13MB)
β”œβ”€β”€ .env.example                # Environment variable template
β”œβ”€β”€ package.json                # Dependencies and scripts
β”œβ”€β”€ README.md                  # This file
β”œβ”€β”€ CONTRIBUTING.md            # Contribution guidelines
β”œβ”€β”€ CLAUDE.md                 # AI assistant documentation
└── deploy.md                 # Deployment guide

πŸ’Ύ Adding Data

Option 1: Local Files (Default)

Simply add .geojson files to the provider-data/ directory:

# Copy your GeoJSON file
cp my-data.geojson provider-data/

# Restart the server (or nodemon will auto-restart)
npm start

The new layer will automatically:

  • Appear in the /catalog endpoint
  • Show up in the frontend sidebar
  • Be queryable via the Koop API
  • Use the filename (without extension) as the layer ID

Option 2: S3-Compatible Storage (DigitalOcean Spaces, AWS S3, etc.)

For production deployments or large datasets, you can store GeoJSON files in S3-compatible object storage:

Supported providers:

  • DigitalOcean Spaces
  • AWS S3
  • Backblaze B2
  • Wasabi
  • MinIO (self-hosted)
  • Any S3-compatible storage

Setup:

  1. Copy the environment example

    cp .env.example .env
  2. Configure your S3 settings in .env

    S3_ENABLED=true
    S3_BUCKET=my-geojson-data
    S3_ENDPOINT=https://nyc3.digitaloceanspaces.com  # For DigitalOcean Spaces
    S3_ACCESS_KEY_ID=your-access-key
    S3_SECRET_ACCESS_KEY=your-secret-key
  3. Start the server

    npm start
    # Files will automatically sync from S3 to local cache on startup

Features:

  • βœ… Automatic sync on server startup
  • βœ… Optional periodic sync at configurable intervals
  • βœ… Manual sync via API: POST /api/sync
  • βœ… Check sync status: GET /api/sync/status
  • βœ… All files are validated before serving
  • βœ… Works seamlessly with existing Koop provider

See docs/DIGITALOCEAN_SPACES_SETUP.md for detailed setup instructions.

Data Guidelines

  • Format: Valid GeoJSON (FeatureCollection recommended)
  • Coordinate System: WGS84 (EPSG:4326) or Web Mercator (EPSG:3857)
  • File Size: No strict limit with S3 storage; < 50MB for local-only hosting
  • Naming: Use lowercase, hyphens for spaces (e.g., property-parcels.geojson)
  • Validation: All files are automatically validated for correct GeoJSON syntax

🚒 Deployment

Quick Deploy Options

Railway (Recommended for quick deployments)

# Install Railway CLI
npm i -g @railway/cli

# Deploy
railway up

Render

  1. Connect your GitHub repository
  2. Set build command: npm install
  3. Set start command: npm start
  4. Deploy automatically on push

DigitalOcean App Platform

  1. Create new app from GitHub
  2. Configure as Node.js service
  3. Set environment variable PORT=8080

See deploy.md for detailed deployment instructions, including:

  • Environment variable configuration
  • Object storage integration (AWS S3, GCS, Azure)
  • Scaling considerations
  • Performance optimization

🀝 Contributing

We welcome contributions from developers, designers, GIS professionals, and civic tech enthusiasts!

Ways to contribute:

  • πŸ› Report bugs and request features via GitHub Issues
  • πŸ’» Submit pull requests for bug fixes or new features
  • πŸ“– Improve documentation
  • πŸ§ͺ Test with different datasets and provide feedback
  • 🎨 Enhance UI/UX design

Development Workflow

IMPORTANT: Always create a feature branch for your work. Never commit directly to main.

  1. Create a feature branch

    git checkout -b feature/your-feature-name
    # or for bug fixes:
    git checkout -b fix/issue-number-description
  2. Make your changes and commit

    git add .
    git commit -m "Description of your changes"
  3. Push to your branch

    git push origin feature/your-feature-name
  4. Open a Pull Request

    • Go to GitHub and create a PR from your branch to main
    • Link any related issues
    • Request review from maintainers

Please read CONTRIBUTING.md for detailed guidelines on:

  • Development workflow
  • Code style and standards
  • Testing procedures
  • Pull request process

πŸ—ΊοΈ Roadmap

Planned Features

v2.0 - Enhanced Search & Analytics

  • Advanced spatial queries (radius search, polygon selection)
  • Property comparison tool
  • Export search results to CSV/GeoJSON
  • Custom layer styling via UI

v3.0 - Data Integration

  • Connect to live county data APIs
  • Automatic data refresh/sync
  • Historical data timeline view
  • Multi-county support

v4.0 - Collaboration & Community

  • User accounts and saved searches
  • Community annotations and notes
  • Public/private layer sharing
  • Embeddable map widgets

Technical Improvements

  • Unit and integration tests
  • TypeScript migration
  • GraphQL API option
  • WebSocket support for real-time updates
  • Vector tile serving for better performance
  • Offline PWA capabilities

πŸ“Š Use Cases

For Residents:

  • Research properties before purchase or rental
  • Understand neighborhood boundaries and zoning
  • Track property development in their area
  • Explore election results in their precinct
  • Compare turnout and voting patterns over time

For Researchers & Democracy Fellows:

  • Analyze property and election patterns across Montgomery County
  • Study voter turnout trends by precinct and demographic area
  • Identify geographic patterns in election results
  • Compare year-over-year changes in voter participation
  • Export data for academic research and civic analysis

For Civic Organizations:

  • Identify underutilized properties
  • Plan community development projects
  • Support affordable housing initiatives
  • Understand voting patterns for outreach campaigns
  • Target areas with low voter turnout

For Developers:

  • Build applications on top of the API
  • Integrate property and election data into other civic tech tools
  • Create custom visualizations and dashboards
  • Access 180+ race results via structured JSON

πŸ™ Acknowledgments

  • Code for Dayton - Civic tech volunteer organization building this platform
  • Democracy Fellows - Supporting civic engagement through election data transparency
  • Montgomery County Board of Elections - Providing comprehensive election results data
  • Montgomery County, Ohio - Open data access for property records
  • Koop Team - Excellent open-source GIS data transformation framework
  • OpenStreetMap - Map tile data and geographic basemaps

πŸ“„ License

This project is licensed under the ISC License - see the LICENSE file for details.

πŸ“ž Contact & Support


Built with ❀️ by Code for Dayton volunteers

Making public data public and accessible for everyone

About

No description, website, or topics provided.

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors