-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
calc.ts
101 lines (88 loc) · 3.86 KB
/
calc.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import * as L from 'leaflet';
import * as GeoJSON from 'geojson';
import { calculateBounds, toCircle, contains, intersects } from '@terraformer/spatial';
function geoJSONGeometryToBounds(geometry: GeoJSON.GeometryObject) {
const bounds = calculateBounds(geometry);
const leafletBounds = L.latLngBounds([bounds[1], bounds[0]], [bounds[3], bounds[2]]);
return leafletBounds;
}
function getCircleMarkerRadius(circleMarker: L.CircleMarker, crs: L.CRS, zoom: number) {
const latLng = circleMarker.getLatLng();
const point = crs.latLngToPoint(latLng, zoom);
const delta = circleMarker.getRadius() / Math.SQRT2;
const topLeftPoint = L.point([point.x - delta, point.y - delta]);
const topLeftLatLng = crs.pointToLatLng(topLeftPoint, zoom);
const radius = crs.distance(latLng, topLeftLatLng);
return radius;
}
function circleToGeoJSONGeometry(latLng: L.LatLng, radius: number) {
return toCircle(L.GeoJSON.latLngToCoords(latLng), radius).geometry;
}
function layerToGeoJSONGeometry(layer: L.Layer, options: { zoom?: number, crs?: L.CRS } = {}): GeoJSON.Geometry | undefined {
if (layer instanceof L.Circle) {
const latLng = layer.getLatLng();
const radius = layer.getRadius();
return circleToGeoJSONGeometry(latLng, radius);
} else if (layer instanceof L.CircleMarker) {
if (options.zoom != undefined && options.crs != undefined) {
const latLng = layer.getLatLng();
const radius = getCircleMarkerRadius(layer, options.crs, options.zoom);
return circleToGeoJSONGeometry(latLng, radius);
} else {
console.warn("Zoom and CRS is required for calculating CircleMarker polygon, falling back to center point only");
return layer.toGeoJSON().geometry;
}
} else if (layer instanceof L.Marker || layer instanceof L.Polyline) {
return layer.toGeoJSON().geometry;
} else {
console.warn("Layer GeoJSON geometry is not available", layer);
return undefined;
}
}
function polygonContains(polygon: GeoJSON.Polygon, layerGeometry: GeoJSON.GeometryObject) {
return contains(polygon, layerGeometry);
}
function polygonIntersects(polygon: GeoJSON.Polygon, layerGeometry: GeoJSON.GeometryObject) {
return layerGeometry.type === "Point" ?
contains(polygon, layerGeometry) :
intersects(polygon, layerGeometry);
}
export function getLayersInPolygon(polygon: L.Polygon, layers: L.Layer[], options: { zoom?: number, crs?: L.CRS, intersect?: boolean } = {}) {
const polygonGeometry = polygon.toGeoJSON().geometry as GeoJSON.Polygon;
const polygonBounds = polygon.getBounds();
const selectedLayers = layers.filter(layer => {
// check bounds first (fast)
let layerGeometry;
let layerBounds;
if (layer instanceof L.Polyline) {
layerBounds = layer.getBounds();
} else {
layerGeometry = layerToGeoJSONGeometry(layer, options);
if (layerGeometry) {
layerBounds = geoJSONGeometryToBounds(layerGeometry);
}
}
// some bounds may be invalid, for example for empty polylines
if (!layerBounds || !layerBounds.isValid()) {
return false;
}
const boundsResult = options.intersect ?
polygonBounds.intersects(layerBounds) :
polygonBounds.contains(layerBounds);
if (!boundsResult) {
return false;
}
// check full geometry (slow)
if (!layerGeometry) {
layerGeometry = layerToGeoJSONGeometry(layer, options);
}
if (!layerGeometry) {
return false;
}
const geometryResult = options.intersect ?
polygonIntersects(polygonGeometry, layerGeometry) :
polygonContains(polygonGeometry, layerGeometry);
return geometryResult;
});
return selectedLayers;
}