496 lines
14 KiB
JavaScript
496 lines
14 KiB
JavaScript
import {Map, NavigationControl, Marker, Popup} from 'maplibre-gl';
|
|
import { Map as Map$1, TileLayer, Marker as Marker$1, polygon } from 'leaflet';
|
|
import * as i0 from '@angular/core';
|
|
import { inject, PLATFORM_ID, output, Directive, Injector, input, runInInjectionContext, effect, ViewChild, Component } from '@angular/core';
|
|
import { isPlatformBrowser } from '@angular/common';
|
|
import {LatLng} from "leaflet/src/geo/index.js";
|
|
|
|
function getLngLat(latLng) {
|
|
return [latLng[1], latLng[0]];
|
|
}
|
|
|
|
class LibreAdapter {
|
|
map;
|
|
deliveryCheckMarker;
|
|
popup;
|
|
markers = {};
|
|
init(container, options) {
|
|
this.map = new Map({
|
|
container,
|
|
style: options.styleUrl,
|
|
center: getLngLat(options.center),
|
|
zoom: options.zoom,
|
|
});
|
|
this.map.addControl(new NavigationControl(), 'top-right');
|
|
}
|
|
setCenter(latLng) {
|
|
this.map.setCenter(getLngLat(latLng));
|
|
}
|
|
setZoom(zoom) {
|
|
this.map.setZoom(zoom);
|
|
}
|
|
addMarker(latLng, options) {
|
|
const coords = getLngLat(latLng);
|
|
|
|
if (options.id) {
|
|
if (this.markers[options.id]) {
|
|
this.markers[options.id].remove();
|
|
}
|
|
|
|
const marker = new Marker({ color: options.color || 'red' })
|
|
.setLngLat(coords)
|
|
.addTo(this.map);
|
|
this.markers[options.id] = marker;
|
|
} else {
|
|
if (this.deliveryCheckMarker) {
|
|
this.deliveryCheckMarker.remove();
|
|
}
|
|
this.deliveryCheckMarker = new Marker({ color: options?.color || 'red' })
|
|
.setLngLat(coords)
|
|
.addTo(this.map);
|
|
}
|
|
}
|
|
removeMarker(id) {
|
|
if (id) {
|
|
if (this.markers[id]) {
|
|
this.markers[id].remove();
|
|
delete this.markers[id];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.deliveryCheckMarker)
|
|
{
|
|
this.deliveryCheckMarker.remove();
|
|
this.deliveryCheckMarker = undefined;
|
|
}
|
|
}
|
|
}
|
|
destroy() {
|
|
this.map.remove();
|
|
}
|
|
addZone(zones) {
|
|
this.updateZone(zones);
|
|
}
|
|
updateZone(zones) {
|
|
const features = zones.map((zone) => {
|
|
const coords = zone.polygon.map((pt) => [pt.y, pt.x]);
|
|
|
|
if (coords[0][0] !== coords[coords.length - 1][0] || coords[0][1] !== coords[coords.length - 1][1]) {
|
|
coords.push(coords[0]);
|
|
}
|
|
|
|
return {
|
|
type: 'Feature',
|
|
properties: {
|
|
id: zone.id,
|
|
name: zone.name,
|
|
color: zone.color ? zone.color : '#ff0000',
|
|
opacity: zone.opacity ? zone.opacity : 0.4,
|
|
},
|
|
geometry: {
|
|
type: 'Polygon',
|
|
coordinates: [coords]
|
|
},
|
|
};
|
|
});
|
|
|
|
const geojson = {
|
|
type: 'FeatureCollection',
|
|
features,
|
|
};
|
|
|
|
if (this.map.getSource("zones")) {
|
|
(this.map.getSource("zones").setData(geojson));
|
|
}
|
|
else {
|
|
this.map.addSource("zones", {
|
|
type: "geojson",
|
|
data: geojson,
|
|
});
|
|
|
|
this.map.addLayer({
|
|
id: "zones-fill",
|
|
type: "fill",
|
|
source: "zones",
|
|
paint: {
|
|
"fill-color": ["get", "color"],
|
|
"fill-opacity": ["get", "opacity"],
|
|
},
|
|
});
|
|
|
|
this.map.addLayer({
|
|
id: "zones-outline",
|
|
type: "line",
|
|
source: "zones",
|
|
paint: {
|
|
"line-color": ["get", "color"],
|
|
"line-width": 2,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
openAddressPopup(address)
|
|
{
|
|
const bounds = this.map.getBounds();
|
|
const corner = bounds.getNorthWest();
|
|
this.closePopup();
|
|
this.popup = new Popup({
|
|
closeButton: false,
|
|
closeOnClick: false,
|
|
})
|
|
.setLngLat([corner.lng, corner.lat])
|
|
.setHTML(`
|
|
<div class="delivery-address">
|
|
<p>${address.line1}</p>
|
|
${address.line2 ? `<p>${address.line2}</p>` : ''}
|
|
<p>${address.city}, ${address.postalCode}</p>
|
|
<p>${address.subdivision}, ${address.country}</p>
|
|
${address.shippingFee ? `<p>Shipping ${address.shippingFee}</p>` : ''}
|
|
${address.deliverySchedule ? `<p>Delivery time estimate ${address.deliverySchedule}</p>` : ''}
|
|
</div>`)
|
|
.addTo(this.map);
|
|
|
|
this.map.on('move', () => {
|
|
if (this.popup) {
|
|
const newBounds = this.map.getBounds();
|
|
const newCorner = newBounds.getNorthWest();
|
|
this.popup.setLngLat([newCorner.lng, newCorner.lat]);
|
|
}
|
|
});
|
|
}
|
|
openZonePopup(zone)
|
|
{
|
|
let sumLng = 0;
|
|
let sumLat = 0;
|
|
zone.polygon.forEach(point => {
|
|
sumLng += point.y;
|
|
sumLat += point.x;
|
|
});
|
|
const centerLng = sumLng / zone.polygon.length;
|
|
const centerLat = sumLat / zone.polygon.length;
|
|
|
|
this.closePopup();
|
|
this.popup = new Popup({
|
|
closeButton: true,
|
|
closeOnClick: false,
|
|
})
|
|
.setLngLat([centerLng, centerLat])
|
|
.setHTML(`
|
|
<div class="delivery-zone">
|
|
${zone.name ? `<p><strong>${zone.name}</strong></p>` : ''}
|
|
${zone.shippingFee ? `<p>Shipping: ${zone.shippingFee}$</p>` : ''}
|
|
${zone.deliverySchedule ? `<p>Delivery: ${zone.deliverySchedule}</p>` : ''}
|
|
</div>`)
|
|
.addTo(this.map);
|
|
}
|
|
closePopup()
|
|
{
|
|
if (this.popup) {
|
|
this.popup.remove();
|
|
this.popup = undefined;
|
|
}
|
|
}
|
|
on(type, event,){
|
|
this.map.on(type, event);
|
|
}
|
|
}
|
|
|
|
class LeafletAdapter {
|
|
map;
|
|
deliveryCheckMarker;
|
|
markers = {};
|
|
popup;
|
|
init(container, options) {
|
|
this.map = new Map$1(container).setView(options.center, options.zoom);
|
|
new TileLayer(options.tileUrl, {
|
|
attribution: '© OpenStreetMap contributors',
|
|
}).addTo(this.map);
|
|
}
|
|
setCenter(latLng) {
|
|
this.map.setView(latLng, this.map.getZoom());
|
|
TileLayer.map
|
|
}
|
|
setZoom(zoom) {
|
|
this.map.setZoom(zoom);
|
|
}
|
|
addMarker(latLng, options) {
|
|
if (options.id) {
|
|
if (this.markers[options.id]) {
|
|
this.map.removeLayer(this.markers[options.id]);
|
|
}
|
|
const marker = new Marker$1(latLng)
|
|
marker.addTo(this.map);
|
|
this.markers[options.id] = marker;
|
|
} else {
|
|
if (this.deliveryCheckMarker) {
|
|
this.map.removeLayer(this.deliveryCheckMarker);
|
|
}
|
|
this.deliveryCheckMarker = new Marker$1(latLng).addTo(this.map);
|
|
}
|
|
}
|
|
|
|
removeMarker(id) {
|
|
if (id) {
|
|
if (this.markers[id]) {
|
|
this.map.removeLayer(this.markers[id]);
|
|
delete this.markers[id];
|
|
}
|
|
} else {
|
|
if (this.deliveryCheckMarker) {
|
|
this.map.removeLayer(this.deliveryCheckMarker);
|
|
this.deliveryCheckMarker = undefined;
|
|
}
|
|
}
|
|
}
|
|
destroy() {
|
|
this.map.remove();
|
|
}
|
|
addZone(zones) {
|
|
this.updateZone(zones);
|
|
}
|
|
updateZone(zones) {
|
|
for(let zone of zones)
|
|
{
|
|
const latlngs = zone.polygon.map((geoPoint) => {
|
|
return new LatLng(geoPoint.x, geoPoint.y);
|
|
});
|
|
let color = "#ff0000"
|
|
let opacity = 0.4;
|
|
if(zone.color)
|
|
color = zone.color;
|
|
|
|
if (zone.opacity)
|
|
opacity = zone.opacity;
|
|
|
|
polygon(latlngs, { color, fillOpacity: opacity })
|
|
.addTo(this.map);
|
|
}
|
|
}
|
|
openAddressPopup(address)
|
|
{
|
|
const bounds = this.map.getBounds();
|
|
const corner = bounds.getNorthEast();
|
|
|
|
if (this.popup) {
|
|
this.map.removeLayer(this.popup);
|
|
}
|
|
|
|
this.popup = new Marker$1(corner, {
|
|
opacity: 0
|
|
})
|
|
.addTo(this.map)
|
|
.bindTooltip(`
|
|
<div class="delivery-address">
|
|
<p>${address.line1}</p>
|
|
${address.line2 ? `<p>${address.line2}</p>` : ''}
|
|
<p>${address.city}, ${address.postalCode}</p>
|
|
<p>${address.subdivision}, ${address.country}</p>
|
|
${address.shippingFee ? `<p>Shipping ${address.shippingFee}</p>` : ''}
|
|
${address.deliverySchedule ? `<p>Delivery time estimate ${address.deliverySchedule}</p>` : ''}
|
|
</div>`,
|
|
{
|
|
permanent: true,
|
|
offset: [-38, 135]
|
|
})
|
|
.openTooltip();
|
|
this.map.on('move', () => {
|
|
if (this.popup) {
|
|
const newBounds = this.map.getBounds();
|
|
const newCorner = newBounds.getNorthEast();
|
|
this.popup.setLatLng(newCorner);
|
|
}
|
|
});
|
|
|
|
}
|
|
openZonePopup(zone)
|
|
{
|
|
let sumLat = 0;
|
|
let sumLng = 0;
|
|
zone.polygon.forEach(point => {
|
|
sumLat += point.x;
|
|
sumLng += point.y;
|
|
});
|
|
const centerLat = sumLat / zone.polygon.length;
|
|
const centerLng = sumLng / zone.polygon.length;
|
|
|
|
if (this.popup) {
|
|
this.map.removeLayer(this.popup);
|
|
}
|
|
|
|
this.popup = new Marker$1([centerLat, centerLng], {
|
|
opacity: 0
|
|
})
|
|
.addTo(this.map)
|
|
.bindPopup(`
|
|
<div >
|
|
${zone.name ? `<p><strong>${zone.name}</strong></p>` : ''}
|
|
${zone.shippingFee ? `<p>Shipping: ${zone.shippingFee}</p>` : ''}
|
|
${zone.deliverySchedule ? `<p>Delivery: ${zone.deliverySchedule}</p>` : ''}
|
|
</div>`)
|
|
.openPopup();
|
|
}
|
|
closePopup()
|
|
{
|
|
if (this.popup) {
|
|
this.map.removeLayer(this.popup);
|
|
this.popup = undefined;
|
|
}
|
|
}
|
|
on(type, event) {
|
|
this.map.on(type, event);
|
|
}
|
|
}
|
|
|
|
class MapFacade {
|
|
adapter;
|
|
leafletZoomOffset = 1;
|
|
constructor(forceRaster, webglAvailable) {
|
|
if (forceRaster || !webglAvailable) {
|
|
this.adapter = new LeafletAdapter();
|
|
}
|
|
else {
|
|
this.adapter = new LibreAdapter();
|
|
}
|
|
}
|
|
init(container, options) {
|
|
if (this.adapter instanceof LeafletAdapter)
|
|
options.zoom += this.leafletZoomOffset;
|
|
this.adapter.init(container, options);
|
|
}
|
|
setCenter(latLng) {
|
|
this.adapter.setCenter(latLng);
|
|
}
|
|
setZoom(zoom) {
|
|
if (this.adapter instanceof LeafletAdapter)
|
|
zoom += this.leafletZoomOffset;
|
|
this.adapter.setZoom(zoom);
|
|
}
|
|
addMarker(latLng, options) {
|
|
this.adapter.addMarker(latLng, options);
|
|
}
|
|
removeMarker() {
|
|
this.adapter.removeMarker();
|
|
}
|
|
destroy() {
|
|
this.adapter.destroy();
|
|
}
|
|
addZone(zones) {
|
|
this.adapter.addZone(zones);
|
|
}
|
|
updateZone(zones) {
|
|
this.adapter.updateZone(zones);
|
|
}
|
|
openAddressPopup(deliveryAddress) {
|
|
this.adapter.openAddressPopup(deliveryAddress);
|
|
}
|
|
openZonePopup(zone) {
|
|
this.adapter.openZonePopup(zone);
|
|
}
|
|
closePopup() {
|
|
this.adapter.closePopup();
|
|
}
|
|
on(type, event,){
|
|
this.adapter.on(type, event);
|
|
}
|
|
}
|
|
|
|
class WebglDetectionDirective {
|
|
platformId = inject(PLATFORM_ID);
|
|
webglSupport = output();
|
|
ngOnInit() {
|
|
if (!isPlatformBrowser(this.platformId))
|
|
return;
|
|
const supported = this.checkWebGLSupport();
|
|
this.webglSupport.emit(supported);
|
|
}
|
|
checkWebGLSupport() {
|
|
let canvas = undefined;
|
|
try {
|
|
canvas = document.createElement('canvas');
|
|
const gl = (canvas.getContext('webgl') ||
|
|
canvas.getContext('experimental-webgl'));
|
|
const supported = !!window.WebGLRenderingContext &&
|
|
!!(gl);
|
|
return supported;
|
|
}
|
|
catch {
|
|
return false;
|
|
}
|
|
finally {
|
|
canvas?.remove();
|
|
}
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: WebglDetectionDirective, isStandalone: true, selector: "[webglDetection]", outputs: { webglSupport: "webglSupport" }, ngImport: i0 });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: WebglDetectionDirective, decorators: [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: '[webglDetection]',
|
|
standalone: true
|
|
}]
|
|
}] });
|
|
|
|
class OpenMapComponent {
|
|
platformId = inject(PLATFORM_ID);
|
|
injector = inject(Injector);
|
|
webglSupported;
|
|
map;
|
|
mapContainer;
|
|
options = input({
|
|
center: [50.426606229502525, 30.56308375468811],
|
|
zoom: 6,
|
|
styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
|
|
tileUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
forceRaster: false,
|
|
});
|
|
mapReady = output();
|
|
ngAfterViewInit() {
|
|
if (false === isPlatformBrowser(this.platformId))
|
|
return;
|
|
runInInjectionContext(this.injector, () => {
|
|
effect(() => {
|
|
if (undefined === this.webglSupported)
|
|
return;
|
|
if (!this.map)
|
|
this.initializeMap();
|
|
});
|
|
});
|
|
}
|
|
webglDetection(supported) {
|
|
this.webglSupported = supported;
|
|
if (undefined === this.map)
|
|
this.initializeMap();
|
|
}
|
|
initializeMap() {
|
|
const options = this.options();
|
|
this.map = new MapFacade(options.forceRaster, this.webglSupported);
|
|
this.map.init(this.mapContainer.nativeElement, options);
|
|
this.mapReady.emit(this.map);
|
|
}
|
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.14", type: OpenMapComponent, isStandalone: true, selector: "open-map", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { mapReady: "mapReady" }, viewQueries: [{ propertyName: "mapContainer", first: true, predicate: ["mapContainer"], descendants: true, static: true }], ngImport: i0, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"], dependencies: [{ kind: "directive", type: WebglDetectionDirective, selector: "[webglDetection]", outputs: ["webglSupport"] }] });
|
|
}
|
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpenMapComponent, decorators: [{
|
|
type: Component,
|
|
args: [{ selector: 'open-map', imports: [
|
|
WebglDetectionDirective
|
|
], standalone: true, template: "<div #mapContainer class=\"map-container\" webglDetection (webglSupport)=\"webglDetection($event)\">\n\n</div>\n", styles: [".map-container{width:100%;height:100%}\n"] }]
|
|
}], propDecorators: { mapContainer: [{
|
|
type: ViewChild,
|
|
args: ['mapContainer', { static: true }]
|
|
}] } });
|
|
|
|
/*
|
|
* Public API Surface of ngx-open-open-map-wrapper
|
|
*/
|
|
// Interfaces & types
|
|
|
|
/**
|
|
* Generated bundle index. Do not edit.
|
|
*/
|
|
|
|
export { MapFacade, OpenMapComponent, WebglDetectionDirective, getLngLat };
|
|
//# sourceMappingURL=svrnty-ngx-open-map-wrapper.mjs.map
|