@maptrip/map-tiles 1.1.0 → 1.2.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.
Files changed (39) hide show
  1. package/README.md +5 -3
  2. package/dist/api-extensions/google/MapTripGoogleMapType.d.ts +28 -0
  3. package/dist/api-extensions/leaflet/MapTripLeafletGridLayer.d.ts +4 -0
  4. package/dist/api-extensions/openlayers/MapTripOpenLayersSource.d.ts +19 -0
  5. package/dist/core/CollisionDetector.d.ts +14 -0
  6. package/dist/core/VectorTileFactory.d.ts +64 -0
  7. package/dist/core/VectorTileLoader.d.ts +40 -0
  8. package/dist/core/VectorTileRenderer.d.ts +123 -0
  9. package/dist/core/constants.d.ts +10 -0
  10. package/dist/core/rendering_config_default.d.ts +658 -0
  11. package/dist/core/rendering_config_grayscale.d.ts +638 -0
  12. package/dist/core/rendering_config_traffic.d.ts +39 -0
  13. package/dist/map-tiles.d.ts +9 -0
  14. package/dist/map-tiles.js +23647 -10188
  15. package/dist/map-tiles.js.map +1 -0
  16. package/dist/types/interfaces.d.ts +135 -0
  17. package/lib/api-extensions/google/MapTripGoogleMapType.ts +40 -0
  18. package/lib/api-extensions/leaflet/MapTripLeafletGridLayer.ts +44 -0
  19. package/lib/api-extensions/openlayers/MapTripOpenLayersSource.ts +61 -0
  20. package/lib/core/CollisionDetector.ts +95 -0
  21. package/lib/core/VectorTileFactory.ts +327 -0
  22. package/lib/core/VectorTileLoader.ts +165 -0
  23. package/lib/core/VectorTileRenderer.ts +1212 -0
  24. package/lib/core/constants.ts +21 -0
  25. package/lib/core/rendering_config_default.ts +1131 -0
  26. package/lib/{main.js → main.ts} +2 -0
  27. package/lib/types/interfaces.ts +147 -0
  28. package/package.json +19 -14
  29. package/lib/api-extensions/google/MapTripGoogleMapType.js +0 -25
  30. package/lib/api-extensions/leaflet/MapTripLeafletGridLayer.js +0 -24
  31. package/lib/api-extensions/openlayers/MapTripOpenLayersSource.js +0 -43
  32. package/lib/core/CollisionDetector.js +0 -65
  33. package/lib/core/VectorTileFactory.js +0 -245
  34. package/lib/core/VectorTileLoader.js +0 -132
  35. package/lib/core/VectorTileRenderer.js +0 -826
  36. package/lib/core/constants.js +0 -18
  37. package/lib/core/rendering_config_default.js +0 -1107
  38. /package/lib/core/{rendering_config_grayscale.js → rendering_config_grayscale.ts} +0 -0
  39. /package/lib/core/{rendering_config_traffic.js → rendering_config_traffic.ts} +0 -0
@@ -1,826 +0,0 @@
1
- import { CollisionDetector } from './CollisionDetector.js';
2
-
3
- class VectorTileRenderer {
4
-
5
- constructor(styles) {
6
- this._styles = styles;
7
- this._images = {};
8
-
9
- this.preloadImages();
10
- }
11
-
12
- drawTile(layer, z, boundaries, context) {
13
- context.fillStyle = this.getBackgroundColor(z);
14
- context.fillRect(0, 0, 256, 256);
15
-
16
- var collisions = new CollisionDetector();
17
-
18
- let polygons = layer
19
- .filter(it => !!it.properties && !!it.geometry.type)
20
- .filter(it => it.geometry.type === 'Polygon' || it.geometry.type === 'MultiPolygon')
21
- .sort((a, b) => parseInt(a.properties.sort_rank) - parseInt(b.properties.sort_rank));
22
-
23
- let lines = layer
24
- .filter(it => !!it.properties)
25
- .filter(it => it.geometry.type === 'LineString' || it.geometry.type === 'MultiLineString')
26
- .sort((a, b) => parseInt(a.properties.sort_rank) - parseInt(b.properties.sort_rank));
27
-
28
- let points = layer
29
- .filter(it => !!it.properties)
30
- .filter(it => it.geometry.type === 'Point')
31
- .sort((a, b) => parseInt(a.properties.sort_rank) - parseInt(b.properties.sort_rank));
32
-
33
- this.drawTilePhase(polygons, z, boundaries, context, 'areas');
34
- this.drawPolylineLayers(lines, z, boundaries, context);
35
- this.drawTilePhase(points, z, boundaries, context, 'texts', collisions);
36
- this.drawTilePhase(lines, z, boundaries, context, 'lineTexts', collisions);
37
- this.drawTilePhase(polygons, z, boundaries, context, 'areaTexts', collisions); // for rivers etc (i.e. less important than the other texts)
38
- }
39
-
40
- drawPolylineLayers(lines, z, boundaries, context) {
41
- let rank = lines
42
- .map(it => parseInt(it.properties.sort_rank))
43
- .filter((value, index, self) => self.indexOf(value) === index)
44
- .sort((a, b) => a - b);
45
-
46
- for (let i = 0; i < rank.length; i++) {
47
- // select all lines with this sort rank
48
- let currentRank = lines.filter(it => parseInt(it.properties.sort_rank) === rank[i]);
49
-
50
- this.drawTilePhase(currentRank, z, boundaries, context, 'linesOutline');
51
- this.drawTilePhase(currentRank, z, boundaries, context, 'lines');
52
- }
53
- }
54
-
55
- drawErroredTile(canvas) {
56
- var context = canvas.getContext('2d');
57
- context.fillStyle = '#C8C8C8';
58
- context.fillRect(0, 0, 256, 256);
59
- context.fillStyle = '#AAA';
60
- context.font = "15px Arial";
61
- context.textAlign = "center";
62
- context.fillText("Could not display map tile", canvas.width / 2, canvas.height / 2 - 10);
63
- context.fillText("Please refresh", canvas.width / 2, canvas.height / 2 + 10);
64
- }
65
-
66
- drawTilePhase(layer, z, boundaries, context, phase, collisions) {
67
- var texts = {};
68
-
69
- for (var i = 0; i < layer.length; i++) {
70
- var kind = layer[i].properties.kind;
71
-
72
- // look up the renderer for this kind of data
73
- var rendererName = this._styles.layers[kind];
74
- if (rendererName && this._styles.renderers[rendererName]) {
75
- var renderer = this._styles.renderers[rendererName];
76
-
77
- for (var j = 0; j < renderer.length; j++) {
78
- if (renderer[j].from <= z && renderer[j].to >= z) {
79
- // this definition is used for the current zoom layer
80
-
81
- if (this.accept(renderer[j], layer[i])) // apply filters
82
- {
83
- if (layer[i].geometry.type === 'Polygon') {
84
- if ((phase === 'areasOutline' || phase === 'areas') && renderer[j].polygon) {
85
- this.drawPolygon(layer[i].geometry.coordinates, layer[i].properties, z, boundaries, renderer[j].polygon, context, phase);
86
- }
87
- else if (phase === 'areaTexts' && renderer[j].text) {
88
- this.drawPolygon(layer[i].geometry.coordinates, layer[i].properties, z, boundaries, renderer[j].text, context, phase, collisions, texts);
89
- }
90
-
91
- }
92
- else if (layer[i].geometry.type === 'MultiPolygon') {
93
- if ((phase === 'areasOutline' || phase === 'areas') && renderer[j].polygon) {
94
- for (let k = 0; k < layer[i].geometry.coordinates.length; k++) {
95
- this.drawPolygon(layer[i].geometry.coordinates[k], layer[i].properties, z, boundaries, renderer[j].polygon, context, phase);
96
- }
97
- }
98
- else if (phase === 'areaTexts' && renderer[j].text) {
99
- for (let k = 0; k < layer[i].geometry.coordinates.length; k++) {
100
- this.drawPolygon(layer[i].geometry.coordinates[k], layer[i].properties, z, boundaries, renderer[j].text, context, phase, collisions, texts);
101
- }
102
- }
103
- }
104
- else if (layer[i].geometry.type === 'LineString') {
105
- if ((phase === 'linesOutline' || phase === 'lines') && renderer[j].line) {
106
- this.drawPolyline(layer[i].geometry.coordinates, layer[i].properties, boundaries, renderer[j].line, context, phase);
107
- }
108
- else if (phase === 'lineTexts' && renderer[j].text) {
109
- this.drawPolylineText(layer[i].geometry.coordinates, layer[i].properties, boundaries, renderer[j].text, context, collisions, texts);
110
- }
111
- }
112
- else if (layer[i].geometry.type === 'MultiLineString') {
113
- if ((phase === 'linesOutline' || phase === 'lines') && renderer[j].line) {
114
- for (let k = 0; k < layer[i].geometry.coordinates.length; k++) {
115
- this.drawPolyline(layer[i].geometry.coordinates[k], layer[i].properties, boundaries, renderer[j].line, context, phase);
116
- }
117
- }
118
- else if (phase === 'lineTexts' && renderer[j].text) {
119
- for (let k = 0; k < layer[i].geometry.coordinates.length; k++) {
120
- this.drawPolylineText(layer[i].geometry.coordinates[k], layer[i].properties, boundaries, renderer[j].text, context, collisions, texts);
121
- }
122
- }
123
- }
124
- else if (layer[i].geometry.type === 'Point') {
125
- if (phase === 'texts' && renderer[j].text) {
126
- var text = layer[i].properties['name'] ? layer[i].properties['name'] : layer[i].properties['TEXT'];
127
-
128
- if (text) {
129
- // if there is a text and an image to render, first check for collisions and then render both of them or none
130
-
131
- if (!this.isTextCollision(layer[i].geometry.coordinates, text, boundaries, renderer[j].text, context, collisions)) {
132
- if (text.match(/([A-Z])\s?([0-9]+[a-z]*).*/)) {
133
- // text is like A3, B9, B55a or L134 -> draw Autobahn and Bundesstrasse, ignore otherwise
134
- if ('A' === RegExp.$1 || 'B' === RegExp.$1) {
135
- // Simplify B55 to 55, B55a to 55a
136
- text = RegExp.$2;
137
-
138
- this.drawImage(layer[i].geometry.coordinates, boundaries, renderer[j].image, context);
139
- this.drawText(layer[i].geometry.coordinates, text, boundaries, renderer[j].text, context, collisions);
140
- }
141
- }
142
- else {
143
- this.drawText(layer[i].geometry.coordinates, text, boundaries, renderer[j].text, context, collisions);
144
- }
145
- }
146
- }
147
- else {
148
- this.drawImage(layer[i].geometry.coordinates, boundaries, renderer[j].image, context);
149
- }
150
- }
151
- }
152
- }
153
- }
154
- }
155
- }
156
- /*else
157
- {
158
- if (layer[i].geometry.type && layer[i].geometry.type.indexOf('Polygon') >= 0)
159
- {
160
- console.log('No config found for kind', kind, 'of type', layer[i].geometry.type);
161
- }
162
- }*/
163
- }
164
- }
165
-
166
- /**
167
- * @private
168
- */
169
- getBackgroundColor(z) {
170
- if (this._styles.background) {
171
- for (var i = 0; i < this._styles.background.length; i++) {
172
- if (this._styles.background[i].from <= z && this._styles.background[i].to >= z) {
173
- return this._styles.background[i].color;
174
- }
175
- }
176
- }
177
-
178
- return '#ffffff';
179
- }
180
-
181
- /**
182
- * @private
183
- */
184
- accept(style, layer) {
185
- if (style.filters) {
186
- for (var i = 0; i < style.filters.length; i++) {
187
- var filter = style.filters[i];
188
-
189
- if (filter.type == 'minimum') {
190
- if (layer.properties[filter.key]) {
191
- let value = layer.properties[filter.key];
192
- if (value < filter.value) {
193
- return false;
194
- }
195
- }
196
- }
197
- else if (filter.type == 'equals not') {
198
- if (layer.properties[filter.key]) {
199
- let value = layer.properties[filter.key];
200
- if (value == filter.value) {
201
- return false;
202
- }
203
- }
204
- }
205
- }
206
- }
207
-
208
- return true;
209
- }
210
-
211
- /**
212
- * @private
213
- */
214
- drawPolygon(coordinates, properties, z, boundaries, style, context, phase, collisions, texts) {
215
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.POLYGON, ...style };
216
-
217
- for (var j = 0; j < coordinates.length; j++) {
218
- var part = coordinates[j];
219
-
220
- context.globalAlpha = merged.opacity;
221
- context.beginPath();
222
-
223
- var points = [];
224
-
225
- for (let k = 0; k < part.length; k++) {
226
- let x = 256 * (part[k][0] - boundaries.min.lon) / (boundaries.max.lon - boundaries.min.lon);
227
- let y = 256 * (part[k][1] - boundaries.min.lat) / (boundaries.max.lat - boundaries.min.lat);
228
-
229
- points.push({ x: x, y: y });
230
-
231
- if (k == 0) {
232
- context.moveTo(x, y);
233
- }
234
- else {
235
- context.lineTo(x, y);
236
- }
237
- }
238
-
239
- context.closePath();
240
-
241
- // doesn't work (see comment above)
242
- if ('areasOutline' === phase) {
243
- if (merged.outlineColor && merged.outlineWidth) {
244
- context.strokeStyle = merged.outlineColor;
245
- context.lineWidth = merged.outlineWidth;
246
- context.stroke();
247
- }
248
-
249
- return;
250
- }
251
- else if ('areas' === phase) {
252
- if (merged.url) {
253
- if (this._images[merged.url]) {
254
- var pattern = context.createPattern(this._images[merged.url], 'repeat');
255
- context.fillStyle = pattern;
256
- }
257
- }
258
- else {
259
- if (z <= 7) // draw an outline to prevent raster lines (see INWA-997)
260
- {
261
- context.strokeStyle = merged.color;
262
- context.lineWidth = 1;
263
- context.stroke();
264
- }
265
-
266
- context.fillStyle = merged.color;
267
- }
268
-
269
- context.fill();
270
- }
271
- else if ('areaTexts' === phase) {
272
- if (properties && properties.NAME) {
273
- // skip texts which already are in this tile
274
- if (texts[properties.NAME]) {
275
- return;
276
- }
277
-
278
- merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...style };
279
-
280
- context.globalAlpha = merged.opacity;
281
- context.font = merged.fontStyle + ' ' + merged.fontWeight + ' ' + merged.size + 'px ' + merged.font;
282
- context.textAlign = 'left';
283
- context.textBaseline = 'top';
284
-
285
- var metrics = context.measureText(properties.NAME);
286
-
287
- var centroid = this.getCentroid(points);
288
- let x = centroid.x - 0.5 * metrics.width;
289
- let y = centroid.y;
290
-
291
- let w = metrics.width;
292
- let h = merged.size;
293
-
294
- // skip texts outside a tile
295
- if (x < 0 || x + w > 256 || y < 0 || y + h > 256) {
296
- return;
297
- }
298
-
299
- // skip texts colliding with other texts
300
- if (collisions.isCollision(x, y, w, h)) {
301
- return;
302
- }
303
-
304
- if (merged.outlineColor && merged.outlineWidth) {
305
- context.lineWidth = merged.outlineWidth;
306
- context.strokeStyle = merged.outlineColor;
307
- context.strokeText(properties.NAME, x, y);
308
- }
309
-
310
- context.fillStyle = merged.color;
311
- context.fillText(properties.NAME, x, y);
312
-
313
- collisions.insert(x, y, w, h);
314
-
315
- texts[properties.NAME] = true;
316
- }
317
- }
318
- }
319
- }
320
-
321
- getCentroid(vertices) {
322
- var centroid = { x: 0, y: 0 };
323
- var signedArea = 0.0;
324
- var x0 = 0.0; // Current vertex X
325
- var y0 = 0.0; // Current vertex Y
326
- var x1 = 0.0; // Next vertex X
327
- var y1 = 0.0; // Next vertex Y
328
- var a = 0.0; // Partial signed area
329
-
330
- // For all vertices except last
331
- for (var i = 0; i < vertices.length - 1; i++) {
332
- x0 = vertices[i].x;
333
- y0 = vertices[i].y;
334
- x1 = vertices[i + 1].x;
335
- y1 = vertices[i + 1].y;
336
- a = x0 * y1 - x1 * y0;
337
- signedArea += a;
338
- centroid.x += (x0 + x1) * a;
339
- centroid.y += (y0 + y1) * a;
340
- }
341
-
342
- // Do last vertex separately to avoid performing an expensive
343
- // modulus operation in each iteration.
344
- x0 = vertices[i].x;
345
- y0 = vertices[i].y;
346
- x1 = vertices[0].x;
347
- y1 = vertices[0].y;
348
- a = x0 * y1 - x1 * y0;
349
- signedArea += a;
350
- centroid.x += (x0 + x1) * a;
351
- centroid.y += (y0 + y1) * a;
352
-
353
- signedArea *= 0.5;
354
- centroid.x /= (6.0 * signedArea);
355
- centroid.y /= (6.0 * signedArea);
356
-
357
- return centroid;
358
- }
359
-
360
- getBBox(coordinates) {
361
- var bbox = {
362
- min: {
363
- x: coordinates[0].x,
364
- y: coordinates[0].y
365
- },
366
- max: {
367
- x: coordinates[0].x,
368
- y: coordinates[0].y
369
- }
370
- };
371
-
372
- for (var i = 1; i < coordinates.length; i++) {
373
- bbox.min.x = Math.min(bbox.min.x, coordinates[i].x);
374
- bbox.min.y = Math.min(bbox.min.y, coordinates[i].y);
375
- bbox.max.x = Math.max(bbox.max.x, coordinates[i].x);
376
- bbox.max.y = Math.max(bbox.max.y, coordinates[i].y);
377
- }
378
-
379
- return bbox;
380
- }
381
-
382
- /**
383
- * @private
384
- */
385
- projectCoordinates(coordinates, boundaries) {
386
- var lastX, lastY;
387
- var points = [];
388
-
389
- for (var j = 0; j < coordinates.length; j++) {
390
- var x = Math.round(256 * (coordinates[j][0] - boundaries.min.lon) / (boundaries.max.lon - boundaries.min.lon));
391
- var y = Math.round(256 * (coordinates[j][1] - boundaries.min.lat) / (boundaries.max.lat - boundaries.min.lat));
392
-
393
- if (x != lastX || y != lastY) {
394
- points.push({ x: x, y: y });
395
- }
396
-
397
- lastX = x;
398
- lastY = y;
399
- }
400
-
401
- return points;
402
- }
403
-
404
- /**
405
- * @private
406
- */
407
- drawPolyline(coordinates, properties, boundaries, style, context, phase) {
408
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.LINE, ...style };
409
- context.globalAlpha = merged.opacity;
410
-
411
- if ('linesOutline' === phase) {
412
- if (merged.outlineColor && merged.outlineWidth) {
413
- let points = this.projectCoordinates(coordinates, boundaries);
414
- this.drawPath(points, merged.outlineColor, merged.width + merged.outlineWidth * 2, context);
415
- }
416
-
417
- return;
418
- }
419
-
420
- let points = this.projectCoordinates(coordinates, boundaries);
421
- this.drawPath(points, merged.color, merged.width, context, merged.url);
422
-
423
- if (merged.dashColor && merged.dashLength) {
424
- context.setLineDash([merged.dashLength, merged.dashLength]);
425
- this.drawPath(points, merged.dashColor, merged.width, context);
426
- context.setLineDash([]);
427
- }
428
-
429
- // draw images on the line (e.g. arrows for one-way roads)
430
- if (merged.url && this._images[merged.url]) {
431
- var desired = 40; // XXX config
432
- var distance = Math.round(desired / 2);
433
-
434
- for (var i = 0; i < points.length - 1; i++) {
435
- // calculate angle of this section of the line
436
- var dx = points[i + 1].x - points[i].x;
437
- var dy = points[i + 1].y - points[i].y;
438
- var angle = Math.atan2(dy, dx);
439
-
440
- if (merged.direction != 'reverse') {
441
- angle = angle + Math.PI;
442
- }
443
-
444
- // now calculate equidistant points on the polyline
445
- var distanceToNext = Math.sqrt(dx * dx + dy * dy);
446
- while (distance < distanceToNext) {
447
- // there are more points on this section of the line
448
- var f = distance / distanceToNext;
449
-
450
- var x = points[i].x + f * dx;
451
- var y = points[i].y + f * dy;
452
-
453
- context.save();
454
-
455
- context.translate(x, y);
456
- context.rotate(angle);
457
- context.translate(-x, -y);
458
-
459
- context.drawImage(this._images[merged.url], x + merged.dx, y + merged.dy, merged.width, merged.height);
460
-
461
- context.restore();
462
-
463
- distance += desired;
464
- }
465
-
466
- distance = (desired - (distanceToNext - distance)) % desired;
467
- }
468
- }
469
- }
470
-
471
- drawPolylineText(coordinates, properties, boundaries, style, context, collisions, texts) {
472
- if (!properties || !properties.NAME || coordinates.length < 2) {
473
- return;
474
- }
475
-
476
- var text = properties.NAME;
477
-
478
- if (texts[text]) {
479
- // there already is a label for this street on this tile
480
- return;
481
- }
482
-
483
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...style };
484
-
485
- context.globalAlpha = merged.opacity;
486
- context.font = merged.fontStyle + ' ' + merged.fontWeight + ' ' + merged.size + 'px ' + merged.font;
487
-
488
- var points = this.projectCoordinates(coordinates, boundaries);
489
- if (points.length < 2) {
490
- return;
491
- }
492
-
493
- var last = points[points.length - 1];
494
- var first = points[0];
495
-
496
- var dx = last.x - first.x;
497
- if (dx < 0) // if road segment goes to the left, the text should be flipped (to prevent the text being upside down)
498
- {
499
- points.reverse();
500
- }
501
-
502
- var positions = this.getLetterPositions(points, text, context, collisions);
503
- if (positions) {
504
- context.fillStyle = merged.color;
505
- context.textAlign = 'center';
506
- context.textBaseline = 'middle';
507
-
508
- if (merged.outlineColor && merged.outlineWidth) {
509
- context.lineWidth = merged.outlineWidth;
510
- context.strokeStyle = merged.outlineColor;
511
-
512
- for (let i = 0; i < text.length; i++) {
513
- this.drawLetter(text.charAt(i), positions[i], merged.outlineWidth, context);
514
- }
515
- }
516
-
517
- var avgLetterWidth = context.measureText('a').width;
518
-
519
- for (let i = 0; i < text.length; i++) {
520
- this.drawLetter(text.charAt(i), positions[i], 0, context);
521
- collisions.insertPolylineText(context, text.charAt(i), positions[i], avgLetterWidth);
522
- }
523
-
524
- texts[text] = true;
525
- }
526
- }
527
-
528
- getPolylineLength(points) {
529
- var length = 0;
530
-
531
- for (var i = 1; i < points.length; i++) {
532
- var current = points[i];
533
- var last = points[i - 1];
534
- var dx = last.x - current.x;
535
- var dy = last.y - current.y;
536
-
537
- length += Math.sqrt(dx * dx + dy * dy);
538
- }
539
-
540
- return length;
541
- }
542
-
543
- getAngleAndCoordsAtLength(points, dist, width) {
544
- var x, y, len = 0;
545
-
546
- var gotxy = false;
547
-
548
- for (var i = 1; i < points.length; i++) {
549
- var current = points[i];
550
- var last = points[i - 1];
551
-
552
- var dx = current.x - last.x;
553
- var dy = current.y - last.y;
554
-
555
- var segLen = Math.sqrt(dx * dx + dy * dy);
556
-
557
- if (!gotxy && len + segLen >= dist) {
558
- let partLen = dist - len;
559
- x = last.x + dx * partLen / segLen;
560
- y = last.y + dy * partLen / segLen;
561
-
562
- gotxy = true;
563
- }
564
-
565
- if (gotxy && len + segLen >= dist + width) {
566
- let partLen = dist + width - len;
567
- dx = last.x + dx * partLen / segLen;
568
- dy = last.y + dy * partLen / segLen;
569
- var angle = Math.atan2(dy - y, dx - x);
570
-
571
- return {
572
- angle: angle,
573
- x: x,
574
- y: y
575
- };
576
- }
577
-
578
- len += segLen;
579
- }
580
- }
581
-
582
- getLetterPositions(points, text, context, collisions) {
583
- var pathlength = this.getPolylineLength(points);
584
- var widthUsed = 0;
585
-
586
- do {
587
- var positions = [];
588
-
589
- for (let i = 0; i < text.length; i++) {
590
- var letter = text.charAt(i);
591
- var letterWidth = context.measureText(letter).width;
592
-
593
- var position = this.getAngleAndCoordsAtLength(points, widthUsed, letterWidth);
594
-
595
- if (widthUsed < pathlength && !!position) {
596
- var collision = collisions.isPolylineTextCollision(context, letter, position);
597
- var deltaAngle = positions.length == 0 ? 0 : Math.abs(positions[positions.length - 1].angle - position.angle);
598
-
599
- if (!collision && deltaAngle < Math.PI / 6) {
600
- widthUsed += letterWidth;
601
- positions.push(position);
602
- }
603
- else {
604
- // there is a collision, so skip 2 letters and try again to place them
605
- widthUsed += 2 * letterWidth;
606
- break;
607
- }
608
- }
609
- else {
610
- widthUsed += 2 * letterWidth;
611
- break;
612
- }
613
- }
614
-
615
- if (positions.length == text.length) // if all letters have been projected
616
- {
617
- // count the number of letters which are upside down
618
- var halfPi = Math.PI / 2;
619
-
620
- var count = 0;
621
- for (let i = 0; i < positions.length; i++) {
622
- if (Math.abs(positions[i].angle) > halfPi) {
623
- count++;
624
- }
625
- }
626
-
627
- if (count < text.length / 2) {
628
- // most of the text is upside down, so discard it
629
- return positions;
630
- }
631
- }
632
- }
633
- while (widthUsed < pathlength); // try as long as there is space left
634
-
635
- return null;
636
- }
637
-
638
- /**
639
- * @private
640
- */
641
- drawLetter(letter, position, outline, context) {
642
- var width = context.measureText(letter).width + outline; // XXX
643
-
644
- var x = position.x + 0.5 * Math.cos(position.angle) * width;
645
- var y = position.y + 0.5 * Math.sin(position.angle) * width;
646
-
647
- context.translate(x, y);
648
- context.rotate(position.angle);
649
-
650
- if (outline) {
651
- context.strokeText(letter, 0, 0);
652
- }
653
- else {
654
- context.fillText(letter, 0, 0);
655
- }
656
-
657
- context.rotate(-position.angle);
658
- context.translate(-x, -y);
659
- }
660
-
661
- /**
662
- * @private
663
- */
664
- drawPath(points, color, width, context) {
665
- context.beginPath();
666
-
667
- context.strokeStyle = color;
668
- context.lineWidth = width;
669
-
670
- context.lineCap = 'square';
671
- context.lineJoin = 'miter';
672
-
673
- context.moveTo(points[0].x, points[0].y);
674
- for (var j = 1; j < points.length; j++) {
675
- context.lineTo(points[j].x, points[j].y);
676
- }
677
- context.stroke();
678
- }
679
-
680
- drawImage(coordinate, boundaries, style, context) {
681
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.IMAGE, ...style };
682
-
683
- if (merged.url) {
684
- if (this._images[merged.url]) {
685
- var x = Math.round(256 * (coordinate[0] - boundaries.min.lon) / (boundaries.max.lon - boundaries.min.lon));
686
- var y = Math.round(256 * (coordinate[1] - boundaries.min.lat) / (boundaries.max.lat - boundaries.min.lat));
687
-
688
- context.drawImage(this._images[merged.url], x + merged.dx, y + merged.dy, merged.width, merged.height);
689
- }
690
- }
691
- }
692
-
693
- /**
694
- * @private
695
- */
696
- isTextCollision(coordinate, name, boundaries, style, context, collisions) {
697
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...style };
698
-
699
- var x = Math.round(256 * (coordinate[0] - boundaries.min.lon) / (boundaries.max.lon - boundaries.min.lon));
700
- var y = Math.round(256 * (coordinate[1] - boundaries.min.lat) / (boundaries.max.lat - boundaries.min.lat));
701
-
702
- context.font = merged.fontStyle + ' ' + merged.fontWeight + ' ' + merged.size + 'px ' + merged.font;
703
-
704
- var w = context.measureText(name).width;
705
- var h = merged.size; // XXX ???
706
-
707
- x = x - w / 2; // center on x
708
-
709
- return collisions.isCollision(x, y, w, h);
710
- }
711
-
712
- /**
713
- * @private
714
- */
715
- drawText(coordinate, name, boundaries, style, context, collisions) {
716
- var merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...style };
717
-
718
- var x = Math.round(256 * (coordinate[0] - boundaries.min.lon) / (boundaries.max.lon - boundaries.min.lon));
719
- var y = Math.round(256 * (coordinate[1] - boundaries.min.lat) / (boundaries.max.lat - boundaries.min.lat));
720
-
721
- context.globalAlpha = merged.opacity;
722
- context.font = merged.fontStyle + ' ' + merged.fontWeight + ' ' + merged.size + 'px ' + merged.font;
723
-
724
- var w = context.measureText(name).width;
725
- var h = merged.size; // XXX ???
726
-
727
- x = x - w / 2; // center on x
728
-
729
- if (collisions.isCollision(x, y, w, h)) {
730
- return;
731
- }
732
-
733
- if (merged.outlineColor && merged.outlineWidth) {
734
- context.lineWidth = merged.outlineWidth;
735
- context.strokeStyle = merged.outlineColor;
736
- context.strokeText(name, x, y);
737
- }
738
-
739
- context.fillStyle = merged.color;
740
- context.fillText(name, x, y);
741
-
742
- collisions.insert(x, y, w, h);
743
- }
744
-
745
- preloadImages() {
746
- for (var name in this._styles.renderers) {
747
- for (var i = 0; i < this._styles.renderers[name].length; i++) {
748
- for (var renderer in this._styles.renderers[name][i]) {
749
- var config = this._styles.renderers[name][i][renderer];
750
-
751
- if (config.url) {
752
- this._images[config.url] = new Image();
753
- this._images[config.url].src = config.url; // TODO wait
754
-
755
- this._images[config.url].onerror = function(url) {
756
- return function() {
757
- delete this._images[url];
758
- }.bind(this);
759
-
760
- }.bind(this)(config.url);
761
- }
762
- }
763
- }
764
- }
765
- }
766
-
767
- findConfiguredFonts() {
768
- let result = [];
769
-
770
- for (let renderer in this._styles.renderers) {
771
- for (let i = 0; i < this._styles.renderers[renderer].length; i++) {
772
- let text = this._styles.renderers[renderer][i].text;
773
- if (text) {
774
- let merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...text };
775
- let font = (merged.fontStyle + ' ' + merged.fontWeight + ' ' + merged.size + 'px ' + merged.font).trim();
776
- result.push(font);
777
- }
778
- }
779
- }
780
-
781
- return [...new Set(result)];
782
- }
783
-
784
- static DEFAULT_STYLE = {
785
- POLYGON: {
786
- color: '#000000',
787
- url: '',
788
- outlineColor: '',
789
- outlineWidth: 0,
790
- opacity: 1.0
791
- },
792
- LINE: {
793
- color: '#000000',
794
- url: '',
795
- height: 6,
796
- dx: -5,
797
- dy: -3,
798
- direction: 'forward',
799
- width: 1,
800
- outlineColor: '',
801
- outlineWidth: 0,
802
- dashColor: '',
803
- dashLength: 0,
804
- opacity: 1.0
805
- },
806
- IMAGE: {
807
- url: '',
808
- width: 33,
809
- height: 20,
810
- dx: 10,
811
- dy: 10
812
- },
813
- TEXT: {
814
- color: 'black',
815
- outlineColor: '',
816
- outlineWidth: 0,
817
- font: 'sans-serif',
818
- fontWeight: '',
819
- fontStyle: '',
820
- size: 12,
821
- opacity: 1.0
822
- }
823
- };
824
- }
825
-
826
- export { VectorTileRenderer };