110 lines
5.6 KiB
Markdown
110 lines
5.6 KiB
Markdown
# 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 <http://localhost:4173>.
|
|
|
|
## 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.
|