map.templ

  1package components
  2
  3import "encoding/json"
  4
  5var mapHandle = templ.NewOnceHandle()
  6var routeHandle = templ.NewOnceHandle()
  7
  8func toJSON(bbox [4]float64) string {
  9	v, _ := json.Marshal(bbox)
 10	return string(v)
 11}
 12
 13templ Map(style string, bbox [4]float64, class string) {
 14	<script type="module">
 15		import { Map } from 'https://esm.sh/maplibre-gl@5';
 16
 17		function loadMapCSS() {
 18          if (document.querySelector('link[href*="maplibre-gl.css"]')) return;
 19          const link = document.createElement('link');
 20          link.rel = 'stylesheet';
 21          link.href = 'https://esm.sh/maplibre-gl@5/dist/maplibre-gl.css';
 22          document.head.appendChild(link);
 23		}
 24
 25		class RTWmap extends HTMLElement {
 26		  map;
 27
 28		  constructor() {
 29				super();
 30				loadMapCSS();
 31				this.addEventListener('map:context', (event) => {
 32				  console.log("received map:context");
 33				  event.detail.cb(this.map);
 34				})
 35		  }
 36		  connectedCallback() {
 37			this.style.cssText = 'display: block; width: 100%; height: 100%; overflow: hidden; border-radius: inherit;';
 38
 39			const elem = document.createElement('div');
 40			elem.style.cssText = 'width:100%;height:100%;overflow:hidden;'
 41			this.appendChild(elem);
 42
 43			const styleURL = this.getAttribute('map-style');
 44			this.map = new Map({
 45				container: elem,
 46				style: styleURL,
 47				center: [0, 0],
 48				zoom: 1,
 49				attributionControl: false
 50			});
 51			const bbox = JSON.parse(this.getAttribute('bbox'));
 52		    this.map.fitBounds(bbox, {padding: {top: 50, bottom: 50, left: 75, right: 75}});
 53		  }
 54		  disconnectedCallback() {
 55			this.map?.remove();
 56		  }
 57		}
 58		if (!customElements.get('rtw-map')) {
 59		  customElements.define('rtw-map', RTWmap);
 60		}
 61	</script>
 62	<rtw-map map-style={ style } bbox={ toJSON(bbox) } class={ class }>
 63		{ children... }
 64	</rtw-map>
 65}
 66
 67templ Route(src string, color string) {
 68	<script type="module">
 69		class RTWRoute extends HTMLElement {
 70		map;
 71		constructor() {
 72		  super();
 73		}
 74		connectedCallback() {
 75		  console.log("route connected");
 76		  if (!this.map) {
 77			this.dispatchEvent(new CustomEvent('map:context', {
 78			  detail: {
 79				cb: (map) => {
 80					console.log("received map", map);
 81					this.map = map;
 82				    this.addRoute();
 83				}
 84			  },
 85			  bubbles: true,
 86			  composed: true
 87			}));
 88		  }
 89		}
 90		addRoute() {
 91		  if (!this.map) return;
 92		  const hash = this.getAttribute('src');
 93		  console.log("got hash", hash);
 94		  if (!hash) return;
 95
 96		  if (!this.map.isStyleLoaded()) {
 97            this.map.once('load', () => this.addRoute());
 98          }
 99
100		  this.map.addSource(`source-${hash}`, {type: 'geojson', data: `/route/${hash}.geojson`});
101		  this.map.addLayer({
102			id: `layer-${hash}`,
103			source: `source-${hash}`,
104			type: 'line',
105			layout: {
106              'line-join': 'round',
107              'line-cap': 'round'
108            },
109            paint: {
110              'line-color': this.getAttribute('color') || '#888',
111              'line-width': 4
112            }
113		  });
114		}
115		}
116		if (!customElements.get('rtw-route')) {
117		  customElements.define('rtw-route', RTWRoute);
118		}
119	</script>
120	<rtw-route src={ src } color={ color }></rtw-route>
121}