Offline-capable geomap, meant for storing location bookmarks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: continue optimizing map rendering

+204 -222
+5 -3
www/components/m-map.ts
··· 627 627 if (!this.#map) return 628 628 let worldFilename = 'world_z3.pmtiles' 629 629 if (!registeredSources.has('world')) { 630 - const z7 = await getCachedPMTiles('world/world_z7.pmtiles') 631 - const z6 = !z7 ? await getCachedPMTiles('world/world_z6.pmtiles') : null 632 - const z5 = !z6 ? await getCachedPMTiles('world/world_z5.pmtiles') : null 630 + const [z7, z6, z5] = await Promise.all([ 631 + getCachedPMTiles('world/world_z7.pmtiles'), 632 + getCachedPMTiles('world/world_z6.pmtiles'), 633 + getCachedPMTiles('world/world_z5.pmtiles'), 634 + ]) 633 635 634 636 if (z7) worldFilename = 'world/world_z7.pmtiles' 635 637 else if (z6) worldFilename = 'world/world_z6.pmtiles'
+104 -113
www/utils/layers.ts
··· 20 20 'paint': { 'fill-color': 'hsl(47, 13%, 86%)', 'fill-opacity': 0.7 }, 21 21 }, 22 22 { 23 - 'id': `${sourceName}-landcover_grass`, 23 + 'id': `${sourceName}-landcover`, 24 24 'type': 'fill', 25 25 'source': sourceName, 26 26 'source-layer': 'landcover', 27 - 'filter': ['==', 'class', 'grass'], 28 - 'paint': { 'fill-color': 'hsl(82, 46%, 72%)', 'fill-opacity': 0.45 }, 29 - }, 30 - { 31 - 'id': `${sourceName}-landcover_wood`, 32 - 'type': 'fill', 33 - 'source': sourceName, 34 - 'source-layer': 'landcover', 35 - 'filter': ['==', 'class', 'wood'], 27 + 'filter': [ 28 + 'in', 29 + ['get', 'class'], 30 + ['literal', ['grass', 'wood', 'sand']], 31 + ], 36 32 'paint': { 37 - 'fill-color': 'hsl(82, 46%, 72%)', 38 - 'fill-opacity': { 'base': 1, 'stops': [[8, 0.6], [22, 1]] }, 33 + 'fill-color': [ 34 + 'match', 35 + ['get', 'class'], 36 + 'sand', 37 + 'rgba(232, 214, 38, 1)', 38 + 'hsl(82, 46%, 72%)', 39 + ], 40 + 'fill-opacity': [ 41 + 'interpolate', 42 + ['linear'], 43 + ['zoom'], 44 + 8, 45 + ['match', ['get', 'class'], 'grass', 0.45, 'wood', 0.6, 0.3], 46 + 22, 47 + ['match', ['get', 'class'], 'grass', 0.45, 'wood', 1, 0.3], 48 + ], 39 49 }, 40 50 }, 41 51 { ··· 80 90 'paint': { 81 91 'fill-color': 'hsl(47, 22%, 94%)', 82 92 'fill-opacity': { 'base': 1, 'stops': [[0, 1], [8, 0.5]] }, 83 - }, 84 - }, 85 - { 86 - 'id': `${sourceName}-landcover_sand`, 87 - 'type': 'fill', 88 - 'metadata': {}, 89 - 'source': sourceName, 90 - 'source-layer': 'landcover', 91 - 'filter': ['all', ['in', 'class', 'sand']], 92 - 'paint': { 93 - 'fill-antialias': false, 94 - 'fill-color': 'rgba(232, 214, 38, 1)', 95 - 'fill-opacity': 0.3, 96 93 }, 97 94 }, 98 95 { ··· 586 583 'line-dasharray': [2, 1], 587 584 }, 588 585 }, 589 - // Country boundaries — split into two layers matching world_layers.ts so 590 - // there is no visual jump when regional tiles load over the world basemap. 591 - { 592 - 'id': `${sourceName}-admin_country_z0-4`, 593 - 'type': 'line', 594 - 'source': sourceName, 595 - 'source-layer': 'boundary', 596 - 'minzoom': 0, 597 - 'maxzoom': 5, 598 - 'filter': [ 599 - 'all', 600 - ['<=', 'admin_level', 2], 601 - ['==', '$type', 'LineString'], 602 - ['!has', 'claimed_by'], 603 - ], 604 - 'layout': { 605 - 'line-cap': 'round', 606 - 'line-join': 'round', 607 - 'visibility': 'visible', 608 - }, 609 - 'paint': { 610 - 'line-color': 'hsl(0, 0%, 60%)', 611 - 'line-width': { 'base': 1.3, 'stops': [[3, 0.5], [22, 15]] }, 612 - }, 613 - }, 586 + // Country boundaries — world tiles cover z0-5 so regional tiles only 587 + // need to render from z5 onwards. 614 588 { 615 - 'id': `${sourceName}-admin_country_z5-`, 589 + 'id': `${sourceName}-admin_country`, 616 590 'type': 'line', 617 591 'source': sourceName, 618 592 'source-layer': 'boundary', ··· 625 599 'layout': { 626 600 'line-cap': 'round', 627 601 'line-join': 'round', 628 - 'visibility': 'visible', 629 602 }, 630 603 'paint': { 631 604 'line-color': 'hsl(0, 0%, 60%)', ··· 699 672 'text-halo-width': 1.5, 700 673 }, 701 674 }, 702 - // Towns: show from z11, fade out before city labels dominate 675 + // Merged place labels — single collision detection pass for cities, 676 + // towns, and suburbs. Per-class zoom visibility is controlled via 677 + // text-size (0 = hidden) so features only appear at their intended zoom. 703 678 { 704 - 'id': `${sourceName}-place_label_town`, 679 + 'id': `${sourceName}-place_label`, 705 680 'type': 'symbol', 706 681 'source': sourceName, 707 682 'source-layer': 'place', 708 - 'minzoom': 12, 709 - 'maxzoom': 15, 710 - 'filter': ['all', ['==', '$type', 'Point'], ['==', 'class', 'town']], 711 - 'layout': { 712 - 'text-anchor': 'center', 713 - 'text-field': '{name:latin}', 714 - 'text-font': ['Noto Sans Regular'], 715 - 'text-max-width': 8, 716 - 'text-size': { 'stops': [[12, 10], [15, 13]] }, 717 - 'symbol-sort-key': ['coalesce', ['get', 'rank'], 10], 718 - 'text-allow-overlap': false, 719 - 'visibility': 'visible', 720 - }, 721 - 'paint': { 722 - 'text-color': 'hsl(0, 0%, 30%)', 723 - 'text-halo-blur': 0, 724 - 'text-halo-color': 'hsl(0, 0%, 100%)', 725 - 'text-halo-width': 1.5, 726 - }, 727 - }, 728 - // Suburbs & neighbourhoods: only at high zoom so they don't crowd city view 729 - { 730 - 'id': `${sourceName}-place_label_suburb`, 731 - 'type': 'symbol', 732 - 'source': sourceName, 733 - 'source-layer': 'place', 734 - 'minzoom': 13, 683 + 'minzoom': 11, 735 684 'maxzoom': 16, 736 685 'filter': [ 737 686 'all', 738 687 ['==', '$type', 'Point'], 739 - ['in', 'class', 'suburb', 'neighbourhood', 'quarter'], 688 + [ 689 + 'in', 690 + ['get', 'class'], 691 + ['literal', ['city', 'town', 'suburb', 'neighbourhood', 'quarter']], 692 + ], 740 693 ], 741 694 'layout': { 742 - 'text-anchor': 'center', 743 695 'text-field': '{name:latin}', 744 - 'text-font': ['Noto Sans Regular'], 745 - 'text-max-width': 6, 746 - 'text-size': { 'stops': [[13, 10], [16, 12]] }, 747 - 'text-transform': 'uppercase', 748 - 'text-letter-spacing': 0.05, 749 - 'visibility': 'visible', 750 - }, 751 - 'paint': { 752 - 'text-color': 'hsl(0, 0%, 45%)', 753 - 'text-halo-blur': 0, 754 - 'text-halo-color': 'hsl(0, 0%, 100%)', 755 - 'text-halo-width': 1, 756 - }, 757 - }, 758 - { 759 - 'id': `${sourceName}-place_label_city`, 760 - 'type': 'symbol', 761 - 'source': sourceName, 762 - 'source-layer': 'place', 763 - 'minzoom': 11, 764 - 'maxzoom': 16, 765 - 'filter': ['all', ['==', '$type', 'Point'], ['==', 'class', 'city']], 766 - 'layout': { 767 - 'text-field': '{name:latin}', 768 - 'text-font': ['Noto Sans Medium'], 769 - 'text-max-width': 10, 770 - 'text-size': { 'stops': [[9, 11], [13, 15]] }, 696 + 'text-font': [ 697 + 'case', 698 + ['==', ['get', 'class'], 'city'], 699 + ['literal', ['Noto Sans Medium']], 700 + ['literal', ['Noto Sans Regular']], 701 + ], 702 + 'text-max-width': ['match', ['get', 'class'], 'city', 10, 'town', 8, 6], 703 + 'text-size': [ 704 + 'interpolate', 705 + ['linear'], 706 + ['zoom'], 707 + 11, 708 + ['match', ['get', 'class'], 'city', 11, 0], 709 + 12, 710 + ['match', ['get', 'class'], 'city', 12, 'town', 10, 0], 711 + 13, 712 + ['match', ['get', 'class'], 'city', 13.5, 'town', 11, 10], 713 + 15, 714 + ['match', ['get', 'class'], 'city', 15, 'town', 13, 11], 715 + 16, 716 + ['match', ['get', 'class'], 'city', 15, 'town', 0, 12], 717 + ], 718 + 'text-transform': [ 719 + 'match', 720 + ['get', 'class'], 721 + 'suburb', 722 + 'uppercase', 723 + 'neighbourhood', 724 + 'uppercase', 725 + 'quarter', 726 + 'uppercase', 727 + 'none', 728 + ], 729 + 'text-letter-spacing': [ 730 + 'match', 731 + ['get', 'class'], 732 + 'suburb', 733 + 0.05, 734 + 'neighbourhood', 735 + 0.05, 736 + 'quarter', 737 + 0.05, 738 + 0, 739 + ], 771 740 'symbol-sort-key': ['coalesce', ['get', 'rank'], 10], 772 741 'text-allow-overlap': false, 773 742 }, 774 743 'paint': { 775 - 'text-color': 'hsl(0, 0%, 0%)', 744 + 'text-color': [ 745 + 'match', 746 + ['get', 'class'], 747 + 'city', 748 + 'hsl(0, 0%, 0%)', 749 + 'town', 750 + 'hsl(0, 0%, 30%)', 751 + 'hsl(0, 0%, 45%)', 752 + ], 776 753 'text-halo-blur': 0, 777 - 'text-halo-color': 'hsla(0, 0%, 100%, 0.75)', 778 - 'text-halo-width': 2, 754 + 'text-halo-color': [ 755 + 'match', 756 + ['get', 'class'], 757 + 'city', 758 + 'hsla(0, 0%, 100%, 0.75)', 759 + 'hsl(0, 0%, 100%)', 760 + ], 761 + 'text-halo-width': [ 762 + 'match', 763 + ['get', 'class'], 764 + 'city', 765 + 2, 766 + 'town', 767 + 1.5, 768 + 1, 769 + ], 779 770 }, 780 771 }, 781 772 {
+95 -106
www/utils/world_layers.ts
··· 32 32 33 33 // ── Landcover — colours match layers.ts exactly ─────────────────────────── 34 34 { 35 - 'id': 'landcover-grass', 35 + 'id': 'landcover', 36 36 'type': 'fill', 37 37 'source': 'world', 38 38 'source-layer': 'landcover', 39 - 'filter': ['==', 'class', 'grass'], 39 + 'filter': [ 40 + 'in', 41 + ['get', 'class'], 42 + ['literal', ['grass', 'wood', 'wetland', 'sand', 'rock', 'ice']], 43 + ], 40 44 'paint': { 41 - 'fill-color': 'hsl(82, 46%, 72%)', 42 - 'fill-opacity': 0.45, 43 - }, 44 - }, 45 - { 46 - 'id': 'landcover-wood', 47 - 'type': 'fill', 48 - 'source': 'world', 49 - 'source-layer': 'landcover', 50 - 'filter': ['==', 'class', 'wood'], 51 - 'paint': { 52 - 'fill-color': 'hsl(82, 46%, 72%)', 53 - 'fill-opacity': { 'base': 1, 'stops': [[6, 0.6], [9, 0.8]] }, 54 - }, 55 - }, 56 - 57 - { 58 - 'id': 'landcover-wetland', 59 - 'type': 'fill', 60 - 'source': 'world', 61 - 'source-layer': 'landcover', 62 - 'filter': ['==', 'class', 'wetland'], 63 - 'paint': { 64 - 'fill-color': 'hsl(180, 25%, 78%)', 65 - 'fill-opacity': 0.5, 66 - }, 67 - }, 68 - { 69 - 'id': 'landcover-sand', 70 - 'type': 'fill', 71 - 'source': 'world', 72 - 'source-layer': 'landcover', 73 - 'filter': ['==', 'class', 'sand'], 74 - 'paint': { 75 - 'fill-antialias': false, 76 - 'fill-color': 'rgba(232, 214, 38, 1)', 77 - 'fill-opacity': 0.3, 78 - }, 79 - }, 80 - { 81 - 'id': 'landcover-rock', 82 - 'type': 'fill', 83 - 'source': 'world', 84 - 'source-layer': 'landcover', 85 - 'filter': ['==', 'class', 'rock'], 86 - 'paint': { 87 - 'fill-color': 'hsl(30, 8%, 72%)', 88 - 'fill-opacity': 0.5, 89 - }, 90 - }, 91 - { 92 - 'id': 'landcover-ice', 93 - 'type': 'fill', 94 - 'source': 'world', 95 - 'source-layer': 'landcover', 96 - 'filter': ['==', 'class', 'ice'], 97 - 'paint': { 98 - 'fill-color': 'hsl(47, 22%, 94%)', 99 - 'fill-opacity': { 'base': 1, 'stops': [[0, 1], [8, 0.5]] }, 45 + 'fill-color': [ 46 + 'match', 47 + ['get', 'class'], 48 + 'grass', 49 + 'hsl(82, 46%, 72%)', 50 + 'wood', 51 + 'hsl(82, 46%, 72%)', 52 + 'wetland', 53 + 'hsl(180, 25%, 78%)', 54 + 'sand', 55 + 'rgba(232, 214, 38, 1)', 56 + 'rock', 57 + 'hsl(30, 8%, 72%)', 58 + 'ice', 59 + 'hsl(47, 22%, 94%)', 60 + 'hsl(82, 46%, 72%)', 61 + ], 62 + 'fill-opacity': [ 63 + 'interpolate', 64 + ['linear'], 65 + ['zoom'], 66 + 0, 67 + [ 68 + 'match', 69 + ['get', 'class'], 70 + 'ice', 71 + 1, 72 + 'wood', 73 + 0.6, 74 + 'sand', 75 + 0.3, 76 + 'grass', 77 + 0.45, 78 + 0.5, 79 + ], 80 + 6, 81 + [ 82 + 'match', 83 + ['get', 'class'], 84 + 'ice', 85 + 0.75, 86 + 'wood', 87 + 0.6, 88 + 'sand', 89 + 0.3, 90 + 'grass', 91 + 0.45, 92 + 0.5, 93 + ], 94 + 9, 95 + [ 96 + 'match', 97 + ['get', 'class'], 98 + 'ice', 99 + 0.5, 100 + 'wood', 101 + 0.8, 102 + 'sand', 103 + 0.3, 104 + 'grass', 105 + 0.45, 106 + 0.5, 107 + ], 108 + ], 100 109 }, 101 110 }, 102 111 ··· 222 231 }, 223 232 }, 224 233 { 225 - 'id': 'place-city', 234 + 'id': 'place-settlement', 226 235 'type': 'symbol', 227 236 'source': 'world', 228 237 'source-layer': 'place', 229 - 'filter': ['==', 'class', 'city'], 238 + 'filter': [ 239 + 'in', 240 + ['get', 'class'], 241 + ['literal', ['city', 'town', 'village']], 242 + ], 230 243 'minzoom': 6, 231 244 'layout': { 232 245 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']], 233 246 'text-font': ['Noto Sans Regular'], 234 - 'text-max-width': 10, 235 - 'text-size': { 'stops': [[6, 11], [9, 15]] }, 236 - }, 237 - 'paint': { 238 - 'text-color': 'hsl(0, 0%, 0%)', 239 - 'text-halo-blur': 0, 240 - 'text-halo-color': 'rgba(255, 255, 255, 0.75)', 241 - 'text-halo-width': 2, 242 - }, 243 - }, 244 - { 245 - 'id': 'place-town', 246 - 'type': 'symbol', 247 - 'source': 'world', 248 - 'source-layer': 'place', 249 - 'filter': ['==', 'class', 'town'], 250 - 'minzoom': 6, 251 - 'layout': { 252 - 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']], 253 - 'text-font': ['Noto Sans Regular'], 254 - 'text-size': { 'stops': [[6, 10], [9, 14]] }, 255 - 'text-max-width': 6, 256 - }, 257 - 'paint': { 258 - 'text-color': 'hsl(0, 0%, 25%)', 259 - 'text-halo-blur': 0, 260 - 'text-halo-color': 'rgba(255, 255, 255, 0.75)', 261 - 'text-halo-width': 2, 262 - }, 263 - }, 264 - { 265 - 'id': 'place-village', 266 - 'type': 'symbol', 267 - 'source': 'world', 268 - 'source-layer': 'place', 269 - 'filter': ['==', 'class', 'village'], 270 - 'minzoom': 8, 271 - 'layout': { 272 - 'text-field': ['coalesce', ['get', 'name:latin'], ['get', 'name']], 273 - 'text-font': ['Noto Sans Regular'], 274 - 'text-size': { 'stops': [[6, 10], [12, 14]] }, 275 - 'text-max-width': 6, 247 + 'text-max-width': ['match', ['get', 'class'], 'city', 10, 6], 248 + 'text-size': [ 249 + 'interpolate', 250 + ['linear'], 251 + ['zoom'], 252 + 6, 253 + ['match', ['get', 'class'], 'city', 11, 'town', 10, 0], 254 + 8, 255 + ['match', ['get', 'class'], 'city', 12, 'town', 11, 10], 256 + 9, 257 + ['match', ['get', 'class'], 'city', 15, 'town', 14, 14], 258 + ], 276 259 }, 277 260 'paint': { 278 - 'text-color': 'hsl(0, 0%, 25%)', 261 + 'text-color': [ 262 + 'match', 263 + ['get', 'class'], 264 + 'city', 265 + 'hsl(0, 0%, 0%)', 266 + 'hsl(0, 0%, 25%)', 267 + ], 279 268 'text-halo-blur': 0, 280 269 'text-halo-color': 'rgba(255, 255, 255, 0.75)', 281 270 'text-halo-width': 2,