'use strict'; class MapLibreRectangle { constructor(options = {}) { this.id = options.id || 'rectangle-' + Ox.uid(); this.bounds = options.bounds; this.draggable = options.draggable || false; this.onclick = options.onclick || null } _createRectangle() { const coords = this._getPolygonCoordinates(); const rectangleGeoJSON = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [coords] }, properties: { id: this.id } }; var sourceId = `${this.id}-rectangles` this.source = this.map.getSource(sourceId) if (!this.source) { this.map.addSource(sourceId, { type: 'geojson', data: { type: 'FeatureCollection', features: [] } }); this.source = this.map.getSource(sourceId) // Add fill layer var layerId = `${this.id}-fill` if (!this.map.getLayer(layerId)) { this.map.addLayer({ id: layerId, type: 'fill', source: sourceId, paint: { 'fill-color': '#088', 'fill-opacity': 0.3 } }); } // Add outline layer var layerId = `${this.id}-outline` if (!this.map.getLayer(layerId)) { this.map.addLayer({ id: layerId, type: 'line', source: sourceId, paint: { 'line-color': '#000', 'line-width': 2 } }); } } /* this.source._data.features.push(rectangleGeoJSON) this.source.setData(this.source._data) */ } _getPolygonCoordinates() { const sw = this.bounds._sw; const ne = this.bounds._ne; return [ [sw.lng, ne.lat], // NW [ne.lng, ne.lat], // NE [ne.lng, sw.lat], // SE [sw.lng, sw.lat], // SW [sw.lng, ne.lat] // Close polygon ]; } setBounds(bounds) { this.bounds = bounds; const coords = this._getPolygonCoordinates(); const updatedData = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [coords] }, properties: { id: this.id } }; var updated = false; this.source._data.features.forEach(feature => { if (feature.properties.id == this.id) { feature.geometry = updatedData.geometry updated = true } }) if (!updated) { this.source._data.features.push(updatedData) } this.source.setData(this.source._data) } getBounds() { return this.bounds; } _enableDragging() { let isDragging = false; let startPos; this.map.on('mousedown', `${this.id}-fill`, (e) => { e.preventDefault(); isDragging = true; startPos = e.lngLat; this.map.getCanvas().style.cursor = 'grabbing'; }); this.map.on('mousemove', (e) => { if (!isDragging) return; const dx = e.lngLat.lng - startPos.lng; const dy = e.lngLat.lat - startPos.lat; const sw = [this.bounds[0][0] + dx, this.bounds[0][1] + dy]; const ne = [this.bounds[1][0] + dx, this.bounds[1][1] + dy]; this.setBounds([sw, ne]); startPos = e.lngLat; }); this.map.on('mouseup', () => { if (isDragging) { isDragging = false; this.map.getCanvas().style.cursor = ''; } }); } _enableClicking() { this.map.on('click', `${this.id}-fill`, e => { console.log('click', e) if (this.onclick) { e.preventDefault() //e.stopPropagation() this.onclick(e) } }) } remove() { this.source._data.features = this.source._data.features.filter(feature => { return feature.properties.id != this.id }) this.source.setData(this.source._data) } add() { this.setBounds(this.bounds) } addTo(map) { this.map = map; this._createRectangle(); if (this.draggable) this._enableDragging(); this._enableClicking(); } } /*@ Ox.MapRectangle MapRectangle Object (options) -> MapRectangle Object options Options object map map place place @*/ Ox.MapRectangle = function(options) { options = Ox.extend({ map: null, place: null }, options); var that = this, themeData = Ox.Theme.getThemeData(); Ox.forEach(options, function(val, key) { that[key] = val; }); /*@ rectangle google.maps.Rectangle @*/ /* that.rectangle = new google.maps.Rectangle({ clickable: true, bounds: that.place.bounds }); */ that.rectangle = new MapLibreRectangle({ bounds: that.place.bounds, }); that.rectangle.addTo(that.map.map); that.rectangle.onclick = click /*@ markers array of markers (only corners for rectangle resizing) @*/ var cornerPositions = ['ne', 'nw', 'se', 'sw']; that.markers = cornerPositions.map(function(position) { return new Ox.MapRectangleMarker({ map: that.map, place: that.place, position: position }); }); setOptions(); function click(e) { if ( that.map.options('editable') && that.place.editable && !that.place.editing ) { that.place.edit(); } else if (that.map.getKey() == 'meta') { that.place.submit(); } else if (that.map.getKey() == 'shift') { that.map.zoomToPlace(); } else { that.map.panToPlace(); } } function setOptions() { var color = '#' + Ox.toHex(themeData[ that.place.editing ? 'mapPlaceEditingBorder' : 'mapPlaceSelectedBorder' ]); /* that.rectangle.setOptions({ bounds: that.place.bounds, fillColor: color, fillOpacity: that.place.editing ? 0.1 : 0, strokeColor: color, strokeOpacity: that.place.id[0] == '_' ? 0.5 : 1, strokeWeight: 2 }); */ /* console.log("fixme", { bounds: that.place.bounds, fillColor: color, fillOpacity: that.place.editing ? 0.1 : 0, strokeColor: color, strokeOpacity: that.place.id[0] == '_' ? 0.5 : 1, strokeWeight: 2 }); */ } /*@ add add @*/ that.add = function() { that.rectangle.add() return that; }; /*@ deselect deselect @*/ that.deselect = function() { setOptions(); Ox.Log('Map', 'MARKERS', that.markers) Ox.forEach(that.markers, function(marker) { marker.remove(); }); return that; }; /*@ remove remove @*/ that.remove = function() { that.rectangle.remove(); return that; } /*@ select select @*/ that.select = function() { setOptions(); Ox.forEach(that.markers, function(marker) { marker.add(); }); return that; }; /*@ update udpate @*/ that.update = function() { Ox.Log('Map', 'UPDATE...') setOptions(); // Update the visual rectangle bounds that.rectangle.setBounds(that.place.bounds); Ox.forEach(that.markers, function(marker) { marker.update(); }); return that; } return that; };