@maptrip/map-tiles 1.4.0 → 1.6.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.
@@ -5,7 +5,8 @@ export declare class MapReleaseFetcher {
5
5
  private _osm;
6
6
  private _here;
7
7
  constructor(token: string);
8
- getRelease(provider: string): Promise<string | null | undefined>;
9
- fetchAndExtractRelease(provider: string, url: string): Promise<string | null>;
8
+ getRelease(provider?: string): Promise<string | null>;
9
+ private getUrl;
10
+ fetchAndExtractRelease(provider: string): Promise<string | null>;
10
11
  private extractTargetString;
11
12
  }
@@ -0,0 +1,4 @@
1
+ declare class VectorTileUtils {
2
+ static getAttribution(token: string, provider?: string, attribution?: string, releaseDate?: boolean): Promise<string>;
3
+ }
4
+ export { VectorTileUtils };
@@ -1,20 +1,19 @@
1
1
  import L from "leaflet";
2
2
  import { VectorTileFactory } from "../../core/VectorTileFactory.js";
3
- import { MIN_ZOOM, MAX_ZOOM, ATTRIBUTION } from "../../core/constants.js";
4
- import { MapReleaseFetcher } from "../../util/MapReleaseFetcher.js";
3
+ import { VectorTileUtils } from "../../util/VectorTileUtils.js"
5
4
  import type { PointXY } from "../../types/interfaces.js";
5
+ import { MIN_ZOOM, MAX_ZOOM } from "../../core/constants.js";
6
6
  import { RENDERING_CONFIG } from "../../types/interfaces.js";
7
7
 
8
8
  class MapTripLeafletGridLayer extends L.GridLayer {
9
- MIN_ZOOM = MIN_ZOOM;
10
- MAX_ZOOM = MAX_ZOOM;
9
+ static attributions: string[] = [];
11
10
  token: string;
12
11
  factory: VectorTileFactory;
13
- releaseFetcher: MapReleaseFetcher;
14
- provider: string;
15
- attribution: string;
16
- release: string;
17
- releaseDate: boolean;
12
+ provider?: string;
13
+ releaseDate?: boolean;
14
+ currentAttribution?: string;
15
+ private attachedMap?: L.Map;
16
+ private customAttribution?: string;
18
17
 
19
18
  constructor(options: {
20
19
  provider?: string;
@@ -30,110 +29,64 @@ class MapTripLeafletGridLayer extends L.GridLayer {
30
29
  maxZoom?: number;
31
30
  releaseDate?: boolean;
32
31
  }) {
33
- super(options as L.GridLayerOptions);
34
- this.token = options.token!;
35
- this.provider = options.provider
36
- ? options.provider?.toLowerCase()
37
- : "openstreetmap";
38
-
39
- this.options.attribution = options.attribution
40
- ? options.attribution
41
- : ATTRIBUTION;
42
- this.releaseFetcher = new MapReleaseFetcher(this.token);
43
- this.release = "";
44
- this.attribution = "";
45
-
46
- this.releaseDate = options.releaseDate ? options.releaseDate : false;
47
- if (this.releaseDate) {
48
- this.initializeAttribution();
49
- this.on("add", () => {
50
- if (this._map) {
51
- this._map.on("baselayerchange", (event) =>
52
- this.onBaseLayerChange(event)
53
- );
54
- }
55
- });
56
- }
32
+ super({
33
+ ...options,
34
+ attribution: '',
35
+ minZoom: Math.max(options.minZoom ? options.minZoom : MIN_ZOOM, MIN_ZOOM),
36
+ maxZoom: Math.min(options.maxZoom ? options.maxZoom : MAX_ZOOM, MAX_ZOOM)
37
+ });
57
38
 
58
- //@ts-expect-error picking up LayerOptions rather than GridLayerOptions
59
- this.options.minZoom = options.minZoom
60
- ? Math.max(this.MIN_ZOOM, options.minZoom)
61
- : this.MIN_ZOOM;
62
- //@ts-expect-error
63
- this.options.maxZoom = options.maxZoom
64
- ? Math.min(this.MAX_ZOOM, options.maxZoom)
65
- : this.MAX_ZOOM;
39
+ this.token = options.token!;
40
+ this.provider = options.provider;
41
+ this.customAttribution = options.attribution;
42
+ this.releaseDate = options.releaseDate;
43
+ this.currentAttribution = undefined;
44
+
45
+ void VectorTileUtils.getAttribution(this.token, this.provider, this.customAttribution, this.releaseDate)
46
+ .then(attribution => {
47
+ this.updateAttribution(attribution);
48
+ }).catch(error => {
49
+ console.error('Unable to add MapTrip attribution', error);
50
+ });
66
51
 
67
52
  this.factory = VectorTileFactory.createFactory(options);
68
53
  }
69
54
 
70
- createTile(coords: PointXY) {
71
- return this.factory.getJsonTile(coords.z!, coords.x, coords.y, this.token);
55
+ override onAdd(map: L.Map): this {
56
+ this.attachedMap = map;
57
+ return super.onAdd(map);
72
58
  }
73
59
 
74
- private async initializeAttribution() {
75
- await this.setRelease();
76
- await this.setAttribution();
60
+ override onRemove(map: L.Map): this {
61
+ const result = super.onRemove(map);
62
+ this.attachedMap = undefined;
63
+ return result;
77
64
  }
78
65
 
79
- // Set an original release
80
- private async setRelease() {
81
- const release = await this.releaseFetcher.getRelease(this.provider);
82
- if (release) {
83
- this.release = release;
84
- }
66
+ createTile(coords: PointXY) {
67
+ return this.factory.getJsonTile(coords.z!, coords.x, coords.y, this.token);
85
68
  }
86
69
 
87
- private async setAttribution() {
88
- if (!this._map) return;
89
- try {
90
- // check if base layer changes to a new provider
91
- const newRelease = await this.releaseFetcher.getRelease(this.provider);
92
- if (newRelease && newRelease !== this.release) {
93
- this.release = newRelease;
94
- }
70
+ private updateAttribution(attribution: string): void {
71
+ const previous = this.options.attribution ?? '';
95
72
 
96
- // remove the original attribution in order to append the release date
97
- this._map.attributionControl.removeAttribution(this.options.attribution!);
98
- this.attribution = `${this.options.attribution} ${this.release}`;
99
- this._map.attributionControl.addAttribution(this.attribution!);
100
-
101
- // remove the attribution with release date from map for instances where different layers have different preferences for setting release date
102
- const map = this._map;
103
- this._map.addEventListener("baselayerchange", () => {
104
- map.attributionControl.removeAttribution(this.attribution);
105
- });
106
- } catch (error) {
107
- console.error("Error fetching release version: ", error);
108
- }
109
- }
110
-
111
- private async onBaseLayerChange(event: L.LayersControlEvent) {
112
- if (!this._map) {
73
+ if (previous === attribution) {
113
74
  return;
114
75
  }
115
76
 
116
- this._map.attributionControl.removeAttribution(this.attribution);
77
+ const map = this.attachedMap;
78
+ const control = map?.attributionControl;
117
79
 
118
- //@ts-expect-error doesn't recognize provider in options object
119
- const eventProvider = event.layer.options.provider;
120
- let newProvider: string;
121
- if (eventProvider) {
122
- if (eventProvider.toLowerCase() === "openstreetmap") {
123
- newProvider = "openstreetmap";
124
- } else if (eventProvider.toLowerCase() === "tomtom") {
125
- newProvider = "tomtom";
126
- } else if (eventProvider.toLowerCase() === "orbis") {
127
- newProvider = "orbis";
128
- } else if (eventProvider.toLowerCase() === "here") {
129
- newProvider = "here";
130
- } else {
131
- newProvider = "openstreetmap";
132
- }
80
+ // remove existing attribution
81
+ if (control && map.hasLayer(this)) {
82
+ control.removeAttribution(previous);
83
+ }
133
84
 
134
- this.provider = newProvider;
85
+ // set attribution and update control
86
+ this.options.attribution = attribution;
135
87
 
136
- await this.setAttribution();
88
+ if (control && map.hasLayer(this)) {
89
+ control.addAttribution(attribution);
137
90
  }
138
91
  }
139
92
  }
@@ -0,0 +1,114 @@
1
+ import { addProtocol, type StyleSpecification } from 'maplibre-gl';
2
+
3
+ import { VectorTileFactory } from "../../core/VectorTileFactory.js";
4
+ import { VectorTileUtils } from "../../util/VectorTileUtils.js"
5
+ import { MIN_ZOOM, MAX_ZOOM } from "../../core/constants.js";
6
+ import { RENDERING_CONFIG } from "../../types/interfaces.js";
7
+
8
+ class MapLibreStyleFactory {
9
+
10
+ private protocolId = 0;
11
+
12
+ public createMapTripStyle(options: {
13
+ provider?: string;
14
+ baseMap?: boolean;
15
+ traffic?: boolean;
16
+ timeout?: number;
17
+ parallelDownloads?: number;
18
+ detourProfile?: string;
19
+ renderingConfig?: RENDERING_CONFIG;
20
+ token?: string;
21
+ attribution?: string;
22
+ minZoom?: number;
23
+ maxZoom?: number;
24
+ releaseDate?: boolean;
25
+ }): StyleSpecification {
26
+
27
+ const token = options.token!;
28
+ const factory = VectorTileFactory.createFactory(options);
29
+ const protocol = `maptrip${++this.protocolId}`;
30
+ let attributionPromise: Promise<string> | undefined;
31
+
32
+ addProtocol(protocol, async (params) => {
33
+
34
+ if (params.url === `${protocol}://metadata`) {
35
+
36
+ const attribution = await (
37
+ attributionPromise ??= VectorTileUtils.getAttribution(token, options.provider, options.attribution, options.releaseDate)
38
+ );
39
+
40
+ return {
41
+ data: {
42
+ tilejson: '2.2.0',
43
+ tileSize: 256,
44
+ tiles: [`${protocol}://{z}/{x}/{y}`],
45
+ minzoom: Math.max(options.minZoom ? options.minZoom : MIN_ZOOM, MIN_ZOOM),
46
+ maxzoom: Math.min(options.maxZoom ? options.maxZoom : MAX_ZOOM, MAX_ZOOM),
47
+ attribution: attribution
48
+ }
49
+ };
50
+ }
51
+
52
+ const match = params.url.match(
53
+ /^maptrip\d+:\/\/(\d+)\/(\d+)\/(\d+)$/
54
+ );
55
+
56
+ if (!match) {
57
+ throw new Error(`Invalid tile URL: ${params.url}`);
58
+ }
59
+
60
+ const z = Number(match[1]);
61
+ const x = Number(match[2]);
62
+ const y = Number(match[3]);
63
+
64
+ const canvas = await this.getTileCanvasAsync(factory, z, x, y, token);
65
+
66
+ return {
67
+ data: await createImageBitmap(canvas)
68
+ };
69
+ });
70
+
71
+ return {
72
+ version: 8,
73
+ name: "MapTrip Map",
74
+ sources: {
75
+ maptrip: {
76
+ type: 'raster',
77
+ url: `${protocol}://metadata`,
78
+ }
79
+ },
80
+ layers: [
81
+ {
82
+ id: 'maptrip-layer',
83
+ type: 'raster',
84
+ source: 'maptrip'
85
+ }
86
+ ],
87
+ }
88
+ };
89
+
90
+ private getTileCanvasAsync(
91
+ factory: VectorTileFactory,
92
+ z: number,
93
+ x: number,
94
+ y: number,
95
+ token: string
96
+ ): Promise<HTMLCanvasElement> {
97
+ return new Promise((resolve, reject) => {
98
+
99
+ try {
100
+ const canvas = factory.getJsonTile(z, x, y, token, () => {
101
+ resolve(canvas);
102
+ });
103
+
104
+ if (!canvas) {
105
+ reject(new Error('getJsonTile returned no canvas'));
106
+ }
107
+ } catch (err) {
108
+ reject(err);
109
+ }
110
+ });
111
+ }
112
+ }
113
+
114
+ export { MapLibreStyleFactory };
@@ -7,7 +7,8 @@ import {
7
7
  PROVIDER_PROJECTS,
8
8
  PROVIDER_TRAFFIC,
9
9
  VECTOR_TILE_SIZE,
10
- VECTOR_TILE_RENDER_PADDING
10
+ VECTOR_TILE_RENDER_PADDING,
11
+ DEFAULT_PROVIDER
11
12
  } from "./constants.js";
12
13
 
13
14
  import WebFont from "webfontloader";
@@ -367,7 +368,7 @@ class VectorTileFactory {
367
368
  }
368
369
 
369
370
  static DEFAULT_PROPERTIES = {
370
- provider: "OpenStreetmap",
371
+ provider: DEFAULT_PROVIDER,
371
372
  baseMap: true,
372
373
  traffic: false,
373
374
  };
@@ -43,6 +43,10 @@ class VectorTileRenderer {
43
43
  boundaries: Boundaries,
44
44
  context: CanvasRenderingContext2D
45
45
  ): void {
46
+
47
+ context.lineJoin = 'round';
48
+ context.lineCap = 'round';
49
+
46
50
  context.fillStyle = this.getBackgroundColor(z);
47
51
  context.fillRect(0, 0, VECTOR_TILE_SIZE, VECTOR_TILE_SIZE);
48
52
 
@@ -79,6 +83,7 @@ class VectorTileRenderer {
79
83
  );
80
84
 
81
85
  this.drawTilePhase(polygons, z, boundaries, context, "areas", collisions);
86
+ // add phase areasOutline here if polygon outlines should be rendered
82
87
  this.drawPolylineLayers(lines, z, boundaries, context, "", collisions);
83
88
  this.drawTilePhase(points, z, boundaries, context, "texts", collisions);
84
89
  this.drawTilePhase(lines, z, boundaries, context, "lineTexts", collisions);
@@ -119,6 +124,14 @@ class VectorTileRenderer {
119
124
  "linesOutline",
120
125
  collisions
121
126
  );
127
+ }
128
+
129
+ for (let i = 0; i < rank.length; i++) {
130
+ // select all lines with this sort rank
131
+ let currentRank = lines.filter(
132
+ (it) => parseInt(it.properties!.sort_rank) === rank[i]
133
+ );
134
+
122
135
  this.drawTilePhase(
123
136
  currentRank,
124
137
  z,
@@ -306,12 +319,18 @@ class VectorTileRenderer {
306
319
  collisions
307
320
  )
308
321
  ) {
309
- const match = text.match(/([A-Z])\s?([0-9]+[a-z]*).*/);
322
+ const match = text.match(/([a-zA-Z])\s?([0-9]+[a-z]*).*/);
310
323
  if (match) {
311
324
  const letter = match[1];
312
325
  const number = match[2];
313
- if (letter === "A" || letter === "B") {
314
- text = number;
326
+
327
+ // Stadsroutes are major roads in some cities in the Netherlands and start with s.
328
+ // Adding them here will render them like Bundesstraßen in Germany.
329
+ // If country-dependent rendering is added, it should be a white box with black border and text.
330
+
331
+ if (letter === "A" || letter === "B" || letter === "s") {
332
+ text = letter === "A" || letter === "B" ? number : letter + number;
333
+
315
334
  this.drawImage(
316
335
  coordinates,
317
336
  boundaries,
@@ -430,7 +449,6 @@ class VectorTileRenderer {
430
449
 
431
450
  context.globalAlpha = merged.opacity;
432
451
 
433
- /// XXX edit here
434
452
  if ('areas' !== phase || j === 0) {
435
453
  // when rendering areas, only the first part of a polygon should begin a path
436
454
  // (otherwise especially islands are clipped on some zoom levels)
@@ -438,11 +456,11 @@ class VectorTileRenderer {
438
456
  }
439
457
 
440
458
  const width = boundaries.max.lon - boundaries.min.lon;
441
- const height = boundaries.max.lat - boundaries.min.lat;
459
+ const height = boundaries.max.lat - boundaries.min.lat;
442
460
  if (width === 0 || height === 0) {
443
461
  return;
444
462
  }
445
-
463
+
446
464
  var points: { x: number; y: number }[] = [];
447
465
 
448
466
  for (let k = 0; k < part.length; k++) {
@@ -636,6 +654,37 @@ class VectorTileRenderer {
636
654
  return points;
637
655
  }
638
656
 
657
+ /**
658
+ * @private
659
+ */
660
+ translatePolyline(points: { x: number; y: number }[], delta: number): void {
661
+ for (let i = 0; i < points.length - 1; i++) {
662
+ const direction = {
663
+ x: points[i + 1].x - points[i].x,
664
+ y: points[i + 1].y - points[i].y
665
+ };
666
+
667
+ const length = Math.sqrt(Math.pow(direction.x, 2) + Math.pow(direction.y, 2));
668
+
669
+ const normal = {
670
+ x: direction.y / length,
671
+ y: -direction.x / length
672
+ };
673
+
674
+ points[i] = {
675
+ x: points[i].x - delta * normal.x,
676
+ y: points[i].y - delta * normal.y
677
+ };
678
+
679
+ if (i === points.length - 2) {
680
+ points[i + 1] = {
681
+ x: points[i + 1].x - delta * normal.x,
682
+ y: points[i + 1].y - delta * normal.y
683
+ };
684
+ }
685
+ }
686
+ }
687
+
639
688
  /**
640
689
  * @private
641
690
  */
@@ -650,9 +699,15 @@ class VectorTileRenderer {
650
699
  var merged = { ...VectorTileRenderer.DEFAULT_STYLE.LINE, ...style };
651
700
  context.globalAlpha = merged.opacity;
652
701
 
702
+ let points = this.projectCoordinates(coordinates, boundaries);
703
+
704
+ if (merged.delta > 0) {
705
+ // translate traffic flow polylines to the right
706
+ this.translatePolyline(points, merged.delta);
707
+ }
708
+
653
709
  if ("linesOutline" === phase) {
654
710
  if (merged.outlineColor && merged.outlineWidth) {
655
- let points = this.projectCoordinates(coordinates, boundaries);
656
711
  this.drawPath(
657
712
  points,
658
713
  merged.outlineColor,
@@ -664,7 +719,6 @@ class VectorTileRenderer {
664
719
  return;
665
720
  }
666
721
 
667
- let points = this.projectCoordinates(coordinates, boundaries);
668
722
  this.drawPath(points, merged.color, merged.width, context);
669
723
 
670
724
  if (merged.dashColor && merged.dashLength) {
@@ -675,48 +729,69 @@ class VectorTileRenderer {
675
729
 
676
730
  // draw images on the line (e.g. arrows for one-way roads)
677
731
  if (merged.url && this._images[merged.url]) {
678
- var desired = 40; // XXX config
679
- var distance = Math.round(desired / 2);
732
+ const desiredDistance: number = 40;
733
+ const margin: number = Math.round(desiredDistance / 2);
734
+
735
+ let totalLength = 0;
736
+ for (let i = 0; i < points.length - 1; i++) {
737
+ totalLength += Math.hypot(
738
+ points[i + 1].x - points[i].x,
739
+ points[i + 1].y - points[i].y
740
+ );
741
+ }
742
+
743
+ let distance: number = margin;
744
+ let traveled: number = 0;
680
745
 
681
746
  for (var i = 0; i < points.length - 1; i++) {
682
747
  // calculate angle of this section of the line
683
- var dx = points[i + 1].x - points[i].x;
684
- var dy = points[i + 1].y - points[i].y;
685
- var angle = Math.atan2(dy, dx);
748
+ const dx: number = points[i + 1].x - points[i].x;
749
+ const dy: number = points[i + 1].y - points[i].y;
750
+ let angle: number = Math.atan2(dy, dx);
686
751
 
687
752
  if (merged.direction != "reverse") {
688
753
  angle = angle + Math.PI;
689
754
  }
690
755
 
691
756
  // now calculate equidistant points on the polyline
692
- var distanceToNext = Math.sqrt(dx * dx + dy * dy);
757
+ const distanceToNext: number = Math.sqrt(dx * dx + dy * dy);
693
758
  while (distance < distanceToNext) {
694
759
  // there are more points on this section of the line
695
- var f = distance / distanceToNext;
696
760
 
697
- var x = points[i].x + f * dx;
698
- var y = points[i].y + f * dy;
761
+ if (traveled + distance <= totalLength - margin) {
699
762
 
700
- context.save();
763
+ const f: number = distance / distanceToNext;
701
764
 
702
- context.translate(x, y);
703
- context.rotate(angle);
704
- context.translate(-x, -y);
765
+ const x: number = points[i].x + f * dx;
766
+ const y: number = points[i].y + f * dy;
705
767
 
706
- context.drawImage(
707
- this._images[merged.url],
708
- x + merged.dx,
709
- y + merged.dy,
710
- Number(merged.width),
711
- Number(merged.height)
712
- );
768
+ const size: number = Math.max(12, 1.5 * merged.width);
713
769
 
714
- context.restore();
770
+ if (x > size && y > size && x < VECTOR_TILE_SIZE - size && y < VECTOR_TILE_SIZE - size) {
771
+ context.save();
715
772
 
716
- distance += desired;
773
+ context.translate(x, y);
774
+ context.rotate(angle);
775
+ context.translate(-x, -y);
776
+
777
+ context.drawImage(
778
+ this._images[merged.url],
779
+ x - size / 2,
780
+ y - size / 2,
781
+ size,
782
+ size,
783
+ );
784
+
785
+ context.restore();
786
+ }
787
+
788
+ }
789
+ distance += desiredDistance;
717
790
  }
718
791
 
719
- distance = (desired - (distanceToNext - distance)) % desired;
792
+ traveled += distanceToNext;
793
+ distance -= distanceToNext;
794
+ // distance = (desiredDistance - (distanceToNext - distance)) % desiredDistance;
720
795
  }
721
796
  }
722
797
  }
@@ -943,7 +1018,7 @@ class VectorTileRenderer {
943
1018
  outline: number,
944
1019
  context: CanvasRenderingContext2D
945
1020
  ): void {
946
- var width = context.measureText(letter).width + outline; // XXX
1021
+ var width = context.measureText(letter).width + outline;
947
1022
 
948
1023
  var x = position.x + 0.5 * Math.cos(position.angle) * width;
949
1024
  var y = position.y + 0.5 * Math.sin(position.angle) * width;
@@ -974,10 +1049,7 @@ class VectorTileRenderer {
974
1049
 
975
1050
  context.strokeStyle = color;
976
1051
  context.lineWidth = width;
977
-
978
- context.lineCap = "square";
979
- context.lineJoin = "miter";
980
-
1052
+
981
1053
  context.moveTo(points[0].x, points[0].y);
982
1054
  for (var j = 1; j < points.length; j++) {
983
1055
  context.lineTo(points[j].x, points[j].y);
@@ -1131,7 +1203,7 @@ class VectorTileRenderer {
1131
1203
  if (text) {
1132
1204
  let merged = { ...VectorTileRenderer.DEFAULT_STYLE.TEXT, ...text };
1133
1205
  let font = `${merged.fontStyle} ${merged.fontWeight} ${merged.size}px ${merged.font}`.trim();
1134
-
1206
+
1135
1207
  result.push(font);
1136
1208
  }
1137
1209
  }
@@ -1161,6 +1233,7 @@ class VectorTileRenderer {
1161
1233
  dashColor: undefined,
1162
1234
  dashLength: 0,
1163
1235
  opacity: 1.0,
1236
+ delta: 0,
1164
1237
  },
1165
1238
  IMAGE: {
1166
1239
  url: undefined,
@@ -1,5 +1,5 @@
1
1
  export const URL_TEMPLATE =
2
- "https://v${index}.maptrip.de/?tilex=${x}&tiley=${y}&acao=true&context=tomtom${z}&project=${project}&projection=wgs84";
2
+ "https://v${index}.maptrip.de/?tilex=${x}&tiley=${y}&acao=true&context=level${z}&project=${project}&projection=wgs84";
3
3
 
4
4
 
5
5
  export const PROVIDER_PROJECTS: { [key: string]: string } = {
@@ -18,13 +18,16 @@ export const PROVIDER_NAMES: { [key: string]: string } = {
18
18
 
19
19
  export const PROVIDER_TRAFFIC: { [key: string]: string | null } = {
20
20
  tomtom:
21
- "https://v0.maptrip.de/tiles?project=TrafficMobileTomTom_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic${z}&projection=wgs84",
22
- here: "https://v0.maptrip.de/tiles?project=TrafficMobileHere_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic${z}&projection=wgs84",
23
- openstreetmap: null,
21
+ "https://v0.maptrip.de/tiles?project=TrafficMobileTomTom_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic_${z}&projection=wgs84",
22
+ here: "https://v0.maptrip.de/tiles?project=TrafficMobileHere_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic_${z}&projection=wgs84",
23
+ openstreetmap: "https://v0.maptrip.de/tiles?project=TrafficMobileOSM_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic_${z}&projection=wgs84",
24
+ orbis: "https://v0.maptrip.de/tiles?project=TrafficMobileORBIS_EUROPE&tilex=${x}&tiley=${y}&acao=true&context=traffic_${z}&projection=wgs84",
24
25
  };
25
26
 
27
+ export const DEFAULT_PROVIDER = "OpenStreetmap";
28
+
26
29
  export const MIN_ZOOM = 2;
27
- export const MAX_ZOOM = 18;
30
+ export const MAX_ZOOM = 21;
28
31
 
29
32
  export const ATTRIBUTION =
30
33
  '&#169;&nbsp;<a href="https://maptrip.de/" target="_blank">infoware GmbH</a>';