File

projects/components/src/lib/editable-map/editable-map.component.ts

Description

Editable Map component

Visualizes polygon data drawn on leaflet map

tag: no-editable-map

Implements

OnInit LeafletMap

Metadata

Index

Properties
Methods
Inputs
Outputs
Accessors

Inputs

config
Type : any

Holds all configuration for this component

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;
editableMapData
Type : any

Holds all data for this component

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;
points
Type : string

Trigger to get all points

Outputs

changeTilesTrigger
Type : EventEmitter<any>

Listener for selected tiles

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}
})

Methods

fitBounds
fitBounds(bounds: any)

Calls leaflet to fit bounds of all our items

Parameters :
Name Type Optional
bounds any No
Returns : void
getPoints
getPoints()

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

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 :
Name Type Optional
change SimpleChanges No
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 :
Name Type Optional
layer Polyline No
Returns : GeoJSON.Feature

JSON

Properties

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

Accessors

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;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""