πΊοΈ Map & Geospatial β
The map tab uses @rnmapbox/maps v10 with Turf.js for geospatial calculations.
Token Setup β
Two Mapbox tokens are required:
| Token | Env Var | Type | Usage |
|---|---|---|---|
Public (pk.) | EXPO_PUBLIC_MAPBOX_ACCESS_TOKEN | Runtime | Map rendering |
Secret (sk.) | RNMAPBOX_MAPS_DOWNLOAD_TOKEN | Build-time only | Downloads Mapbox SDK during pod install / Gradle |
CAUTION
Never commit the secret sk. token. It is only needed in .env locally and in CI secrets β not in source code.
Layer Components (components/map/) β
| Component | Purpose |
|---|---|
map-view | Root MapboxGL.MapView wrapper with camera, gesture config |
fields-layer | Renders company fields as filled polygons |
field-drawing-layer | Live preview layer while drawing a new field |
notes-layer | Renders note pins (location-tagged drafts) |
parcels-layer | Renders cadastral parcels from the API |
map-controls | Zoom, compass, and locate-me buttons |
map-drawing-toolbar | Undo, finish, cancel toolbar for polygon drawing |
Tapping Features β
Tapping a field, note, or parcel opens the corresponding action sheet via SheetManager:
tsx
// Simplified from fields-layer
onPress={(feature) => {
SheetManager.show('field-action-sheet', {
payload: { fieldId: feature.id },
});
}}Drawing Fields (hooks/use-drawing.ts) β
Field drawing uses a useReducer state machine:
idle β drawing β (undo removes last vertex) β confirming β saved
β
cancelled β idleKey actions:
ts
const { state, addVertex, undo, finishDrawing, cancelDrawing } = useDrawing();
// state.vertices: [lng, lat][] β the polygon points so far
// state.mode: 'idle' | 'drawing' | 'confirming'Parcel Loading β
Parcels are fetched by map bounding box to avoid loading the entire country dataset. The useParcels(bbox) hook debounces bounds changes by 500ms to avoid hammering the API while the user pans.
ts
const [bbox, setBbox] = useState<BoundingBox | null>(null);
const { data: parcels } = useParcels(bbox);
// Update bbox in onMapIdle or onRegionDidChange (debounced externally)The bbox parameter is [minLng, minLat, maxLng, maxLat].
Turf.js Utilities (lib/map/) β
Common geospatial helpers used in the app:
ts
import * as turf from '@turf/turf';
// Check if a point is inside a polygon (e.g. note inside field)
turf.booleanPointInPolygon(point, polygon);
// Calculate field area in hectares
const area = turf.area(polygon) / 10_000; // mΒ² β ha
// Get bounding box of a polygon
const [minLng, minLat, maxLng, maxLat] = turf.bbox(polygon);