@fieldia/map 0.0.0-stage → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +91 -2
- package/package.json +37 -3
- package/src/index.d.mts +6 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/index.mjs +6 -0
- package/src/index.mjs.map +1 -0
- package/src/lib/area.d.mts +3 -0
- package/src/lib/area.d.ts +3 -0
- package/src/lib/area.js +591 -0
- package/src/lib/area.js.map +1 -0
- package/src/lib/area.mjs +588 -0
- package/src/lib/area.mjs.map +1 -0
- package/src/lib/geo.d.mts +72 -0
- package/src/lib/geo.d.ts +72 -0
- package/src/lib/geo.js +149 -0
- package/src/lib/geo.js.map +1 -0
- package/src/lib/geo.mjs +134 -0
- package/src/lib/geo.mjs.map +1 -0
- package/src/lib/kinds.d.mts +35 -0
- package/src/lib/kinds.d.ts +35 -0
- package/src/lib/kinds.js +127 -0
- package/src/lib/kinds.js.map +1 -0
- package/src/lib/kinds.mjs +124 -0
- package/src/lib/kinds.mjs.map +1 -0
- package/src/lib/labels.d.mts +58 -0
- package/src/lib/labels.d.ts +58 -0
- package/src/lib/labels.js +214 -0
- package/src/lib/labels.js.map +1 -0
- package/src/lib/labels.mjs +209 -0
- package/src/lib/labels.mjs.map +1 -0
- package/src/lib/leaflet-css.d.mts +2 -0
- package/src/lib/leaflet-css.d.ts +2 -0
- package/src/lib/leaflet-css.js +34 -0
- package/src/lib/leaflet-css.js.map +1 -0
- package/src/lib/leaflet-css.mjs +31 -0
- package/src/lib/leaflet-css.mjs.map +1 -0
- package/src/lib/leaflet.d.mts +15 -0
- package/src/lib/leaflet.d.ts +15 -0
- package/src/lib/leaflet.js +76 -0
- package/src/lib/leaflet.js.map +1 -0
- package/src/lib/leaflet.mjs +72 -0
- package/src/lib/leaflet.mjs.map +1 -0
- package/src/lib/map-core.d.mts +47 -0
- package/src/lib/map-core.d.ts +47 -0
- package/src/lib/map-core.js +300 -0
- package/src/lib/map-core.js.map +1 -0
- package/src/lib/map-core.mjs +297 -0
- package/src/lib/map-core.mjs.map +1 -0
- package/src/lib/place.d.mts +3 -0
- package/src/lib/place.d.ts +3 -0
- package/src/lib/place.js +323 -0
- package/src/lib/place.js.map +1 -0
- package/src/lib/place.mjs +320 -0
- package/src/lib/place.mjs.map +1 -0
- package/src/lib/providers.d.mts +131 -0
- package/src/lib/providers.d.ts +131 -0
- package/src/lib/providers.js +224 -0
- package/src/lib/providers.js.map +1 -0
- package/src/lib/providers.mjs +217 -0
- package/src/lib/providers.mjs.map +1 -0
- package/src/lib/search-box.d.mts +28 -0
- package/src/lib/search-box.d.ts +28 -0
- package/src/lib/search-box.js +141 -0
- package/src/lib/search-box.js.map +1 -0
- package/src/lib/search-box.mjs +138 -0
- package/src/lib/search-box.mjs.map +1 -0
- package/src/lib/settings.d.mts +19 -0
- package/src/lib/settings.d.ts +19 -0
- package/src/lib/settings.js +3 -0
- package/src/lib/settings.js.map +1 -0
- package/src/lib/settings.mjs +2 -0
- package/src/lib/settings.mjs.map +1 -0
- package/src/lib/styles.d.mts +11 -0
- package/src/lib/styles.d.ts +11 -0
- package/src/lib/styles.js +127 -0
- package/src/lib/styles.js.map +1 -0
- package/src/lib/styles.mjs +122 -0
- package/src/lib/styles.mjs.map +1 -0
- package/src/lib/widgets.d.mts +14 -0
- package/src/lib/widgets.d.ts +14 -0
- package/src/lib/widgets.js +21 -0
- package/src/lib/widgets.js.map +1 -0
- package/src/lib/widgets.mjs +18 -0
- package/src/lib/widgets.mjs.map +1 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { JsonValue } from '@fieldia/core';
|
|
2
|
+
/**
|
|
3
|
+
* Places and areas as a form keeps them: GeoJSON (RFC 7946), longitude
|
|
4
|
+
* first, so any backend or map library reads them as they are. A place is a
|
|
5
|
+
* Point. An area is a Feature whose `properties.shape` says how it was drawn,
|
|
6
|
+
* so it comes back as the same shape with the same grips: a polygon, a box
|
|
7
|
+
* (a polygon of four corners), or a circle (its centre as a Point and its
|
|
8
|
+
* `radius` in metres, since GeoJSON has no circle).
|
|
9
|
+
*/
|
|
10
|
+
export interface LatLng {
|
|
11
|
+
lat: number;
|
|
12
|
+
lng: number;
|
|
13
|
+
}
|
|
14
|
+
export type Shape = 'polygon' | 'box' | 'circle';
|
|
15
|
+
export type Area = {
|
|
16
|
+
shape: 'polygon' | 'box';
|
|
17
|
+
points: LatLng[];
|
|
18
|
+
} | {
|
|
19
|
+
shape: 'circle';
|
|
20
|
+
center: LatLng;
|
|
21
|
+
radius: number;
|
|
22
|
+
};
|
|
23
|
+
export type PointsArea = Extract<Area, {
|
|
24
|
+
points: LatLng[];
|
|
25
|
+
}>;
|
|
26
|
+
export type CircleArea = Extract<Area, {
|
|
27
|
+
shape: 'circle';
|
|
28
|
+
}>;
|
|
29
|
+
export interface Bounds {
|
|
30
|
+
south: number;
|
|
31
|
+
west: number;
|
|
32
|
+
north: number;
|
|
33
|
+
east: number;
|
|
34
|
+
}
|
|
35
|
+
export interface PointGeometry {
|
|
36
|
+
type: 'Point';
|
|
37
|
+
coordinates: [number, number];
|
|
38
|
+
}
|
|
39
|
+
export interface AreaFeature {
|
|
40
|
+
type: 'Feature';
|
|
41
|
+
properties: {
|
|
42
|
+
shape: Shape;
|
|
43
|
+
radius?: number;
|
|
44
|
+
};
|
|
45
|
+
geometry: PointGeometry | {
|
|
46
|
+
type: 'Polygon';
|
|
47
|
+
coordinates: [number, number][][];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare const isLatLng: (lat: unknown, lng: unknown) => boolean;
|
|
51
|
+
/** A place's value: a GeoJSON Point. */
|
|
52
|
+
export declare function writePoint(point: LatLng): PointGeometry;
|
|
53
|
+
/** The place a value holds: a Point, or a Feature holding one; anything else is no place. */
|
|
54
|
+
export declare function readPoint(value: unknown): LatLng | null;
|
|
55
|
+
/** An area's value: a GeoJSON Feature, its ring closed and counter-clockwise as RFC 7946 asks. */
|
|
56
|
+
export declare function writeArea(area: Area): AreaFeature;
|
|
57
|
+
/** The area a value holds, as it was drawn; a plain Polygon from elsewhere is read as a polygon. */
|
|
58
|
+
export declare function readArea(value: unknown): Area | null;
|
|
59
|
+
/** A coordinate as a person types it: a point or a comma for the decimals, Western or Arabic digits. */
|
|
60
|
+
export declare function parseCoordinate(text: string): number | null;
|
|
61
|
+
/** Metres between two places on the ground (the haversine formula). */
|
|
62
|
+
export declare function metresBetween(a: LatLng, b: LatLng): number;
|
|
63
|
+
/** The place `metres` due east: where a circle's edge grip sits. */
|
|
64
|
+
export declare function offsetEast(from: LatLng, metres: number): LatLng;
|
|
65
|
+
/** A box's four corners from two dragged to, counter-clockwise from the south-west. */
|
|
66
|
+
export declare function boxFrom(a: LatLng, b: LatLng): LatLng[];
|
|
67
|
+
/** The same area, carried by so many degrees. */
|
|
68
|
+
export declare function moveArea(area: Area, dLat: number, dLng: number): Area;
|
|
69
|
+
/** The smallest box an area fits in. */
|
|
70
|
+
export declare function areaBounds(area: Area): Bounds;
|
|
71
|
+
/** A value fit for a json field. */
|
|
72
|
+
export declare const asJson: (value: AreaFeature | PointGeometry) => JsonValue;
|
package/src/lib/geo.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { JsonValue } from '@fieldia/core';
|
|
2
|
+
/**
|
|
3
|
+
* Places and areas as a form keeps them: GeoJSON (RFC 7946), longitude
|
|
4
|
+
* first, so any backend or map library reads them as they are. A place is a
|
|
5
|
+
* Point. An area is a Feature whose `properties.shape` says how it was drawn,
|
|
6
|
+
* so it comes back as the same shape with the same grips: a polygon, a box
|
|
7
|
+
* (a polygon of four corners), or a circle (its centre as a Point and its
|
|
8
|
+
* `radius` in metres, since GeoJSON has no circle).
|
|
9
|
+
*/
|
|
10
|
+
export interface LatLng {
|
|
11
|
+
lat: number;
|
|
12
|
+
lng: number;
|
|
13
|
+
}
|
|
14
|
+
export type Shape = 'polygon' | 'box' | 'circle';
|
|
15
|
+
export type Area = {
|
|
16
|
+
shape: 'polygon' | 'box';
|
|
17
|
+
points: LatLng[];
|
|
18
|
+
} | {
|
|
19
|
+
shape: 'circle';
|
|
20
|
+
center: LatLng;
|
|
21
|
+
radius: number;
|
|
22
|
+
};
|
|
23
|
+
export type PointsArea = Extract<Area, {
|
|
24
|
+
points: LatLng[];
|
|
25
|
+
}>;
|
|
26
|
+
export type CircleArea = Extract<Area, {
|
|
27
|
+
shape: 'circle';
|
|
28
|
+
}>;
|
|
29
|
+
export interface Bounds {
|
|
30
|
+
south: number;
|
|
31
|
+
west: number;
|
|
32
|
+
north: number;
|
|
33
|
+
east: number;
|
|
34
|
+
}
|
|
35
|
+
export interface PointGeometry {
|
|
36
|
+
type: 'Point';
|
|
37
|
+
coordinates: [number, number];
|
|
38
|
+
}
|
|
39
|
+
export interface AreaFeature {
|
|
40
|
+
type: 'Feature';
|
|
41
|
+
properties: {
|
|
42
|
+
shape: Shape;
|
|
43
|
+
radius?: number;
|
|
44
|
+
};
|
|
45
|
+
geometry: PointGeometry | {
|
|
46
|
+
type: 'Polygon';
|
|
47
|
+
coordinates: [number, number][][];
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare const isLatLng: (lat: unknown, lng: unknown) => boolean;
|
|
51
|
+
/** A place's value: a GeoJSON Point. */
|
|
52
|
+
export declare function writePoint(point: LatLng): PointGeometry;
|
|
53
|
+
/** The place a value holds: a Point, or a Feature holding one; anything else is no place. */
|
|
54
|
+
export declare function readPoint(value: unknown): LatLng | null;
|
|
55
|
+
/** An area's value: a GeoJSON Feature, its ring closed and counter-clockwise as RFC 7946 asks. */
|
|
56
|
+
export declare function writeArea(area: Area): AreaFeature;
|
|
57
|
+
/** The area a value holds, as it was drawn; a plain Polygon from elsewhere is read as a polygon. */
|
|
58
|
+
export declare function readArea(value: unknown): Area | null;
|
|
59
|
+
/** A coordinate as a person types it: a point or a comma for the decimals, Western or Arabic digits. */
|
|
60
|
+
export declare function parseCoordinate(text: string): number | null;
|
|
61
|
+
/** Metres between two places on the ground (the haversine formula). */
|
|
62
|
+
export declare function metresBetween(a: LatLng, b: LatLng): number;
|
|
63
|
+
/** The place `metres` due east: where a circle's edge grip sits. */
|
|
64
|
+
export declare function offsetEast(from: LatLng, metres: number): LatLng;
|
|
65
|
+
/** A box's four corners from two dragged to, counter-clockwise from the south-west. */
|
|
66
|
+
export declare function boxFrom(a: LatLng, b: LatLng): LatLng[];
|
|
67
|
+
/** The same area, carried by so many degrees. */
|
|
68
|
+
export declare function moveArea(area: Area, dLat: number, dLng: number): Area;
|
|
69
|
+
/** The smallest box an area fits in. */
|
|
70
|
+
export declare function areaBounds(area: Area): Bounds;
|
|
71
|
+
/** A value fit for a json field. */
|
|
72
|
+
export declare const asJson: (value: AreaFeature | PointGeometry) => JsonValue;
|
package/src/lib/geo.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.asJson = exports.isLatLng = void 0;
|
|
4
|
+
exports.writePoint = writePoint;
|
|
5
|
+
exports.readPoint = readPoint;
|
|
6
|
+
exports.writeArea = writeArea;
|
|
7
|
+
exports.readArea = readArea;
|
|
8
|
+
exports.parseCoordinate = parseCoordinate;
|
|
9
|
+
exports.metresBetween = metresBetween;
|
|
10
|
+
exports.offsetEast = offsetEast;
|
|
11
|
+
exports.boxFrom = boxFrom;
|
|
12
|
+
exports.moveArea = moveArea;
|
|
13
|
+
exports.areaBounds = areaBounds;
|
|
14
|
+
/** The earth's mean radius, in metres: the one GeoJSON's distances are usually worked out with. */
|
|
15
|
+
const EARTH = 6371008.8;
|
|
16
|
+
const rad = (degrees) => (degrees * Math.PI) / 180;
|
|
17
|
+
const deg = (radians) => (radians * 180) / Math.PI;
|
|
18
|
+
/** Six decimals: about 11 cm on the ground, finer than any finger. */
|
|
19
|
+
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
20
|
+
const isLatLng = (lat, lng) => typeof lat === 'number' && typeof lng === 'number' && Number.isFinite(lat) && Number.isFinite(lng) && Math.abs(lat) <= 90 && Math.abs(lng) <= 180;
|
|
21
|
+
exports.isLatLng = isLatLng;
|
|
22
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
+
/** A place's value: a GeoJSON Point. */
|
|
24
|
+
function writePoint(point) {
|
|
25
|
+
return { type: 'Point', coordinates: [round6(point.lng), round6(point.lat)] };
|
|
26
|
+
}
|
|
27
|
+
function geometryOf(value) {
|
|
28
|
+
if (!isObject(value))
|
|
29
|
+
return null;
|
|
30
|
+
if (value['type'] === 'Feature')
|
|
31
|
+
return isObject(value['geometry']) ? value['geometry'] : null;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function latLngOf(position) {
|
|
35
|
+
if (!Array.isArray(position))
|
|
36
|
+
return null;
|
|
37
|
+
const [lng, lat] = position;
|
|
38
|
+
return (0, exports.isLatLng)(lat, lng) ? { lat: lat, lng: lng } : null;
|
|
39
|
+
}
|
|
40
|
+
/** The place a value holds: a Point, or a Feature holding one; anything else is no place. */
|
|
41
|
+
function readPoint(value) {
|
|
42
|
+
const geometry = geometryOf(value);
|
|
43
|
+
return geometry?.['type'] === 'Point' ? latLngOf(geometry['coordinates']) : null;
|
|
44
|
+
}
|
|
45
|
+
/** The signed area of a ring in degrees: above zero when it runs counter-clockwise. */
|
|
46
|
+
function signedArea(points) {
|
|
47
|
+
let sum = 0;
|
|
48
|
+
points.forEach((p, i) => {
|
|
49
|
+
const q = points[(i + 1) % points.length];
|
|
50
|
+
sum += p.lng * q.lat - q.lng * p.lat;
|
|
51
|
+
});
|
|
52
|
+
return sum / 2;
|
|
53
|
+
}
|
|
54
|
+
/** An area's value: a GeoJSON Feature, its ring closed and counter-clockwise as RFC 7946 asks. */
|
|
55
|
+
function writeArea(area) {
|
|
56
|
+
if (area.shape === 'circle') {
|
|
57
|
+
return { type: 'Feature', properties: { shape: 'circle', radius: Math.round(area.radius) }, geometry: writePoint(area.center) };
|
|
58
|
+
}
|
|
59
|
+
const [first, ...rest] = area.points;
|
|
60
|
+
const ring = signedArea(area.points) < 0 ? [first, ...rest.reverse()] : area.points;
|
|
61
|
+
const coordinates = [...ring, ring[0]].map((p) => [round6(p.lng), round6(p.lat)]);
|
|
62
|
+
return { type: 'Feature', properties: { shape: area.shape }, geometry: { type: 'Polygon', coordinates: [coordinates] } };
|
|
63
|
+
}
|
|
64
|
+
/** The area a value holds, as it was drawn; a plain Polygon from elsewhere is read as a polygon. */
|
|
65
|
+
function readArea(value) {
|
|
66
|
+
const geometry = geometryOf(value);
|
|
67
|
+
if (!geometry)
|
|
68
|
+
return null;
|
|
69
|
+
const properties = isObject(value) && value['type'] === 'Feature' && isObject(value['properties']) ? value['properties'] : {};
|
|
70
|
+
if (geometry['type'] === 'Point') {
|
|
71
|
+
const center = latLngOf(geometry['coordinates']);
|
|
72
|
+
const radius = properties['radius'];
|
|
73
|
+
return center && properties['shape'] === 'circle' && typeof radius === 'number' && radius > 0 ? { shape: 'circle', center, radius } : null;
|
|
74
|
+
}
|
|
75
|
+
if (geometry['type'] !== 'Polygon' || !Array.isArray(geometry['coordinates']))
|
|
76
|
+
return null;
|
|
77
|
+
const ring = (geometry['coordinates'][0] ?? []);
|
|
78
|
+
const points = ring.map(latLngOf);
|
|
79
|
+
if (points.some((p) => p === null))
|
|
80
|
+
return null;
|
|
81
|
+
const open = points;
|
|
82
|
+
const last = open[open.length - 1];
|
|
83
|
+
// The ring's closing point repeats its first: drawn once.
|
|
84
|
+
if (open.length > 1 && last.lat === open[0].lat && last.lng === open[0].lng)
|
|
85
|
+
open.pop();
|
|
86
|
+
if (open.length < 3)
|
|
87
|
+
return null;
|
|
88
|
+
return { shape: properties['shape'] === 'box' && open.length === 4 ? 'box' : 'polygon', points: open };
|
|
89
|
+
}
|
|
90
|
+
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩';
|
|
91
|
+
/** A coordinate as a person types it: a point or a comma for the decimals, Western or Arabic digits. */
|
|
92
|
+
function parseCoordinate(text) {
|
|
93
|
+
const plain = text
|
|
94
|
+
.trim()
|
|
95
|
+
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
|
96
|
+
.replace(/[٫,]/g, '.')
|
|
97
|
+
.replace(/^[−–]/, '-');
|
|
98
|
+
if (!/^-?\d+(\.\d+)?$/.test(plain))
|
|
99
|
+
return null;
|
|
100
|
+
return Number(plain);
|
|
101
|
+
}
|
|
102
|
+
/** Metres between two places on the ground (the haversine formula). */
|
|
103
|
+
function metresBetween(a, b) {
|
|
104
|
+
const dLat = rad(b.lat - a.lat);
|
|
105
|
+
const dLng = rad(b.lng - a.lng);
|
|
106
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(dLng / 2) ** 2;
|
|
107
|
+
return 2 * EARTH * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
108
|
+
}
|
|
109
|
+
/** The place `metres` due east: where a circle's edge grip sits. */
|
|
110
|
+
function offsetEast(from, metres) {
|
|
111
|
+
return { lat: from.lat, lng: from.lng + deg(metres / (EARTH * Math.cos(rad(from.lat)))) };
|
|
112
|
+
}
|
|
113
|
+
/** A box's four corners from two dragged to, counter-clockwise from the south-west. */
|
|
114
|
+
function boxFrom(a, b) {
|
|
115
|
+
const south = Math.min(a.lat, b.lat);
|
|
116
|
+
const north = Math.max(a.lat, b.lat);
|
|
117
|
+
const west = Math.min(a.lng, b.lng);
|
|
118
|
+
const east = Math.max(a.lng, b.lng);
|
|
119
|
+
return [
|
|
120
|
+
{ lat: south, lng: west },
|
|
121
|
+
{ lat: south, lng: east },
|
|
122
|
+
{ lat: north, lng: east },
|
|
123
|
+
{ lat: north, lng: west },
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
/** The same area, carried by so many degrees. */
|
|
127
|
+
function moveArea(area, dLat, dLng) {
|
|
128
|
+
const move = (p) => ({ lat: p.lat + dLat, lng: p.lng + dLng });
|
|
129
|
+
return area.shape === 'circle' ? { ...area, center: move(area.center) } : { ...area, points: area.points.map(move) };
|
|
130
|
+
}
|
|
131
|
+
/** The smallest box an area fits in. */
|
|
132
|
+
function areaBounds(area) {
|
|
133
|
+
const points = area.shape === 'circle'
|
|
134
|
+
? [
|
|
135
|
+
{ lat: area.center.lat + deg(area.radius / EARTH), lng: offsetEast(area.center, area.radius).lng },
|
|
136
|
+
{ lat: area.center.lat - deg(area.radius / EARTH), lng: 2 * area.center.lng - offsetEast(area.center, area.radius).lng },
|
|
137
|
+
]
|
|
138
|
+
: area.points;
|
|
139
|
+
return {
|
|
140
|
+
south: Math.min(...points.map((p) => p.lat)),
|
|
141
|
+
west: Math.min(...points.map((p) => p.lng)),
|
|
142
|
+
north: Math.max(...points.map((p) => p.lat)),
|
|
143
|
+
east: Math.max(...points.map((p) => p.lng)),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** A value fit for a json field. */
|
|
147
|
+
const asJson = (value) => value;
|
|
148
|
+
exports.asJson = asJson;
|
|
149
|
+
//# sourceMappingURL=geo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geo.js","sourceRoot":"","sources":["../../../../../libs/map/src/lib/geo.ts"],"names":[],"mappings":"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"}
|
package/src/lib/geo.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/** The earth's mean radius, in metres: the one GeoJSON's distances are usually worked out with. */
|
|
2
|
+
const EARTH = 6371008.8;
|
|
3
|
+
const rad = (degrees) => (degrees * Math.PI) / 180;
|
|
4
|
+
const deg = (radians) => (radians * 180) / Math.PI;
|
|
5
|
+
/** Six decimals: about 11 cm on the ground, finer than any finger. */
|
|
6
|
+
const round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
7
|
+
export const isLatLng = (lat, lng) => typeof lat === 'number' && typeof lng === 'number' && Number.isFinite(lat) && Number.isFinite(lng) && Math.abs(lat) <= 90 && Math.abs(lng) <= 180;
|
|
8
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
+
/** A place's value: a GeoJSON Point. */
|
|
10
|
+
export function writePoint(point) {
|
|
11
|
+
return { type: 'Point', coordinates: [round6(point.lng), round6(point.lat)] };
|
|
12
|
+
}
|
|
13
|
+
function geometryOf(value) {
|
|
14
|
+
if (!isObject(value))
|
|
15
|
+
return null;
|
|
16
|
+
if (value['type'] === 'Feature')
|
|
17
|
+
return isObject(value['geometry']) ? value['geometry'] : null;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function latLngOf(position) {
|
|
21
|
+
if (!Array.isArray(position))
|
|
22
|
+
return null;
|
|
23
|
+
const [lng, lat] = position;
|
|
24
|
+
return isLatLng(lat, lng) ? { lat: lat, lng: lng } : null;
|
|
25
|
+
}
|
|
26
|
+
/** The place a value holds: a Point, or a Feature holding one; anything else is no place. */
|
|
27
|
+
export function readPoint(value) {
|
|
28
|
+
const geometry = geometryOf(value);
|
|
29
|
+
return geometry?.['type'] === 'Point' ? latLngOf(geometry['coordinates']) : null;
|
|
30
|
+
}
|
|
31
|
+
/** The signed area of a ring in degrees: above zero when it runs counter-clockwise. */
|
|
32
|
+
function signedArea(points) {
|
|
33
|
+
let sum = 0;
|
|
34
|
+
points.forEach((p, i) => {
|
|
35
|
+
const q = points[(i + 1) % points.length];
|
|
36
|
+
sum += p.lng * q.lat - q.lng * p.lat;
|
|
37
|
+
});
|
|
38
|
+
return sum / 2;
|
|
39
|
+
}
|
|
40
|
+
/** An area's value: a GeoJSON Feature, its ring closed and counter-clockwise as RFC 7946 asks. */
|
|
41
|
+
export function writeArea(area) {
|
|
42
|
+
if (area.shape === 'circle') {
|
|
43
|
+
return { type: 'Feature', properties: { shape: 'circle', radius: Math.round(area.radius) }, geometry: writePoint(area.center) };
|
|
44
|
+
}
|
|
45
|
+
const [first, ...rest] = area.points;
|
|
46
|
+
const ring = signedArea(area.points) < 0 ? [first, ...rest.reverse()] : area.points;
|
|
47
|
+
const coordinates = [...ring, ring[0]].map((p) => [round6(p.lng), round6(p.lat)]);
|
|
48
|
+
return { type: 'Feature', properties: { shape: area.shape }, geometry: { type: 'Polygon', coordinates: [coordinates] } };
|
|
49
|
+
}
|
|
50
|
+
/** The area a value holds, as it was drawn; a plain Polygon from elsewhere is read as a polygon. */
|
|
51
|
+
export function readArea(value) {
|
|
52
|
+
const geometry = geometryOf(value);
|
|
53
|
+
if (!geometry)
|
|
54
|
+
return null;
|
|
55
|
+
const properties = isObject(value) && value['type'] === 'Feature' && isObject(value['properties']) ? value['properties'] : {};
|
|
56
|
+
if (geometry['type'] === 'Point') {
|
|
57
|
+
const center = latLngOf(geometry['coordinates']);
|
|
58
|
+
const radius = properties['radius'];
|
|
59
|
+
return center && properties['shape'] === 'circle' && typeof radius === 'number' && radius > 0 ? { shape: 'circle', center, radius } : null;
|
|
60
|
+
}
|
|
61
|
+
if (geometry['type'] !== 'Polygon' || !Array.isArray(geometry['coordinates']))
|
|
62
|
+
return null;
|
|
63
|
+
const ring = (geometry['coordinates'][0] ?? []);
|
|
64
|
+
const points = ring.map(latLngOf);
|
|
65
|
+
if (points.some((p) => p === null))
|
|
66
|
+
return null;
|
|
67
|
+
const open = points;
|
|
68
|
+
const last = open[open.length - 1];
|
|
69
|
+
// The ring's closing point repeats its first: drawn once.
|
|
70
|
+
if (open.length > 1 && last.lat === open[0].lat && last.lng === open[0].lng)
|
|
71
|
+
open.pop();
|
|
72
|
+
if (open.length < 3)
|
|
73
|
+
return null;
|
|
74
|
+
return { shape: properties['shape'] === 'box' && open.length === 4 ? 'box' : 'polygon', points: open };
|
|
75
|
+
}
|
|
76
|
+
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩';
|
|
77
|
+
/** A coordinate as a person types it: a point or a comma for the decimals, Western or Arabic digits. */
|
|
78
|
+
export function parseCoordinate(text) {
|
|
79
|
+
const plain = text
|
|
80
|
+
.trim()
|
|
81
|
+
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
|
82
|
+
.replace(/[٫,]/g, '.')
|
|
83
|
+
.replace(/^[−–]/, '-');
|
|
84
|
+
if (!/^-?\d+(\.\d+)?$/.test(plain))
|
|
85
|
+
return null;
|
|
86
|
+
return Number(plain);
|
|
87
|
+
}
|
|
88
|
+
/** Metres between two places on the ground (the haversine formula). */
|
|
89
|
+
export function metresBetween(a, b) {
|
|
90
|
+
const dLat = rad(b.lat - a.lat);
|
|
91
|
+
const dLng = rad(b.lng - a.lng);
|
|
92
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(rad(a.lat)) * Math.cos(rad(b.lat)) * Math.sin(dLng / 2) ** 2;
|
|
93
|
+
return 2 * EARTH * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
94
|
+
}
|
|
95
|
+
/** The place `metres` due east: where a circle's edge grip sits. */
|
|
96
|
+
export function offsetEast(from, metres) {
|
|
97
|
+
return { lat: from.lat, lng: from.lng + deg(metres / (EARTH * Math.cos(rad(from.lat)))) };
|
|
98
|
+
}
|
|
99
|
+
/** A box's four corners from two dragged to, counter-clockwise from the south-west. */
|
|
100
|
+
export function boxFrom(a, b) {
|
|
101
|
+
const south = Math.min(a.lat, b.lat);
|
|
102
|
+
const north = Math.max(a.lat, b.lat);
|
|
103
|
+
const west = Math.min(a.lng, b.lng);
|
|
104
|
+
const east = Math.max(a.lng, b.lng);
|
|
105
|
+
return [
|
|
106
|
+
{ lat: south, lng: west },
|
|
107
|
+
{ lat: south, lng: east },
|
|
108
|
+
{ lat: north, lng: east },
|
|
109
|
+
{ lat: north, lng: west },
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
/** The same area, carried by so many degrees. */
|
|
113
|
+
export function moveArea(area, dLat, dLng) {
|
|
114
|
+
const move = (p) => ({ lat: p.lat + dLat, lng: p.lng + dLng });
|
|
115
|
+
return area.shape === 'circle' ? { ...area, center: move(area.center) } : { ...area, points: area.points.map(move) };
|
|
116
|
+
}
|
|
117
|
+
/** The smallest box an area fits in. */
|
|
118
|
+
export function areaBounds(area) {
|
|
119
|
+
const points = area.shape === 'circle'
|
|
120
|
+
? [
|
|
121
|
+
{ lat: area.center.lat + deg(area.radius / EARTH), lng: offsetEast(area.center, area.radius).lng },
|
|
122
|
+
{ lat: area.center.lat - deg(area.radius / EARTH), lng: 2 * area.center.lng - offsetEast(area.center, area.radius).lng },
|
|
123
|
+
]
|
|
124
|
+
: area.points;
|
|
125
|
+
return {
|
|
126
|
+
south: Math.min(...points.map((p) => p.lat)),
|
|
127
|
+
west: Math.min(...points.map((p) => p.lng)),
|
|
128
|
+
north: Math.max(...points.map((p) => p.lat)),
|
|
129
|
+
east: Math.max(...points.map((p) => p.lng)),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** A value fit for a json field. */
|
|
133
|
+
export const asJson = (value) => value;
|
|
134
|
+
//# sourceMappingURL=geo.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geo.mjs","sourceRoot":"","sources":["../../../../../libs/map/src/lib/geo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Field, FieldNode, Locale, Page } from '@fieldia/core';
|
|
2
|
+
import { type MapLabels } from './labels.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* The maps as kinds of field in Fieldia's designer: Place and Area in its
|
|
5
|
+
* toolbox, each with its settings on the picked field. Pass them to the
|
|
6
|
+
* designer as `kinds`, and the widgets as `widgets`, as an app does its own
|
|
7
|
+
* kinds. Shaped as the designer's AppKind, without this package needing the
|
|
8
|
+
* designer.
|
|
9
|
+
*/
|
|
10
|
+
export interface MapKindContext {
|
|
11
|
+
document: Document;
|
|
12
|
+
/** Keeps settings on the field's place on the page (`node.options`); null takes one away. */
|
|
13
|
+
set(patch: Record<string, string | number | boolean | null>): boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface MapKind {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
group: string;
|
|
19
|
+
icon: string;
|
|
20
|
+
widget: string;
|
|
21
|
+
field(label: string): Field;
|
|
22
|
+
fits(field: Field): boolean;
|
|
23
|
+
settings(context: MapKindContext): {
|
|
24
|
+
element: HTMLElement;
|
|
25
|
+
refresh(page: Page, node: FieldNode): void;
|
|
26
|
+
};
|
|
27
|
+
preview(context: {
|
|
28
|
+
document: Document;
|
|
29
|
+
}): HTMLElement;
|
|
30
|
+
}
|
|
31
|
+
/** Place and Area for the designer, in its language. */
|
|
32
|
+
export declare function mapKinds(options?: {
|
|
33
|
+
locale?: Locale;
|
|
34
|
+
labels?: Partial<MapLabels>;
|
|
35
|
+
}): MapKind[];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Field, FieldNode, Locale, Page } from '@fieldia/core';
|
|
2
|
+
import { type MapLabels } from './labels';
|
|
3
|
+
/**
|
|
4
|
+
* The maps as kinds of field in Fieldia's designer: Place and Area in its
|
|
5
|
+
* toolbox, each with its settings on the picked field. Pass them to the
|
|
6
|
+
* designer as `kinds`, and the widgets as `widgets`, as an app does its own
|
|
7
|
+
* kinds. Shaped as the designer's AppKind, without this package needing the
|
|
8
|
+
* designer.
|
|
9
|
+
*/
|
|
10
|
+
export interface MapKindContext {
|
|
11
|
+
document: Document;
|
|
12
|
+
/** Keeps settings on the field's place on the page (`node.options`); null takes one away. */
|
|
13
|
+
set(patch: Record<string, string | number | boolean | null>): boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface MapKind {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
group: string;
|
|
19
|
+
icon: string;
|
|
20
|
+
widget: string;
|
|
21
|
+
field(label: string): Field;
|
|
22
|
+
fits(field: Field): boolean;
|
|
23
|
+
settings(context: MapKindContext): {
|
|
24
|
+
element: HTMLElement;
|
|
25
|
+
refresh(page: Page, node: FieldNode): void;
|
|
26
|
+
};
|
|
27
|
+
preview(context: {
|
|
28
|
+
document: Document;
|
|
29
|
+
}): HTMLElement;
|
|
30
|
+
}
|
|
31
|
+
/** Place and Area for the designer, in its language. */
|
|
32
|
+
export declare function mapKinds(options?: {
|
|
33
|
+
locale?: Locale;
|
|
34
|
+
labels?: Partial<MapLabels>;
|
|
35
|
+
}): MapKind[];
|
package/src/lib/kinds.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapKinds = mapKinds;
|
|
4
|
+
const labels_1 = require("./labels");
|
|
5
|
+
const PIN_ICON = '<path d="M12 21s-6.5-6.2-6.5-11.2a6.5 6.5 0 0 1 13 0C18.5 14.8 12 21 12 21z" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="12" cy="9.8" r="2.4" fill="none" stroke="currentColor" stroke-width="1.8"/>';
|
|
6
|
+
const AREA_ICON = '<path d="M4.5 8.5 11 4l8.5 5-2.5 10.5H7.5z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>';
|
|
7
|
+
const HEIGHTS = [
|
|
8
|
+
['heightSmall', 200],
|
|
9
|
+
['heightMedium', 280],
|
|
10
|
+
['heightLarge', 400],
|
|
11
|
+
];
|
|
12
|
+
/** A setting: words then a choice, or a tick then its words, as the designer's own ticks read. */
|
|
13
|
+
function row(doc, words, control) {
|
|
14
|
+
const label = doc.createElement('label');
|
|
15
|
+
label.className = 'fd-inline-setting';
|
|
16
|
+
const span = doc.createElement('span');
|
|
17
|
+
span.textContent = words;
|
|
18
|
+
if (control instanceof HTMLInputElement && control.type === 'checkbox')
|
|
19
|
+
label.append(control, span);
|
|
20
|
+
else
|
|
21
|
+
label.append(span, control);
|
|
22
|
+
return label;
|
|
23
|
+
}
|
|
24
|
+
/** The settings side by side, spaced so each tick plainly goes with its own words. */
|
|
25
|
+
function settingsRow(doc) {
|
|
26
|
+
const element = doc.createElement('div');
|
|
27
|
+
element.className = 'fd-map-settings';
|
|
28
|
+
element.style.cssText = 'display:flex;flex-wrap:wrap;align-items:center;gap:8px 20px;';
|
|
29
|
+
return element;
|
|
30
|
+
}
|
|
31
|
+
function checkbox(doc, onChange) {
|
|
32
|
+
const box = doc.createElement('input');
|
|
33
|
+
box.type = 'checkbox';
|
|
34
|
+
box.addEventListener('change', () => onChange(box.checked));
|
|
35
|
+
return box;
|
|
36
|
+
}
|
|
37
|
+
function heightSetting(doc, words, set) {
|
|
38
|
+
const select = doc.createElement('select');
|
|
39
|
+
select.className = 'fd-inline-select';
|
|
40
|
+
for (const [key, px] of HEIGHTS)
|
|
41
|
+
select.append(new Option(words[key], String(px)));
|
|
42
|
+
select.addEventListener('change', () => set({ height: select.value === '280' ? null : Number(select.value) }));
|
|
43
|
+
return { element: row(doc, words.settingHeight, select), read: (node) => (select.value = String(node.options?.['height'] ?? 280)) };
|
|
44
|
+
}
|
|
45
|
+
/** The demo's drawing on a closed card: a pin or an outline on a faint grid, never a live map. */
|
|
46
|
+
function previewOf(doc, icon, words) {
|
|
47
|
+
const element = doc.createElement('div');
|
|
48
|
+
element.className = 'fd-map-preview';
|
|
49
|
+
element.style.cssText = 'display:flex;align-items:center;gap:8px;padding:10px 12px;border:1px dashed var(--fd-border);border-radius:var(--fd-control-radius);color:var(--fd-muted);font-size:13px;';
|
|
50
|
+
element.innerHTML = `<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">${icon}</svg>`;
|
|
51
|
+
element.append(words);
|
|
52
|
+
return element;
|
|
53
|
+
}
|
|
54
|
+
/** Place and Area for the designer, in its language. */
|
|
55
|
+
function mapKinds(options = {}) {
|
|
56
|
+
const words = (0, labels_1.mapLabels)(options.locale, options.labels);
|
|
57
|
+
const place = {
|
|
58
|
+
id: 'map-place',
|
|
59
|
+
label: words.placeKind,
|
|
60
|
+
group: words.mapsGroup,
|
|
61
|
+
icon: PIN_ICON,
|
|
62
|
+
widget: 'place',
|
|
63
|
+
field: (label) => ({ type: 'json', label }),
|
|
64
|
+
fits: (field) => field.type === 'json',
|
|
65
|
+
settings({ document: doc, set }) {
|
|
66
|
+
const myLocation = checkbox(doc, (on) => set({ myLocation: on ? null : false }));
|
|
67
|
+
const search = checkbox(doc, (on) => set({ search: on ? null : false }));
|
|
68
|
+
const height = heightSetting(doc, words, set);
|
|
69
|
+
const element = settingsRow(doc);
|
|
70
|
+
element.append(row(doc, words.settingMyLocation, myLocation), row(doc, words.settingSearch, search), height.element);
|
|
71
|
+
return {
|
|
72
|
+
element,
|
|
73
|
+
refresh(_page, node) {
|
|
74
|
+
myLocation.checked = node.options?.['myLocation'] !== false;
|
|
75
|
+
search.checked = node.options?.['search'] !== false;
|
|
76
|
+
height.read(node);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
preview: ({ document: doc }) => previewOf(doc, PIN_ICON, words.placeKind),
|
|
81
|
+
};
|
|
82
|
+
const area = {
|
|
83
|
+
id: 'map-area',
|
|
84
|
+
label: words.areaKind,
|
|
85
|
+
group: words.mapsGroup,
|
|
86
|
+
icon: AREA_ICON,
|
|
87
|
+
widget: 'area',
|
|
88
|
+
field: (label) => ({ type: 'json', label }),
|
|
89
|
+
fits: (field) => field.type === 'json',
|
|
90
|
+
settings({ document: doc, set }) {
|
|
91
|
+
const element = settingsRow(doc);
|
|
92
|
+
const boxes = ['polygon', 'box', 'circle'].map((shape) => {
|
|
93
|
+
const box = checkbox(doc, () => {
|
|
94
|
+
const on = boxes.filter((b) => b.box.checked).map((b) => b.shape);
|
|
95
|
+
// At least one shape stays: the last one ticked cannot be taken away.
|
|
96
|
+
if (!on.length) {
|
|
97
|
+
box.checked = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
set({ shapes: on.length === 3 ? null : on.join(',') });
|
|
101
|
+
});
|
|
102
|
+
element.append(row(doc, words[shape], box));
|
|
103
|
+
return { shape, box };
|
|
104
|
+
});
|
|
105
|
+
const legend = doc.createElement('span');
|
|
106
|
+
legend.className = 'fd-map-settings-title';
|
|
107
|
+
legend.style.cssText = 'font-weight:600;';
|
|
108
|
+
legend.textContent = words.settingShapes;
|
|
109
|
+
element.prepend(legend);
|
|
110
|
+
const height = heightSetting(doc, words, set);
|
|
111
|
+
element.append(height.element);
|
|
112
|
+
return {
|
|
113
|
+
element,
|
|
114
|
+
refresh(_page, node) {
|
|
115
|
+
const raw = node.options?.['shapes'];
|
|
116
|
+
const listed = Array.isArray(raw) ? raw.map(String) : typeof raw === 'string' ? raw.split(',').map((s) => s.trim()) : null;
|
|
117
|
+
for (const { shape, box } of boxes)
|
|
118
|
+
box.checked = !listed || listed.includes(shape);
|
|
119
|
+
height.read(node);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
preview: ({ document: doc }) => previewOf(doc, AREA_ICON, words.areaKind),
|
|
124
|
+
};
|
|
125
|
+
return [place, area];
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=kinds.js.map
|