projects/components/src/lib/editable-map/editable-map.component.ts
Editable Map component
Visualizes polygon data drawn on leaflet map
tag: no-editable-map
OnInit
LeafletMap
| selector | no-editable-map |
| styleUrls | ./editable-map.component.scss |
| templateUrl | ./editable-map.component.html |
Properties |
|
Methods |
Inputs |
Outputs |
Accessors |
| config | |
Type : any
|
|
|
Holds all configuration for this component |
|
| editableMapData | |
Type : any
|
|
|
Holds all data for this component |
|
| points | |
Type : string
|
|
|
Trigger to get all points |
|
| fitBounds | ||||||
fitBounds(bounds: any)
|
||||||
|
Calls leaflet to fit bounds of all our items
Parameters :
Returns :
void
|
| getPoints |
getPoints()
|
|
Getter for all points drawn To get all points drawn in js
Returns :
string
JSON |
| Async initMap |
initMap()
|
|
Creates Leaflet map
Returns :
Promise<Map>
Map object |
| Async ngOnChanges | ||||||
ngOnChanges(change: SimpleChanges)
|
||||||
|
Main function where init of map happens + wiring up data and controls
Parameters :
Returns :
Promise<void>
|
| setupCustomControls |
setupCustomControls()
|
|
Function to wire up custom controls which dont exist anywhere else
Returns :
void
|
| setupLayers |
setupLayers()
|
|
Initiation of all layers we need
Returns :
void
|
| setView |
setView()
|
|
Trigger for fitting all items on the map in view
Returns :
void
|
| transformLayerToJson | ||||||
transformLayerToJson(layer: Polyline)
|
||||||
|
Transforms layer to json format
Parameters :
Returns :
GeoJSON.Feature
JSON |
| layer__drawings |
Type : FeatureGroup
|
Default value : new FeatureGroup()
|
|
Layer for heats to hold in |
| map |
Type : Map
|
|
Leaflet map |
| Public tag |
Type : string
|
|
ID key for map |
| userInteraction |
Type : boolean
|
Default value : false
|
|
If user interacted with the map. Reset by clicking on control |
| points |
getpoints()
|
|
Trigger to get all points |
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
SimpleChanges,
} from '@angular/core';
import {
Canvas,
FeatureGroup,
LatLng,
Layer,
LayerEvent,
Map,
Polyline,
} from 'leaflet';
import { LeafletMap } from '../Interfaces/LeafletMap';
import { Helpers } from '../Utils/Helpers';
import { MapHelper } from '../Utils/MapHelper';
import 'leaflet-draw';
/**
* @ignore
*/
declare var L: any;
/**
* Editable Map component
*
* Visualizes polygon data drawn on leaflet map
*
* tag: no-editable-map
*/
@Component({
selector: 'no-editable-map',
templateUrl: './editable-map.component.html',
styleUrls: ['./editable-map.component.scss'],
})
export class EditableMapComponent implements OnInit, LeafletMap {
/**
* ID key for map
*/
public tag!: string;
/**
* Leaflet map
* @property {Map}
*/
map!: Map;
/**
* Holds all configuration for this component
* @example
* ```
* var config = {
* mapLat: '48.35815612764849',
* mapLon: '14.483862289547574',
* tiles: ['default', 'extended', 'dark', 'satellite'],
* mapStyle: 'dark',
* showLocation: false,
* showReset: true,
* controls: {
* layers: {
* position: 'topright',
* },
* zoom: {
* position: 'topright',
* },
* scale: {
* scaleUnitMetric: true,
* position: 'bottomleft',
* },
* fullscreen: {
* position: 'topright',
* viewFullscreen: 'View fullscreen',
* exitFullscreen: 'Exit fullscreen',
* },
* reset: {
* position: 'topright',
* icon: 'mdi mdi-fit-to-screen',
* },
* },
*
* boundsPadding: [50, 50],
* translations: {
* maps: {
* defaultMap: 'Default map',
* extendedMap: 'Extended map',
* darkMap: 'Dark map',
* satelliteMap: 'Satellite map',
* },
* },
* }
* const heatMap = document.querySelector('no-heat-map');
* heatMap.config = config;
* ```
*/
@Input() config: any;
/**
* Holds all data for this component
* @example
* ```
* var data = {
* features: [
* {
* type: 'Feature',
* properties: {
* radius: 51.054317060984374,
* },
* geometry: {
* type: 'Point',
* coordinates: [14.484015, 48.35812],
* },
* },
* {
* type: 'Feature',
* properties: {},
* geometry: {
* type: 'Polygon',
* coordinates: [
* [
* [14.477599, 48.358163],
* [14.477599, 48.358577],
* [14.478779, 48.358577],
* [14.478779, 48.358163],
* [14.477599, 48.358163],
* ],
* ],
* },
* },
* ],
* type: 'FeatureCollection',
* }
* const heatMap = document.querySelector('no-heat-map');
* heatMap.editableMapData = data;
* ```
*/
@Input() editableMapData: any;
/**
* Listener for selected tiles
*
* @example
* in js
* ```
* const editableMap = document.querySelector('no-editable-map');
* editableMap.addEventListener('changeTilesTrigger', (tile) => {
//could be tile.detail, since its event
* console.log(tile); //{name, mapstyle}
* })
* ```
*
*/
@Output() changeTilesTrigger: EventEmitter<any> = new EventEmitter<any>();
/**
* If user interacted with the map. Reset by clicking on control
*/
userInteraction: boolean = false;
/**
* Layer for heats to hold in
*/
layer__drawings: FeatureGroup = new FeatureGroup();
/**
* Trigger to get all points
*/
@Input() get points() {
return this.getPoints();
}
/**
* @ignore
*/
constructor(private helpers: Helpers, private mapHelper: MapHelper) {
this.tag = this.helpers.generateToken();
}
/**
* @ignore
*/
ngOnInit(): void {
/* if (this.config == null) {
throw new Error('Config cannot be null!');
} */
}
/**
* Main function where init of map happens + wiring up data and controls
* @param change
*/
async ngOnChanges(change: SimpleChanges): Promise<void> {
if (!this.map) {
this.map = await this.initMap();
this.setupLayers();
this.setupCustomControls();
}
if (change.config) {
if (change.config?.currentValue == null && this.map) {
throw new Error('Config cannot be null!');
} else {
if (!change.config.currentValue.translations) {
throw new Error('There are no translations, please include them');
}
this.map.panTo(new LatLng(this.config.mapLat, this.config.mapLon));
this.mapHelper.setupTiles(this);
this.mapHelper.setupControls(this);
this.mapHelper.listenToUserEvents(this);
}
}
if (this.config?.translations.draw && this.map && this.editableMapData) {
L.geoJSON(this.editableMapData, {
pointToLayer: (feature: GeoJSON.Feature, latlng: LatLng) => {
if (feature?.properties?.radius) {
return new L.Circle(latlng, feature?.properties?.radius);
} else {
return new L.Marker(latlng);
}
},
onEachFeature: (feature: GeoJSON.Feature, layer: Layer) => {
this.layer__drawings.addLayer(layer);
},
});
}
}
/**
* Function to wire up custom controls which dont exist anywhere else
*/
setupCustomControls(): void {
L.drawLocal = this.config?.translations.draw;
this.map.addControl(
new L.Control.Draw({
position: this.config?.controls.draw.position,
edit: {
featureGroup: this.layer__drawings,
poly: {
allowIntersection: false,
},
},
draw: {
polygon: {
allowIntersection: false,
showArea: true,
},
circle: false,
circlemarker: false,
polyline: false,
rectangle: {
shapeOptions: {
clickable: false,
},
},
marker: false,
},
})
);
this.map.on(L.Draw.Event.CREATED, (event: LayerEvent) => {
var layer = event.layer;
this.layer__drawings.addLayer(layer);
});
}
/**
* Creates Leaflet map
* @returns {Map} Map object
*/
async initMap(): Promise<Map> {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
const leafletMap = L.map(this.tag, {
preferCanvas: true,
editable: true,
renderer: new Canvas(),
center: [48.35847605526803, 14.483717889857843],
zoom: 16,
maxZoom: 19,
minZoom: 2,
zoomControl: false,
attributionControl: false,
});
resolve(leafletMap);
}, 1);
} catch (error: any) {
reject(error);
}
});
}
/**
* Initiation of all layers we need
*/
setupLayers(): void {
this.layer__drawings = new FeatureGroup().addTo(this.map);
}
/**
* Trigger for fitting all items on the map in view
*/
setView(): void {
throw new Error('Method not implemented.');
}
/**
* Calls leaflet to fit bounds of all our items
* @param bounds
*/
fitBounds(bounds: any): void {
throw new Error('Method not implemented.');
}
/**
* Getter for all points drawn
*
* To get all points drawn in js
* ```
* const editableMap = document.querySelector('no-editable-map');
* function setSelection() {
* console.log(nexoptEditableMap.points);
* }
* ```
* @returns {string} JSON
*/
getPoints(): string {
let holder: any[] = [];
this.layer__drawings.eachLayer((layer: any) => {
holder.push(this.transformLayerToJson(layer));
});
var collection = JSON.stringify({
features: holder,
type: 'FeatureCollection',
});
return collection;
}
/**
* Transforms layer to json format
* @param layer
* @returns {GeoJSON.Feature} JSON
*/
transformLayerToJson(layer: Polyline): GeoJSON.Feature {
const json = layer.toGeoJSON();
/* if (layer instanceof L.Circle) {
json.properties.radius = layer.getRadius();
} */
return json;
}
}
<div class="map-container">
<div id="{{tag}}" class="editable-map"></div>
</div>
./editable-map.component.scss
.map-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
}
.editable-map {
height: 100%;
background-color: transparent;
}
::ng-deep
.leaflet-control-layers.leaflet-control
.leaflet-control-layers-toggle {
width: 30px;
height: 30px;
background: none;
text-decoration: none;
&:before {
content: "";
display: inline-block;
font: normal normal normal 24px/1 Material Design Icons;
font-size: inherit;
text-rendering: auto;
line-height: inherit;
-webkit-font-smoothing: antialiased;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 18px;
color: #000;
}
}
::ng-deep .leaflet-control-layers.leaflet-control {
transition: all 0.25s ease-in-out;
}
::ng-deep
.leaflet-control-layers.leaflet-control
.leaflet-control-layers-list
label {
cursor: pointer;
}