ts-maps 0.3.1 → 0.3.3
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/dist/core-map/control/FullscreenControl.d.ts +39 -0
- package/dist/core-map/control/GeocoderControl.d.ts +61 -0
- package/dist/core-map/control/LocateControl.d.ts +56 -0
- package/dist/core-map/control/NavigationControl.d.ts +39 -0
- package/dist/core-map/control/index.d.ts +8 -0
- package/dist/core-map/game/TerritoryLog.d.ts +66 -0
- package/dist/core-map/game/TerritoryStore.d.ts +59 -0
- package/dist/core-map/game/index.d.ts +11 -0
- package/dist/core-map/game/loop.d.ts +61 -0
- package/dist/core-map/geo/area.d.ts +52 -0
- package/dist/core-map/geo/index.d.ts +30 -0
- package/dist/core-map/geo/index.js +856 -31
- package/dist/core-map/geo/polygonClip.d.ts +75 -0
- package/dist/core-map/geo/validate.d.ts +54 -0
- package/dist/core-map/geometry/index.js +28 -28
- package/dist/core-map/index.d.ts +8 -2
- package/dist/core-map/layer/GeoJSONTileSource.d.ts +50 -0
- package/dist/core-map/layer/RunTrailLayer.d.ts +35 -0
- package/dist/core-map/layer/TerritoryLayer.d.ts +62 -0
- package/dist/core-map/layer/index.d.ts +6 -0
- package/dist/core-map/layer/tile/RasterDEMLayer.d.ts +4 -0
- package/dist/core-map/layer/tile/VectorTileMapLayer.d.ts +59 -11
- package/dist/core-map/layer/tile/urlTemplate.d.ts +1 -2
- package/dist/core-map/map/Map.d.ts +32 -1
- package/dist/core-map/mvt/DecodedTile.d.ts +20 -0
- package/dist/core-map/mvt/FlatTile.d.ts +25 -0
- package/dist/core-map/mvt/VectorTileFeature.d.ts +8 -0
- package/dist/core-map/services/index.js +48 -48
- package/dist/core-map/storage/index.js +5 -5
- package/dist/core-map/style-spec/expressions/formatted.d.ts +36 -0
- package/dist/core-map/style-spec/expressions/operators/binding.d.ts +1 -0
- package/dist/core-map/style-spec/expressions/operators/geometry.d.ts +19 -0
- package/dist/core-map/style-spec/expressions/types.d.ts +3 -0
- package/dist/core-map/style-spec/index.js +460 -60
- package/dist/core-map/style-spec/schema.d.ts +43 -3
- package/dist/core-map/style-spec/types.d.ts +47 -7
- package/dist/core-map/styles/basemap.d.ts +41 -0
- package/dist/core-map/styles/index.d.ts +5 -0
- package/dist/core-map/styles/palette.d.ts +31 -0
- package/dist/core-map/symbols/CollisionIndex.d.ts +22 -5
- package/dist/core-map/symbols/GlyphAtlas.d.ts +6 -1
- package/dist/core-map/symbols/GlyphProvider.d.ts +21 -0
- package/dist/core-map/symbols/GlyphRenderer.d.ts +48 -0
- package/dist/core-map/symbols/IconAtlas.d.ts +10 -1
- package/dist/core-map/symbols/SymbolOverlay.d.ts +47 -0
- package/dist/core-map/symbols/index.d.ts +4 -0
- package/dist/core-map/symbols/index.js +1072 -68
- package/dist/core-map/symbols/loadGlyphs.d.ts +53 -0
- package/dist/core-map/symbols/loadSprite.d.ts +57 -0
- package/dist/core-map/symbols/placement.d.ts +67 -0
- package/dist/core-map/symbols/sdf.d.ts +37 -0
- package/dist/core-map/symbols/sections.d.ts +32 -0
- package/dist/core-map/workers/WorkerPool.d.ts +40 -0
- package/dist/core-map/workers/decodeMvtFlat.d.ts +52 -0
- package/dist/index.js +11783 -6100
- package/package.json +2 -2
- package/src/core-map/ts-maps.css +576 -79
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Control } from './Control';
|
|
2
|
+
/**
|
|
3
|
+
* Expand the map to fill the screen.
|
|
4
|
+
*
|
|
5
|
+
* Two mechanisms, because one of them is unavailable more often than it
|
|
6
|
+
* looks: the Fullscreen API is blocked in cross-origin iframes without an
|
|
7
|
+
* `allowfullscreen` attribute, and on iPhone Safari it does not exist at all —
|
|
8
|
+
* both being exactly the places a map is most likely to be embedded. When it
|
|
9
|
+
* is unavailable the control falls back to a fixed, full-viewport class, which
|
|
10
|
+
* is not true fullscreen (browser chrome stays) but does the thing the user
|
|
11
|
+
* pressed the button for.
|
|
12
|
+
*
|
|
13
|
+
* Either way the map is told to re-measure: the container changes size without
|
|
14
|
+
* a window resize event in the API case.
|
|
15
|
+
*/
|
|
16
|
+
export declare interface FullscreenControlOptions {
|
|
17
|
+
position?: string
|
|
18
|
+
container?: HTMLElement
|
|
19
|
+
title?: string
|
|
20
|
+
titleCancel?: string
|
|
21
|
+
}
|
|
22
|
+
export declare class FullscreenControl extends Control {
|
|
23
|
+
_button?: HTMLAnchorElement;
|
|
24
|
+
_pseudo?: boolean;
|
|
25
|
+
_onDocumentChange?: () => void;
|
|
26
|
+
onAdd(_map: any): HTMLElement;
|
|
27
|
+
onRemove(_map: any): void;
|
|
28
|
+
isFullscreen(): boolean;
|
|
29
|
+
toggle(): this;
|
|
30
|
+
request(): this;
|
|
31
|
+
exit(): this;
|
|
32
|
+
_target(): HTMLElement;
|
|
33
|
+
_documentElement(): Element | null;
|
|
34
|
+
_enterPseudo(): void;
|
|
35
|
+
_exitPseudo(): void;
|
|
36
|
+
_syncFromDocument(): void;
|
|
37
|
+
_onChanged(active: boolean): void;
|
|
38
|
+
_updateButton(active: boolean): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Control } from './Control';
|
|
2
|
+
import { Marker } from '../layer/marker/Marker';
|
|
3
|
+
import type { GeocoderProvider, GeocodingResult } from '../services/types';
|
|
4
|
+
/**
|
|
5
|
+
* A place search box, sitting on top of the geocoder providers in
|
|
6
|
+
* `services/`.
|
|
7
|
+
*
|
|
8
|
+
* The adapters have always been there; what was missing was the control that
|
|
9
|
+
* makes them usable without hand-rolling an input, a debounce, a result list
|
|
10
|
+
* and keyboard handling on every map. The default provider is Nominatim, which
|
|
11
|
+
* needs no key — so `control.geocoder().addTo(map)` searches out of the box.
|
|
12
|
+
*
|
|
13
|
+
* Requests are debounced and the in-flight one is aborted on every keystroke.
|
|
14
|
+
* That is politeness towards a public endpoint as much as it is correctness:
|
|
15
|
+
* without the abort, a slow early response can land after a fast later one and
|
|
16
|
+
* repopulate the list with results for a query the user has already moved past.
|
|
17
|
+
*/
|
|
18
|
+
export declare interface GeocoderControlOptions {
|
|
19
|
+
position?: string
|
|
20
|
+
provider?: GeocoderProvider
|
|
21
|
+
placeholder?: string
|
|
22
|
+
title?: string
|
|
23
|
+
limit?: number
|
|
24
|
+
debounce?: number
|
|
25
|
+
minLength?: number
|
|
26
|
+
collapsed?: boolean
|
|
27
|
+
flyTo?: boolean
|
|
28
|
+
zoom?: number
|
|
29
|
+
marker?: boolean
|
|
30
|
+
proximity?: boolean
|
|
31
|
+
language?: string
|
|
32
|
+
countries?: string[]
|
|
33
|
+
bbox?: [number, number, number, number]
|
|
34
|
+
errorText?: string
|
|
35
|
+
noResultsText?: string
|
|
36
|
+
}
|
|
37
|
+
export declare class GeocoderControl extends Control {
|
|
38
|
+
_input?: HTMLInputElement;
|
|
39
|
+
_list?: HTMLUListElement;
|
|
40
|
+
_toggle?: HTMLAnchorElement;
|
|
41
|
+
_results: GeocodingResult[];
|
|
42
|
+
_selected: number;
|
|
43
|
+
_timer?: ReturnType<typeof setTimeout>;
|
|
44
|
+
_abort?: AbortController;
|
|
45
|
+
_marker?: Marker;
|
|
46
|
+
_expanded?: boolean;
|
|
47
|
+
onAdd(_map: any): HTMLElement;
|
|
48
|
+
onRemove(_map: any): void;
|
|
49
|
+
setQuery(query: string): this;
|
|
50
|
+
clear(): this;
|
|
51
|
+
_onToggle(): void;
|
|
52
|
+
_setExpanded(expanded: boolean): void;
|
|
53
|
+
_onInput(): void;
|
|
54
|
+
_search(query: string): void;
|
|
55
|
+
_request(query: string): void;
|
|
56
|
+
_renderList(): void;
|
|
57
|
+
_onKeyDown(event: KeyboardEvent): void;
|
|
58
|
+
_move(delta: number): void;
|
|
59
|
+
select(index: number): this;
|
|
60
|
+
_cancelPending(): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Circle } from '../layer/vector/Circle';
|
|
2
|
+
import { Control } from './Control';
|
|
3
|
+
import { Marker } from '../layer/marker/Marker';
|
|
4
|
+
/**
|
|
5
|
+
* "Where am I" — the crosshair button every map is expected to have.
|
|
6
|
+
*
|
|
7
|
+
* Centres the map on the device's own position and, unless told otherwise,
|
|
8
|
+
* keeps following it as the position updates. The button carries its own state
|
|
9
|
+
* so the user is never left wondering whether anything happened:
|
|
10
|
+
*
|
|
11
|
+
* idle the outline crosshair
|
|
12
|
+
* locating pulsing, while the first fix is being acquired
|
|
13
|
+
* active filled, while the map is following the device
|
|
14
|
+
* denied struck through, when permission was refused or the fix failed
|
|
15
|
+
*
|
|
16
|
+
* Geolocation is requested on CLICK and never on load. A permission prompt
|
|
17
|
+
* that appears unasked is the fastest way to be denied for the rest of the
|
|
18
|
+
* session, and a denied permission cannot be re-requested without the user
|
|
19
|
+
* going into browser settings.
|
|
20
|
+
*
|
|
21
|
+
* Following stops the moment the user pans, drags, or zooms by hand: a map
|
|
22
|
+
* that yanks itself back under a finger is worse than one that does nothing.
|
|
23
|
+
*/
|
|
24
|
+
export declare interface LocateControlOptions {
|
|
25
|
+
position?: string
|
|
26
|
+
zoom?: number | null
|
|
27
|
+
follow?: boolean
|
|
28
|
+
enableHighAccuracy?: boolean
|
|
29
|
+
timeout?: number
|
|
30
|
+
maximumAge?: number
|
|
31
|
+
title?: string
|
|
32
|
+
titleLocating?: string
|
|
33
|
+
titleActive?: string
|
|
34
|
+
titleDenied?: string
|
|
35
|
+
showMarker?: boolean
|
|
36
|
+
}
|
|
37
|
+
export declare class LocateControl extends Control {
|
|
38
|
+
_button?: HTMLAnchorElement;
|
|
39
|
+
_watchId?: number;
|
|
40
|
+
_following?: boolean;
|
|
41
|
+
_selfMoving?: boolean;
|
|
42
|
+
_marker?: Marker;
|
|
43
|
+
_accuracyCircle?: Circle;
|
|
44
|
+
onAdd(map: any): HTMLElement;
|
|
45
|
+
onRemove(map: any): void;
|
|
46
|
+
start(): this;
|
|
47
|
+
stop(): this;
|
|
48
|
+
_onClick(): void;
|
|
49
|
+
_locate(): void;
|
|
50
|
+
_onPosition(position: GeolocationPosition, recenter: boolean): void;
|
|
51
|
+
_updateMarker(latlng: [number, number], accuracy: number): void;
|
|
52
|
+
_clearMarker(): void;
|
|
53
|
+
_stopFollowing(): void;
|
|
54
|
+
_clearWatch(): void;
|
|
55
|
+
_setState(state: 'idle' | 'locating' | 'active' | 'denied'): void;
|
|
56
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Control } from './Control';
|
|
2
|
+
/**
|
|
3
|
+
* Zoom buttons plus a compass, in one stack — Mapbox's `NavigationControl`.
|
|
4
|
+
*
|
|
5
|
+
* The compass is the part `ZoomControl` cannot grow into: once a map can be
|
|
6
|
+
* rotated and pitched (it can — see `TwoFingerRotate` and `TwoFingerPitch`),
|
|
7
|
+
* there has to be a way back to north that does not involve wrestling two
|
|
8
|
+
* fingers into alignment. The needle doubles as the only always-visible
|
|
9
|
+
* indication that the map is rotated at all.
|
|
10
|
+
*
|
|
11
|
+
* It composes rather than extends ZoomControl: the two are separate controls
|
|
12
|
+
* with separate positions, and inheritance would tie their DOM together for
|
|
13
|
+
* the sake of sharing one small button helper.
|
|
14
|
+
*/
|
|
15
|
+
export declare interface NavigationControlOptions {
|
|
16
|
+
position?: string
|
|
17
|
+
showZoom?: boolean
|
|
18
|
+
showCompass?: boolean
|
|
19
|
+
visualizePitch?: boolean
|
|
20
|
+
resetDuration?: number
|
|
21
|
+
zoomInTitle?: string
|
|
22
|
+
zoomOutTitle?: string
|
|
23
|
+
compassTitle?: string
|
|
24
|
+
}
|
|
25
|
+
export declare class NavigationControl extends Control {
|
|
26
|
+
_zoomInButton?: HTMLAnchorElement;
|
|
27
|
+
_zoomOutButton?: HTMLAnchorElement;
|
|
28
|
+
_compassButton?: HTMLAnchorElement;
|
|
29
|
+
_needle?: HTMLElement;
|
|
30
|
+
onAdd(map: any): HTMLElement;
|
|
31
|
+
onRemove(map: any): void;
|
|
32
|
+
_zoomIn(event: any): void;
|
|
33
|
+
_zoomOut(event: any): void;
|
|
34
|
+
_resetNorth(): void;
|
|
35
|
+
_reducedMotion(): boolean;
|
|
36
|
+
_updateCompass(): void;
|
|
37
|
+
_updateDisabled(): void;
|
|
38
|
+
_createButton(className: string, title: string, html: string, container: HTMLElement, fn: (event: any) => void): HTMLAnchorElement;
|
|
39
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
export type { FullscreenControlOptions } from './FullscreenControl';
|
|
2
|
+
export type { GeocoderControlOptions } from './GeocoderControl';
|
|
3
|
+
export type { LocateControlOptions } from './LocateControl';
|
|
4
|
+
export type { NavigationControlOptions } from './NavigationControl';
|
|
1
5
|
export { AttributionControl } from './AttributionControl';
|
|
2
6
|
export { Control } from './Control';
|
|
7
|
+
export { FullscreenControl } from './FullscreenControl';
|
|
8
|
+
export { GeocoderControl } from './GeocoderControl';
|
|
3
9
|
export { LayersControl } from './LayersControl';
|
|
10
|
+
export { LocateControl } from './LocateControl';
|
|
11
|
+
export { NavigationControl } from './NavigationControl';
|
|
4
12
|
export { ScaleControl } from './ScaleControl';
|
|
5
13
|
export { ZoomControl } from './ZoomControl';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Evented } from '../core/Events';
|
|
2
|
+
import type { CaptureResult, TerritoryStore, TerritoryStoreOptions } from './TerritoryStore';
|
|
3
|
+
import type { MultiPolygon, Ring } from '../geo/polygonClip';
|
|
4
|
+
/**
|
|
5
|
+
* The agreed order: server sequence, then timestamp, then id.
|
|
6
|
+
*
|
|
7
|
+
* Every participant computes this the same way from the event alone, which is
|
|
8
|
+
* what makes the fold converge. The id tiebreak is not a formality — phone
|
|
9
|
+
* clocks disagree, so identical timestamps are common enough that without it
|
|
10
|
+
* two clients would order the same pair differently and diverge.
|
|
11
|
+
*/
|
|
12
|
+
export declare function compareEvents(a: CaptureEvent, b: CaptureEvent): number;
|
|
13
|
+
export declare interface CaptureEvent {
|
|
14
|
+
id: string
|
|
15
|
+
owner: string
|
|
16
|
+
ring: Ring
|
|
17
|
+
at: number
|
|
18
|
+
seq?: number
|
|
19
|
+
}
|
|
20
|
+
export declare interface AppliedCapture {
|
|
21
|
+
event: CaptureEvent
|
|
22
|
+
result?: CaptureResult
|
|
23
|
+
duplicate: boolean
|
|
24
|
+
replayed: boolean
|
|
25
|
+
}
|
|
26
|
+
export declare interface TerritoryLogOptions {
|
|
27
|
+
maxEvents?: number
|
|
28
|
+
store?: TerritoryStoreOptions
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An ordered log of captures, and the territory they add up to.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* const log = new TerritoryLog()
|
|
35
|
+
*
|
|
36
|
+
* // Local: apply at once, send to the server.
|
|
37
|
+
* const applied = log.apply({ id: uuid(), owner: 'me', ring, at: Date.now() })
|
|
38
|
+
* socket.send(applied.event)
|
|
39
|
+
*
|
|
40
|
+
* // Remote: apply whatever arrives, in whatever order.
|
|
41
|
+
* socket.on('capture', event => log.apply(event))
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare class TerritoryLog extends Evented {
|
|
45
|
+
_log: CaptureEvent[];
|
|
46
|
+
_seen: Set<string>;
|
|
47
|
+
_store: TerritoryStore;
|
|
48
|
+
_baseline: Map<string, MultiPolygon>;
|
|
49
|
+
_options: Required<Omit<TerritoryLogOptions, 'store'>> & { store?: TerritoryStoreOptions };
|
|
50
|
+
initialize(options?: TerritoryLogOptions): void;
|
|
51
|
+
get store(): TerritoryStore;
|
|
52
|
+
get events(): readonly CaptureEvent[];
|
|
53
|
+
apply(event: CaptureEvent): AppliedCapture;
|
|
54
|
+
applyAll(events: CaptureEvent[]): AppliedCapture[];
|
|
55
|
+
has(id: string): boolean;
|
|
56
|
+
confirm(id: string, seq: number): boolean;
|
|
57
|
+
compact(keep?: number): number;
|
|
58
|
+
snapshot(): {
|
|
59
|
+
baseline: Array<{ owner: string, territory: MultiPolygon }>
|
|
60
|
+
events: CaptureEvent[]
|
|
61
|
+
};
|
|
62
|
+
restore(snapshot: { baseline?: Array<{ owner: string, territory: MultiPolygon }>, events?: CaptureEvent[] }): this;
|
|
63
|
+
clear(): this;
|
|
64
|
+
_newStore(): TerritoryStore;
|
|
65
|
+
_rebuild(): void;
|
|
66
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Evented } from '../core/Events';
|
|
2
|
+
import type { MultiPolygon, Position, Ring } from '../geo/polygonClip';
|
|
3
|
+
export declare interface TerritoryStoreOptions {
|
|
4
|
+
steal?: boolean
|
|
5
|
+
minFragmentArea?: number
|
|
6
|
+
}
|
|
7
|
+
export declare interface StolenFrom {
|
|
8
|
+
owner: string
|
|
9
|
+
area: number
|
|
10
|
+
}
|
|
11
|
+
export declare interface CaptureResult {
|
|
12
|
+
owner: string
|
|
13
|
+
ring: Ring
|
|
14
|
+
areaClaimed: number
|
|
15
|
+
areaGained: number
|
|
16
|
+
stolen: StolenFrom[]
|
|
17
|
+
territory: MultiPolygon
|
|
18
|
+
totalArea: number
|
|
19
|
+
}
|
|
20
|
+
export declare interface LeaderboardEntry {
|
|
21
|
+
owner: string
|
|
22
|
+
area: number
|
|
23
|
+
pieces: number
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Territories by owner.
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* const territories = new TerritoryStore()
|
|
30
|
+
* territories.on('capture', ({ owner, areaGained, stolen }) => {
|
|
31
|
+
* toast(`${owner} claimed ${formatArea(areaGained)}`)
|
|
32
|
+
* })
|
|
33
|
+
* territories.capture('sam', loop.ring)
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class TerritoryStore extends Evented {
|
|
37
|
+
_byOwner: Map<string, MultiPolygon>;
|
|
38
|
+
_options: Required<TerritoryStoreOptions>;
|
|
39
|
+
initialize(options?: TerritoryStoreOptions): void;
|
|
40
|
+
capture(owner: string, ring: Ring): CaptureResult;
|
|
41
|
+
get(owner: string): MultiPolygon;
|
|
42
|
+
set(owner: string, territory: MultiPolygon): this;
|
|
43
|
+
areaOf(owner: string): number;
|
|
44
|
+
owners(): string[];
|
|
45
|
+
leaderboard(): LeaderboardEntry[];
|
|
46
|
+
ownerAt(position: Position): string | null;
|
|
47
|
+
preview(owner: string, ring: Ring): { areaGained: number, stolen: StolenFrom[] };
|
|
48
|
+
toGeoJSON(): {
|
|
49
|
+
type: 'FeatureCollection'
|
|
50
|
+
features: Array<{
|
|
51
|
+
type: 'Feature'
|
|
52
|
+
properties: { owner: string, area: number }
|
|
53
|
+
geometry: { type: 'MultiPolygon', coordinates: MultiPolygon }
|
|
54
|
+
}>
|
|
55
|
+
};
|
|
56
|
+
loadGeoJSON(collection: { features?: Array<any> }): this;
|
|
57
|
+
clear(): this;
|
|
58
|
+
_prune(territory: MultiPolygon): MultiPolygon;
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { AppliedCapture, CaptureEvent, TerritoryLogOptions } from './TerritoryLog';
|
|
2
|
+
export type { DetectedLoop, LoopDetectorOptions, LoopOptions } from './loop';
|
|
3
|
+
export type {
|
|
4
|
+
CaptureResult,
|
|
5
|
+
LeaderboardEntry,
|
|
6
|
+
StolenFrom,
|
|
7
|
+
TerritoryStoreOptions,
|
|
8
|
+
} from './TerritoryStore';
|
|
9
|
+
export { compareEvents, TerritoryLog } from './TerritoryLog';
|
|
10
|
+
export { detectLoop, LoopDetector, simplifyTrack } from './loop';
|
|
11
|
+
export { TerritoryStore } from './TerritoryStore';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Position } from '../geo/area';
|
|
2
|
+
/**
|
|
3
|
+
* Find the first loop a track closes, or null.
|
|
4
|
+
*
|
|
5
|
+
* The loop returned is the earliest one that closes, not the largest — a
|
|
6
|
+
* runner who laps a park and keeps going has earned the park at the moment
|
|
7
|
+
* they closed it, and waiting to see whether a bigger shape appears later
|
|
8
|
+
* would mean the game reacting to a run several minutes after it happened.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectLoop(track: Position[], options?: LoopOptions): DetectedLoop | null;
|
|
11
|
+
/**
|
|
12
|
+
* Reduce a track to its shape, dropping points that say nothing new.
|
|
13
|
+
*
|
|
14
|
+
* A GPS logging at 1 Hz produces a great many points that lie on the line
|
|
15
|
+
* between their neighbours. Keeping them costs area calculations, boolean
|
|
16
|
+
* operations and every redraw, and they change none of the answers.
|
|
17
|
+
*
|
|
18
|
+
* `tolerance` is in metres: the furthest a dropped point may lie from the line
|
|
19
|
+
* that replaces it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function simplifyTrack(track: Position[], tolerance?: number): Position[];
|
|
22
|
+
export declare interface LoopOptions {
|
|
23
|
+
snapDistance?: number
|
|
24
|
+
minArea?: number
|
|
25
|
+
minLoopLength?: number
|
|
26
|
+
minPoints?: number
|
|
27
|
+
}
|
|
28
|
+
export declare interface DetectedLoop {
|
|
29
|
+
ring: Position[]
|
|
30
|
+
area: number
|
|
31
|
+
perimeter: number
|
|
32
|
+
startIndex: number
|
|
33
|
+
endIndex: number
|
|
34
|
+
closure: 'crossing' | 'proximity'
|
|
35
|
+
}
|
|
36
|
+
export declare interface LoopDetectorOptions extends LoopOptions {
|
|
37
|
+
carryOver?: number
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Feed it GPS points; it tells you when a loop closes.
|
|
41
|
+
*
|
|
42
|
+
* A live run calls this on every position update, so it does the work
|
|
43
|
+
* incrementally rather than re-scanning the whole track each time: only the
|
|
44
|
+
* newest segment is tested, against the ones already recorded.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* const detector = new LoopDetector({ snapDistance: 25 })
|
|
48
|
+
* watchPosition((position) => {
|
|
49
|
+
* const loop = detector.push([position.coords.longitude, position.coords.latitude])
|
|
50
|
+
* if (loop)
|
|
51
|
+
* territories.capture('me', loop.ring)
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare class LoopDetector {
|
|
56
|
+
constructor(options?: LoopDetectorOptions);
|
|
57
|
+
get track(): Position[];
|
|
58
|
+
get length(): number;
|
|
59
|
+
push(point: Position): DetectedLoop | null;
|
|
60
|
+
reset(): void;
|
|
61
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signed area of one ring in square metres, positive when it winds
|
|
3
|
+
* counter-clockwise.
|
|
4
|
+
*
|
|
5
|
+
* Signed rather than absolute because the sign is what distinguishes a hole
|
|
6
|
+
* from an outer ring, and a territory with a lake in it is a shape this has to
|
|
7
|
+
* measure correctly.
|
|
8
|
+
*
|
|
9
|
+
* Coordinates are `[longitude, latitude]`, the order GeoJSON uses.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ringArea(ring: Ring): number;
|
|
12
|
+
/**
|
|
13
|
+
* Area of a polygon in square metres: its outer ring less its holes.
|
|
14
|
+
*
|
|
15
|
+
* Ring winding is not trusted — a track recorded by a runner going clockwise
|
|
16
|
+
* is as valid as one going the other way, and GeoJSON from the wild is
|
|
17
|
+
* inconsistent about it. The first ring is taken as the outer one and the rest
|
|
18
|
+
* as holes, which is what the GeoJSON specification says they are.
|
|
19
|
+
*/
|
|
20
|
+
export declare function polygonArea(polygon: Polygon): number;
|
|
21
|
+
/** Area of a multipolygon in square metres. */
|
|
22
|
+
export declare function multiPolygonArea(multi: MultiPolygon): number;
|
|
23
|
+
/**
|
|
24
|
+
* Perimeter of a ring in metres.
|
|
25
|
+
*
|
|
26
|
+
* Useful next to the area: two territories of the same size but very different
|
|
27
|
+
* perimeters were earned by quite different runs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function ringPerimeter(ring: Ring, closed?: boolean): number;
|
|
30
|
+
/** Great-circle distance between two `[lng, lat]` positions, in metres. */
|
|
31
|
+
export declare function haversine(a: Position, b: Position): number;
|
|
32
|
+
/**
|
|
33
|
+
* Square metres as something to put on a screen.
|
|
34
|
+
*
|
|
35
|
+
* The unit is chosen for the magnitude, because a territory game spans four
|
|
36
|
+
* orders of it: a small block is a few thousand square metres, a good long run
|
|
37
|
+
* encloses a couple of square kilometres, and "0.004 km²" tells a player
|
|
38
|
+
* nothing they can feel.
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatArea(squareMetres: number, options?: FormatAreaOptions): string;
|
|
41
|
+
/** Metres as something to put on a screen. */
|
|
42
|
+
export declare function formatDistance(metres: number, options?: FormatAreaOptions): string;
|
|
43
|
+
/** Mean Earth radius in metres, as WGS 84 defines it. */
|
|
44
|
+
export declare const EARTH_RADIUS: 6371008.8;
|
|
45
|
+
export declare interface FormatAreaOptions {
|
|
46
|
+
units?: 'metric' | 'imperial'
|
|
47
|
+
locale?: string
|
|
48
|
+
}
|
|
49
|
+
export type Position = [number, number] | number[];
|
|
50
|
+
export type Ring = Position[];
|
|
51
|
+
export type Polygon = Ring[];
|
|
52
|
+
export type MultiPolygon = Polygon[];
|
|
@@ -4,6 +4,16 @@ export type { LatLngBoundsLike } from './LatLngBounds';
|
|
|
4
4
|
export type { DEMEncoding } from './elevation';
|
|
5
5
|
export type { TerrainMesh, TerrainMeshOptions } from './terrainMesh';
|
|
6
6
|
export type { TerrainSourceOptions, TileCoord } from './TerrainSource';
|
|
7
|
+
export type { FormatAreaOptions } from './area';
|
|
8
|
+
// Renamed on the way out: `Polygon` and `Position` are already taken at the
|
|
9
|
+
// package root by the vector layer and the screen-space point, and a game's
|
|
10
|
+
// coordinate arrays are not either of those.
|
|
11
|
+
export type {
|
|
12
|
+
MultiPolygon as MultiPolygonCoordinates,
|
|
13
|
+
Polygon as PolygonCoordinates,
|
|
14
|
+
Position as LngLat,
|
|
15
|
+
Ring as LinearRing,
|
|
16
|
+
} from './area';
|
|
7
17
|
export { LatLng, toLatLng } from './LatLng';
|
|
8
18
|
export { LatLngBounds, toLatLngBounds } from './LatLngBounds';
|
|
9
19
|
export { decodeElevationGrid, decodeMapboxRGB, decodeTerrariumRGB, getElevationDecoder, sampleElevationBilinear } from './elevation';
|
|
@@ -11,3 +21,23 @@ export { buildTerrainMesh } from './terrainMesh';
|
|
|
11
21
|
export { TerrainSource } from './TerrainSource';
|
|
12
22
|
export { Projection };
|
|
13
23
|
export * from './crs/index';
|
|
24
|
+
export {
|
|
25
|
+
EARTH_RADIUS,
|
|
26
|
+
formatArea,
|
|
27
|
+
formatDistance,
|
|
28
|
+
haversine,
|
|
29
|
+
multiPolygonArea,
|
|
30
|
+
polygonArea,
|
|
31
|
+
ringArea,
|
|
32
|
+
ringPerimeter,
|
|
33
|
+
} from './area';
|
|
34
|
+
export { contains, difference, intersects, intersection, union, xor } from './polygonClip';
|
|
35
|
+
export {
|
|
36
|
+
InvalidGeometryError,
|
|
37
|
+
prepareClaim,
|
|
38
|
+
resolveSelfIntersections,
|
|
39
|
+
selfIntersects,
|
|
40
|
+
splitSelfIntersecting,
|
|
41
|
+
unwrapRing,
|
|
42
|
+
validateRing,
|
|
43
|
+
} from './validate';
|