# Yard Map Static yard mapping site built with Leaflet and plain browser modules. ## Status The project currently has: - A static Leaflet map with OSM raster tiles. - Client-side JavaScript-authored yard geometry converted to GeoJSON at load time. - Typed feature classes with centralized styling. - Per-anchor local coordinate rotation for offset-based authoring. - Feature relationship metadata (`id`, `parentId`, `groupIds`) and a top-level group catalog for future visibility controls. - Example yard data mostly copied from `buildings.gpkg` and then preserved as JS modules. The next likely product direction is map-layer visibility controls driven by the existing feature groups and parent-child relationships. ## Authoring model Geometry stays in JavaScript, then gets converted to GeoJSON client-side at load time. - Use absolute coordinates with `point(lat, lon)`. - Use absolute GeoJSON-style coordinates with `lngLat(lon, lat)` or `.trace([[lon, lat], ...])`. - Use local offsets with `offset(origin)` inside `.add(...)`, for example `.add(offset(lotCorner), south(ft(15)), east(ft(10)))`. - Build typed features with classes like `new House()`, `new Garage()`, `new Tree()`, and `new Sprinkler()` so styles live in one place. - Add relationships with `.childOf(...)` and `.inGroup(...)` so future UI can show or hide related features together. The example yard is now split by concern: - `src/data/boundaries.js` - `src/data/buildings.js` - `src/data/pavement.js` - `src/data/plants.js` - `src/data/irrigation.js` The typed feature registry lives in `src/lib/feature-types.js`, feature classes live in `src/lib/yard-features.js`, and shared coordinate helpers live in `src/lib/geometry.js`. Local `north`, `south`, `east`, and `west` offsets are rotated per anchor. In this example, `topLeftLotCorner` in `src/data/anchors.js` carries `assumedNorthClockwiseDegrees` from `src/data/settings.js`. A negative value means that anchor's local north is counterclockwise from true north, so local east drifts a bit toward true north. Most example geometry was copied from `buildings.gpkg` and preserved as JS-authored coordinates. Tree records also carry custom attributes like canopy diameter, height, and planted date for popup rendering. Feature relationships are exported with each GeoJSON feature as stable `id`, `parentId`, and `groupIds` properties, and the collection also exposes a top-level `groups` array. The current sample marks the porch and deck as part of the house and groups the Medora junipers under `Juniper Trees`. ## Project structure - `src/lib/geometry.js`: low-level coordinate and offset helpers. - `src/lib/feature-types.js`: centralized default styling by feature kind. - `src/lib/yard-features.js`: typed feature classes, feature/group metadata, and GeoJSON export. - `src/data/settings.js`: local authoring settings such as lot rotation. - `src/data/anchors.js`: named anchor points for offset-based authoring. - `src/data/boundaries.js`: lot boundary and fence features. - `src/data/buildings.js`: house, garage, porch, deck, and building-related groups. - `src/data/pavement.js`: driveway and walkway geometry. - `src/data/plants.js`: flower beds, trees, and tree groups. - `src/data/irrigation.js`: sprinkler features. - `src/data/yard.js`: top-level yard collection assembly. - `src/main.js`: Leaflet rendering, legend generation, and popup rendering. ## Key decisions - Geometry is authored in JavaScript, not GeoJSON, so yard data can mix exact coordinates with local offset expressions. - Styling is defined by feature type rather than repeated inline for each feature. - Local offsets rotate per anchor, not globally, so future anchors can carry different orientations. - Grouping is stored in the data model now so later show/hide behavior can be implemented without restructuring features. - The example GPKG data was used as a one-time source to seed JS-authored geometry, not as a runtime dependency. ## GeoPackage notes - `buildings.gpkg` was used to seed most sample geometry. - The `lot_boundary` layer had mixed or incorrect CRS metadata. - The usable lot polygon was `fid = 2`, and it had to be treated as `EPSG:26914` during one-time extraction before converting to JS coordinates. ## Validation Useful local checks: ```bash node -e "import('./src/data/yard.js').then(({ yardGeoJSON }) => console.log(yardGeoJSON.features.length))" ``` ```bash python3 -m http.server 4173 ``` The first check validates that the authored yard data still imports and exports cleanly. The second runs the static site locally. ## Run locally Because the app uses ES modules, serve it over HTTP instead of opening `index.html` directly. ```bash python3 -m http.server 4173 ``` Then open . ## Zoom behavior The yard overlays are vector data, so they stay sharp at any zoom level the map allows. The current basemap is still raster OSM tiles, so above zoom 19 Leaflet reuses and scales those tiles instead of fetching higher-resolution imagery. If you want a truly crisp basemap at arbitrary close zoom, you need a vector tile or self-hosted tile source rather than the standard public OSM raster tile endpoint. ## Future direction - Add UI controls for toggling groups and possibly parent-linked feature clusters. - Decide whether toggles should operate on feature groups, feature kinds, parent-child trees, or all three. - If sharper basemap zoom is needed, replace the raster OSM background with a vector tile or self-hosted tile solution. - If more data import is needed from GIS sources, prefer one-time extraction into JS-authored modules rather than making the app depend on runtime GIS tooling.