@bitbaum/geo-kit 0.2.0 → 0.3.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/README.md +2 -1
- package/dist/map/controller.d.ts +13 -1
- package/dist/map/controller.js +44 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,7 +130,8 @@ map.flyTo(cameraForBounds([5.96, 45.82, 10.49, 47.81], limits));
|
|
|
130
130
|
- **Layers.** The palette's sphere, graticule, glow and limb shade are each
|
|
131
131
|
optional, so a lit globe of the world and a plain map where the data is the
|
|
132
132
|
picture are the same painter. `over` draws the caller's own layer (sites,
|
|
133
|
-
routes, figures) on top each frame, with the frame's projection
|
|
133
|
+
routes, figures) on top each frame, with the frame's projection, and
|
|
134
|
+
`pickFirst` lets that layer take a tap or a hover before the features.
|
|
134
135
|
- **Pure parts.** The camera, motion, culling, picking and the fast
|
|
135
136
|
projection are exported and tested without a browser.
|
|
136
137
|
|
package/dist/map/controller.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface FrameInfo {
|
|
|
24
24
|
flight: number | null;
|
|
25
25
|
/** True while the map moves and paints its coarse shapes. */
|
|
26
26
|
moving: boolean;
|
|
27
|
+
/** Milliseconds since the reader last touched the map. */
|
|
28
|
+
sinceTouch: number;
|
|
27
29
|
}
|
|
28
30
|
export interface MapOptions<P = Record<string, unknown>> {
|
|
29
31
|
shapes: MapShapes<P>;
|
|
@@ -42,8 +44,16 @@ export interface MapOptions<P = Record<string, unknown>> {
|
|
|
42
44
|
idleTurn?: boolean;
|
|
43
45
|
/** A feature was tapped or chosen by keyboard; null for the sea. */
|
|
44
46
|
onSelect?: (id: string | null) => void;
|
|
45
|
-
/** The mouse is over a feature (null when over none), at a canvas point. */
|
|
47
|
+
/** The mouse is over a feature (null when over none, or when a gesture starts), at a canvas point. */
|
|
46
48
|
onHover?: (id: string | null, x: number, y: number) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Asked first on a tap and on hover, before the features: true when the
|
|
51
|
+
* caller's own layer (a site, a marker) takes the point, so no feature is
|
|
52
|
+
* selected or outlined there.
|
|
53
|
+
*/
|
|
54
|
+
pickFirst?: (x: number, y: number, projection: GeoProjection, kind: "tap" | "hover") => boolean;
|
|
55
|
+
/** The keyboard turned the map: the feature now facing the reader, or null. */
|
|
56
|
+
onFacing?: (id: string | null) => void;
|
|
47
57
|
/** Draw on top of the map each frame. */
|
|
48
58
|
over?: (ctx: CanvasRenderingContext2D, basis: Basis, projection: GeoProjection, frame: FrameInfo) => void;
|
|
49
59
|
/** True while the overlay animates and needs another frame. */
|
|
@@ -62,6 +72,8 @@ export interface MapController {
|
|
|
62
72
|
fly?: boolean;
|
|
63
73
|
}): void;
|
|
64
74
|
flyTo(camera: Camera, duration?: number): void;
|
|
75
|
+
/** Go to a view at once, with no flight. */
|
|
76
|
+
jumpTo(camera: Camera): void;
|
|
65
77
|
zoomBy(factor: number | "home"): void;
|
|
66
78
|
camera(): Camera;
|
|
67
79
|
projection(): GeoProjection | null;
|
package/dist/map/controller.js
CHANGED
|
@@ -102,7 +102,9 @@ export function createMap(canvas, options) {
|
|
|
102
102
|
const dpr = pixelRatio();
|
|
103
103
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
104
104
|
const flightAt = motion.flight ? (now - motion.flight.start) / motion.flight.f.duration : null;
|
|
105
|
-
paintMap(ctx, size, proj, moving ? shapes.coarse : shapes, paint, { hover: feature(hover), selected: feature(selected) }, options.over
|
|
105
|
+
paintMap(ctx, size, proj, moving ? shapes.coarse : shapes, paint, { hover: feature(hover), selected: feature(selected) }, options.over
|
|
106
|
+
? (c, basis, p) => options.over(c, basis, p, { now, flight: flightAt, moving, sinceTouch: now - touched })
|
|
107
|
+
: undefined);
|
|
106
108
|
}
|
|
107
109
|
if (again)
|
|
108
110
|
raf = requestAnimationFrame(tick);
|
|
@@ -144,6 +146,17 @@ export function createMap(canvas, options) {
|
|
|
144
146
|
return proj ? pickAt(index, proj, x, y) : null;
|
|
145
147
|
};
|
|
146
148
|
const choose = (id) => options.onSelect?.(id);
|
|
149
|
+
const takenFirst = (x, y, kind) => {
|
|
150
|
+
const proj = projection();
|
|
151
|
+
return !!proj && !!options.pickFirst?.(x, y, proj, kind);
|
|
152
|
+
};
|
|
153
|
+
const facing = () => {
|
|
154
|
+
const view = viewAt(performance.now());
|
|
155
|
+
if (!view)
|
|
156
|
+
return null;
|
|
157
|
+
const d = disc(view);
|
|
158
|
+
return pick(d.cx, d.cy)?.id ?? null;
|
|
159
|
+
};
|
|
147
160
|
/* ------------------------------------------------------------ input */
|
|
148
161
|
const pointers = new Map();
|
|
149
162
|
let gesture = { moved: false, vx: 0, vy: 0, t: 0, startX: 0, startY: 0 };
|
|
@@ -155,7 +168,16 @@ export function createMap(canvas, options) {
|
|
|
155
168
|
keydown(e) {
|
|
156
169
|
const step = e.shiftKey ? 30 : 10;
|
|
157
170
|
const perStep = step / Math.max(camera.k, 1);
|
|
158
|
-
const turn = (dLon, dLat) =>
|
|
171
|
+
const turn = (dLon, dLat) => {
|
|
172
|
+
motion = {};
|
|
173
|
+
touched = performance.now();
|
|
174
|
+
camera = {
|
|
175
|
+
center: [wrapLon(camera.center[0] + dLon * perStep), clampTilt(camera.center[1] + dLat * perStep, limits)],
|
|
176
|
+
k: camera.k,
|
|
177
|
+
};
|
|
178
|
+
invalidate();
|
|
179
|
+
options.onFacing?.(facing());
|
|
180
|
+
};
|
|
159
181
|
const keys = {
|
|
160
182
|
ArrowLeft: () => turn(-1, 0),
|
|
161
183
|
ArrowRight: () => turn(1, 0),
|
|
@@ -166,13 +188,7 @@ export function createMap(canvas, options) {
|
|
|
166
188
|
"-": () => zoomBy(1 / 1.5),
|
|
167
189
|
"0": () => zoomBy("home"),
|
|
168
190
|
Home: () => zoomBy("home"),
|
|
169
|
-
Enter: () =>
|
|
170
|
-
const view = viewAt(performance.now());
|
|
171
|
-
if (!view)
|
|
172
|
-
return;
|
|
173
|
-
const d = disc(view);
|
|
174
|
-
choose(pick(d.cx, d.cy)?.id ?? null);
|
|
175
|
-
},
|
|
191
|
+
Enter: () => choose(facing()),
|
|
176
192
|
};
|
|
177
193
|
const run = keys[e.key];
|
|
178
194
|
if (!run)
|
|
@@ -197,19 +213,27 @@ export function createMap(canvas, options) {
|
|
|
197
213
|
gesture.moved = true; // a second finger is never a tap
|
|
198
214
|
grab();
|
|
199
215
|
grabbed = true;
|
|
216
|
+
if (hover !== null) {
|
|
217
|
+
hover = null;
|
|
218
|
+
options.onHover?.(null, at.x, at.y);
|
|
219
|
+
}
|
|
200
220
|
},
|
|
201
221
|
pointermove(e) {
|
|
202
222
|
const at = local(e);
|
|
203
223
|
const before = pointers.get(e.pointerId);
|
|
204
224
|
if (!before) {
|
|
205
225
|
if (e.pointerType === "mouse") {
|
|
206
|
-
|
|
207
|
-
|
|
226
|
+
touched = performance.now();
|
|
227
|
+
const taken = takenFirst(at.x, at.y, "hover");
|
|
228
|
+
const id = taken ? null : (pick(at.x, at.y)?.id ?? null);
|
|
229
|
+
canvas.style.cursor = taken || id ? "pointer" : "";
|
|
208
230
|
if (id !== hover) {
|
|
209
231
|
hover = id;
|
|
210
232
|
invalidate();
|
|
211
233
|
}
|
|
212
|
-
|
|
234
|
+
// The caller's own layer answered this point; its hover is its own to show.
|
|
235
|
+
if (!taken)
|
|
236
|
+
options.onHover?.(id, at.x, at.y);
|
|
213
237
|
}
|
|
214
238
|
return;
|
|
215
239
|
}
|
|
@@ -248,7 +272,8 @@ export function createMap(canvas, options) {
|
|
|
248
272
|
const g = gesture;
|
|
249
273
|
if (!g.moved) {
|
|
250
274
|
const at = local(e);
|
|
251
|
-
|
|
275
|
+
if (!takenFirst(at.x, at.y, "tap"))
|
|
276
|
+
choose(pick(at.x, at.y)?.id ?? null);
|
|
252
277
|
return;
|
|
253
278
|
}
|
|
254
279
|
const fresh = e.timeStamp - g.t < 80;
|
|
@@ -327,6 +352,12 @@ export function createMap(canvas, options) {
|
|
|
327
352
|
invalidate();
|
|
328
353
|
},
|
|
329
354
|
flyTo,
|
|
355
|
+
jumpTo(to) {
|
|
356
|
+
motion = {};
|
|
357
|
+
touched = performance.now();
|
|
358
|
+
camera = { center: [wrapLon(to.center[0]), clampTilt(to.center[1], limits)], k: clampZoom(to.k, limits) };
|
|
359
|
+
invalidate();
|
|
360
|
+
},
|
|
330
361
|
zoomBy,
|
|
331
362
|
camera: () => camera,
|
|
332
363
|
projection: () => projection(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/geo-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Versioned, on-demand geography resources and sourced boundary assertions for web applications, and a Canvas 2D renderer for them (globe or map).",
|
|
5
5
|
"author": "BitBaum",
|
|
6
6
|
"homepage": "https://github.com/bitbaum/geo-kit#readme",
|