Skip to content

Commit 0c74ebd

Browse files
Olivier Bernardclaude
authored andcommitted
refactor: Simplify vegetation mesh strategies with Turf utilities
- TreeRowStrategy: Replace manual haversine distance calculation with @turf/line-chunk for cleaner interval-based point spacing - vegetationUtils: Use @turf/bbox for bounding box calculation instead of manual min/max iteration - vegetationUtils: Improve hash function precision (multiply by 1e6 instead of 1000) - Both changes reduce complexity and leverage battle-tested geospatial libraries Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 9b0cfa7 commit 0c74ebd

4 files changed

Lines changed: 30 additions & 47 deletions

File tree

bun.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"@kobalte/core": "^0.13.11",
4747
"@mapbox/vector-tile": "^2.0.4",
4848
"@turf/area": "^7.3.4",
49+
"@turf/bbox": "^7.3.4",
4950
"@turf/bearing": "^7.3.4",
5051
"@turf/boolean-contains": "^7.3.4",
5152
"@turf/boolean-intersects": "^7.3.4",
@@ -55,6 +56,7 @@
5556
"@turf/helpers": "^7.3.4",
5657
"@turf/intersect": "^7.3.4",
5758
"@turf/length": "^7.3.4",
59+
"@turf/line-chunk": "^7.3.4",
5860
"@turf/line-slice-along": "^7.3.4",
5961
"@turf/line-split": "^7.3.4",
6062
"@turf/point-grid": "^7.3.4",
Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import type { VegetationVisual } from '../types';
22
import { vegetationMeshConfig } from '../../../config';
3-
import { EARTH_RADIUS } from '../../../gis/GeoCoordinates';
43
import type { IVegetationStrategy, TreePoint, BushPoint } from './types';
54
import { BROADLEAF_COLORS, NEEDLELEAF_COLORS } from './vegetationUtils';
6-
7-
const TO_RAD = Math.PI / 180;
5+
import lineChunk from '@turf/line-chunk';
6+
import { lineString } from '@turf/helpers';
87

98
export class TreeRowStrategy implements IVegetationStrategy {
109
collectPoints(
@@ -17,41 +16,27 @@ export class TreeRowStrategy implements IVegetationStrategy {
1716
if (coords.length < 2) return;
1817

1918
const config = vegetationMeshConfig.treeRow;
20-
const interval = config.intervalMeters;
2119
const isNeedle = veg.leafType === 'needleleaved';
2220
const colors = isNeedle ? NEEDLELEAF_COLORS : BROADLEAF_COLORS;
2321

24-
let accumulated = 0;
25-
26-
for (let i = 0; i < coords.length - 1; i++) {
27-
const [lng1, lat1] = coords[i]!;
28-
const [lng2, lat2] = coords[i + 1]!;
29-
30-
const midLat = (lat1 + lat2) / 2;
31-
const cosLat = Math.cos(midLat * TO_RAD);
32-
const dEast = (lng2 - lng1) * TO_RAD * EARTH_RADIUS * cosLat;
33-
const dNorth = (lat2 - lat1) * TO_RAD * EARTH_RADIUS;
34-
const segLen = Math.sqrt(dEast * dEast + dNorth * dNorth);
35-
if (segLen < 0.1) continue;
36-
37-
const dLng = lng2 - lng1;
38-
const dLat = lat2 - lat1;
39-
40-
while (accumulated < segLen) {
41-
const t = accumulated / segLen;
42-
trees.push({
43-
lng: lng1 + dLng * t,
44-
lat: lat1 + dLat * t,
45-
trunkHeightMin: config.trunkHeightMin,
46-
trunkHeightMax: config.trunkHeightMax,
47-
crownRadiusMin: config.crownRadiusMin,
48-
crownRadiusMax: config.crownRadiusMax,
49-
isNeedle,
50-
colors,
51-
});
52-
accumulated += interval;
53-
}
54-
accumulated -= segLen;
22+
const chunks = lineChunk(lineString(coords), config.intervalMeters, {
23+
units: 'meters',
24+
});
25+
for (const chunk of chunks.features) {
26+
const start = chunk.geometry.coordinates[0];
27+
if (!start || start[0] === undefined || start[1] === undefined) continue;
28+
const lng = start[0];
29+
const lat = start[1];
30+
trees.push({
31+
lng,
32+
lat,
33+
trunkHeightMin: config.trunkHeightMin,
34+
trunkHeightMax: config.trunkHeightMax,
35+
crownRadiusMin: config.crownRadiusMin,
36+
crownRadiusMax: config.crownRadiusMax,
37+
isNeedle,
38+
colors,
39+
});
5540
}
5641
}
5742
}

src/features/vegetation/meshStrategies/vegetationUtils.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Color,
99
type Object3D,
1010
} from 'three';
11+
import bbox from '@turf/bbox';
1112
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
1213
import { point, polygon as turfPolygon } from '@turf/helpers';
1314
import pointGrid from '@turf/point-grid';
@@ -34,8 +35,8 @@ export const NEEDLELEAF_COLORS = ['#1a5020', '#205828', '#256030', '#1a4a20'];
3435
export const SCRUB_COLORS = ['#4a7a38', '#5a8a40', '#4a8030'];
3536

3637
export function hash(x: number, y: number): number {
37-
const a = Math.floor(x * 1000) & 0xffff;
38-
const b = Math.floor(y * 1000) & 0xffff;
38+
const a = Math.floor(x * 1e6);
39+
const b = Math.floor(y * 1e6);
3940
return ((a * 73856093) ^ (b * 19349663)) & 0x7fffffff;
4041
}
4142

@@ -74,16 +75,7 @@ export function distributeGridInPolygon(
7475
const ring = polygon.coordinates[0];
7576
if (!ring || ring.length < 4) return [];
7677

77-
let minLng = Infinity,
78-
maxLng = -Infinity;
79-
let minLat = Infinity,
80-
maxLat = -Infinity;
81-
for (const [lng, lat] of ring as [number, number][]) {
82-
if (lng < minLng) minLng = lng;
83-
if (lng > maxLng) maxLng = lng;
84-
if (lat < minLat) minLat = lat;
85-
if (lat > maxLat) maxLat = lat;
86-
}
78+
const [minLng, minLat, maxLng, maxLat] = bbox(polygon);
8779

8880
const grid = pointGrid([minLng, minLat, maxLng, maxLat], spacingX, {
8981
units: 'meters',

0 commit comments

Comments
 (0)