Required Plugins
Superpowers plugin: MUST be active for all work using this skill. Use throughout the entire build pipeline — design decisions, code generation, debugging, quality checks, and any task where it offers enhanced capabilities. If superpowers provides a better way to accomplish something, prefer it over the default approach.
Frontend Design plugin (webapp-gui-design): MUST be active for all visual output from this skill. Use for design system work, component styling, layout decisions, colour selection, typography, responsive design, and visual QA.
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.
- Basic Mapping & Visualization (core Leaflet)
- Spatial Queries (Turf.js)
- Geocoding (Nominatim or Leaflet Control Geocoder)
- Buffering & Zone Analysis (Turf.js)
- Heatmaps & Density (leaflet.heat)
- Routing & Network Analysis (OSRM / GraphHopper + leaflet-routing-machine)
- Drawing & Editing (leaflet.draw / Geoman)
- Measurement Tools (leaflet-measure or custom)
- Clustering & Aggregation (leaflet.markercluster)
- Spatial Analysis (Turf.js)
- Time-Based Analysis (custom + heat)
- 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 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)
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
/>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
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
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.
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)