--- name: gis-mapping description: "Use for web apps that need Leaflet-first GIS mapping, location selection, map-driven UIs, or geofencing validation. Covers Leaflet setup, optional tile providers, data storage, and backend validation patterns." --- # GIS Mapping (Leaflet-First) ## Quick Summary - Leaflet-first mapping for web apps (default engine) - Optional OpenStreetMap tiles or other providers - Location selection (marker, polygon, rectangle) + map UI patterns - Geofencing enforcement with client + server validation (see geofencing.md) - Performance, clustering, and safe storage of spatial data ## Capability Index (Leaflet-First) Use this index to load only the section you need. Details live in [skills/gis-mapping/references/leaflet-capabilities.md](skills/gis-mapping/references/leaflet-capabilities.md). 1. **Basic Mapping & Visualization** (core Leaflet) 2. **Spatial Queries** (Turf.js) 3. **Geocoding** (Nominatim or Leaflet Control Geocoder) 4. **Buffering & Zone Analysis** (Turf.js) 5. **Heatmaps & Density** (leaflet.heat) 6. **Routing & Network Analysis** (OSRM / GraphHopper + leaflet-routing-machine) 7. **Drawing & Editing** (leaflet.draw / Geoman) 8. **Measurement Tools** (leaflet-measure or custom) 9. **Clustering & Aggregation** (leaflet.markercluster) 10. **Spatial Analysis** (Turf.js) 11. **Time-Based Analysis** (custom + heat) 12. **Export & Printing** (html2canvas/jsPDF + GeoJSON export) ## When to Use - You need interactive maps for customers, assets, farms, or delivery zones - Users must select or edit locations on a map - You must enforce geo-fencing or boundary validation - You need GIS data display with filters, legends, and clustering ## Key Patterns - Leaflet is the default mapping engine. - Always include attribution for the chosen tile provider. - Capture geometry in GeoJSON and validate server-side. - Use bounding-box checks before deeper polygon math. - Cluster markers when data is large or dense. - Store optional tile provider API keys in system settings (e.g., `osm_api_key`) and load them at runtime. - Default to terrain tiles (OpenTopoMap) for administrative borders. ## Leaflet Standardization (BIRDC) - Use a shared Leaflet configuration for tile URLs, attribution, and defaults. - Prefer a shared loader or include pattern so Leaflet CSS/JS is consistent across pages. - Use [public/farmer-profile.php](public/farmer-profile.php) as the canonical Leaflet UI reference. ## Stack Choices (Frontend) - **Leaflet (default)**: Lightweight, fastest to implement, best for most apps. - **OpenLayers (optional)**: Heavyweight GIS controls and projections. - **MapLibre GL JS (optional)**: Vector tiles, smoother at scale. ## Data Model & Storage - **Point**: `latitude` + `longitude` (DECIMAL(10,7)) - **Polygon/Boundary**: Store as GeoJSON (TEXT/JSON) - **Metadata**: `location_label`, `address`, `last_updated_at` Recommended formats: - GeoJSON for portability across JS + backend - WKT only if your DB tooling depends on it ## Map Initialization (Leaflet) ```html ``` ```javascript const map = L.map("map").setView([0.3476, 32.5825], 12); const osmApiKey = window.osmApiKey || ""; const osmTileUrl = osmApiKey ? `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?api_key=${osmApiKey}` : "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"; const osm = L.tileLayer(osmTileUrl, { attribution: "© OpenStreetMap contributors", maxZoom: 19, }); const terrain = L.tileLayer( "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", { attribution: "© OpenTopoMap", maxZoom: 17, }, ); terrain.addTo(map); ``` ## Location Selection Patterns ### Marker (Point) - Single click adds/updates a marker - Store coordinates in hidden inputs ```javascript let marker; map.on("click", (e) => { if (marker) map.removeLayer(marker); marker = L.marker(e.latlng).addTo(map); document.querySelector("#latitude").value = e.latlng.lat; document.querySelector("#longitude").value = e.latlng.lng; }); ``` ### Polygon / Rectangle (Area) Use Leaflet Draw or Geoman. Store GeoJSON geometry. ```javascript const drawn = new L.FeatureGroup().addTo(map); const drawControl = new L.Control.Draw({ draw: { polygon: true, rectangle: true, marker: false, circle: false }, edit: { featureGroup: drawn }, }); map.addControl(drawControl); map.on("draw:created", (e) => { drawn.clearLayers(); drawn.addLayer(e.layer); document.querySelector("#boundary_geojson").value = JSON.stringify( e.layer.toGeoJSON(), ); }); ``` ## Geofencing (Overview) Geofencing must be enforced at two levels: - **UI constraint**: prevent invalid selections in the browser - **Server constraint**: verify boundaries in backend validation See geofencing.md for full patterns, point-in-polygon checks, and multi-geometry rules. ## UI Patterns - Filters + stats in a side card, map in a large canvas - Legend to toggle categories (customer groups, territories) - Clustering when markers exceed ~200 ## Performance & UX - Debounce search and map redraw - Use marker clustering or server-side tiling - Lazy load heavy layers - Simplify large polygons for UI display ## Backend Validation Always validate coordinates server-side: - Ensure latitude is between -90 and 90 - Ensure longitude is between -180 and 180 - Enforce geofence boundaries with the same logic as UI - Load optional tile provider keys (for example `osm_api_key`) from system settings when building map pages ## Privacy & Security - Location data is sensitive: protect with permissions - Never trust client-only validation - Avoid exposing private coordinates without authorization ## Recommended Plugins - Leaflet Draw or Leaflet Geoman (drawing tools) - Leaflet MarkerCluster (performance) - Leaflet Control Geocoder (search) - Turf.js (geospatial analysis) ## References - geofencing.md (sub-skill) - references/leaflet-capabilities.md - references/leaflet-arcgis-equivalents.md (index)