Source: components/userComponents/maps/ZonesMap.js

import React from "react";
import { SimpleHereMap } from './SimpleHereMap';
import * as mapsjs from 'react-bootstrap';
import { fetchMonitoredArea } from '../../restmodules/MonitoredAreaRestModule';
import ReactDOMServer from 'react-dom/server';
import MonitoredAreaMarkerPopUp from './MonitoredAreaMarkerPopUp';
import { withNamespaces } from 'react-i18next';

/**
 * Map of zones
 * @extends Component
 * @hideconstructor
 */
class ZonesMap extends React.Component {

  /**
   * @constructs
   * @param props
   */
  constructor(props) {
    super(props);
    /* Initialize React ref to map */
    this.hereMap = React.createRef();
    this.isDragged = false;

    this.state = {
      newZone: [],
      monitoredArea: null
    };
    this.newZonePolygon = null;
    this.oldZonePolygons = [];

    this.clearNewZone = this.clearNewZone.bind(this);
  }

  /**
   *
   */
  clearNewZone() {
    if (this.newZonePolygon && this.newZonePolygon  !== null && this.newZonePolygon  !== undefined) this.hereMap.current.removeObjectFromMap(this.newZonePolygon);
    if (this.state.newZone !== undefined || this.state.newZone !== null || this.state.newZone.length !== 0) this.hereMap.current.removeObjectsFromMap(this.state.newZone);
    this.newZonePolygon = null;
    this.setState({
      newZone: []
    }, this.saveChanges
    );

  }

  /**
   *
   */
  saveChanges(){
    let markers = this.state.newZone;
    let worldVertices = {"points": [] };
    for (let i = 0; i < markers.length; ++i) {
      let point = markers[i].getPosition();
      let vertex = {
        "x": point.lat,
        "y": point.lng
      }
      worldVertices.points.push(vertex);
    }
    this.props.onNewZoneMapChange(worldVertices);
  }

  /**
   *
   * @param event
   */
  onMapClick = (event) => {
    if (this.isDragged) {
      this.isDragged = false;
      return;
    }
    let coords = this.hereMap.current.screenToGeo(event.currentPointer.viewportX, event.currentPointer.viewportY);
    let marker = new window.H.map.Marker(coords);
    marker.draggable = true;
    this.hereMap.current.addObjectToMap(marker);

    //add this point to newZone polygon points
    let points = this.state.newZone;
    points.push(marker);

    this.setState({
      newZone: points
      }, this.saveChanges
    );
  };

  /**
   *
   * @param event
   */
  onDragStart = (event) => {
    this.isDragged = true;
    let target = event.target;
    if(target instanceof window.H.map.Marker){
      this.hereMap.current.disableBehaviour();
    }
  };

  /**
   *
   * @param event
   */
  onDragEnd = (event) => {
    let target = event.target;
    if (target instanceof window.H.map.Marker) {
      this.hereMap.current.enableBehaviour();

      this.saveChanges();
      this.updateMap();
    }
  };

  /**
   *
   * @param event
   */
  onDrag = (event) => {
    this.isDragged = true;
    let target = event.target,
      pointer = event.currentPointer;
    if (target instanceof window.H.map.Marker) {
      target.setPosition(this.hereMap.current.screenToGeo(pointer.viewportX, pointer.viewportY));
    }
  };

  /**
   *
   */
  updateMap(){
    //draw existing zones

    if (this.oldZonePolygons.length > 0) this.hereMap.current.removeObjectsFromMap(this.oldZonePolygons);
    this.oldZonePolygons = [];

    if (this.props.zones.length > 0) {
      this.props.zones.map(zone => {
        if (zone.worldVertices === undefined || zone.worldVertices === null || zone.worldVertices.points.length === 0) return;
        let coords = zone.worldVertices.points;

        let lineString = new window.H.geo.LineString();
        for (let i = 0; i < coords.length; ++i) {
          //add a point from worldVertices.points, ie. a polygon vertice, to lineString
          //x is lat, y is lng, altitude is set to 100
            lineString.pushLatLngAlt(coords[i].x, coords[i].y, 100);
        }
        //lineString.pushLatLngAlt(coords[0].x, coords[0].y, 100);
        //create polygon from the linestring
        let polygon = new window.H.map.Polygon(lineString, {
          style: {
            fillColor: 'rgba(255, 152, 56, 0.5)',
            strokeColor: '#ff8614',
            lineWidth: 3
          }
        });

        //and add the object to HereMap
        this.oldZonePolygons.push(polygon);
        this.hereMap.current.addObjectToMap(polygon);

        //draw names as markers if need be
        if (this.props.showZoneNames === undefined || this.props.showZoneNames === true) {
          let svgMarkup = '<svg  width="24" height="24" xmlns="http://www.w3.org/2000/svg">' +
            '<text x="10" y="18" font-size="12pt" font-family="Arial" font-weight="bold" ' +
            'text-anchor="middle"  fill="white">${TEXT}</text></svg>';

          let zoneIcon = new window.H.map.Icon(
            svgMarkup.replace('${TEXT}', zone.zoneNumber.toString())),
            zoneMarker = new window.H.map.Marker(polygon.getBounds().getCenter(),
              {icon: zoneIcon});

          this.hereMap.current.addObjectToMap(zoneMarker);
          this.oldZonePolygons.push(zoneMarker);
        }
      });
    }

    //remove old polygon if there is any
    if (this.newZonePolygon && this.newZonePolygon  !== null && this.newZonePolygon  !== undefined) this.hereMap.current.removeObjectFromMap(this.newZonePolygon);

    //draw new zone polygon
    if (this.state.newZone === undefined || this.state.newZone === null || this.state.newZone.length === 0) return;

    //get the markers from newZone
    let markers = this.state.newZone;

    //update worldVertices
    let worldVertices = {"points": [] };

    let lineString = new window.H.geo.LineString();
    for (let i = 0; i < markers.length; ++i) {
      //add a point from worldVertices.points, ie. a polygon vertice, to lineString
      //get position from marker
      let point = markers[i].getPosition();
      lineString.pushLatLngAlt(point.lat, point.lng, point.alt);
      let vertex = {
        "x": point.lat,
        "y": point.lng
      }
      worldVertices.points.push(vertex);
    }

    //create polygon from the linestring
    this.newZonePolygon = new window.H.map.Polygon(lineString, {
      style: {
        fillColor: 'rgba(0, 255, 0, 0.5)',
        strokeColor: '#00ff00',
        lineWidth: 3
      }
    });
    this.hereMap.current.addObjectToMap(this.newZonePolygon);
  }

  /**
   *
   */
  componentDidMount() {
    this.updateMap();

    fetchMonitoredArea(this.props.monitoredAreaID)
    .then(data => this.setState({ monitoredArea: data },
      this.addMonitoredAreaMarker))
    .catch(error => this.setState({ monitoredArea: [{ name: error.toString() }] }));

    if (this.props.interactive !== undefined && this.props.interactive === false) return;
    this.hereMap.current.addEventListenerToMap('pointerup', this.onMapClick);
    this.hereMap.current.addEventListenerToMap('dragstart', this.onDragStart);
    this.hereMap.current.addEventListenerToMap('dragend', this.onDragEnd);
    this.hereMap.current.addEventListenerToMap('drag', this.onDrag);
  }

  /**
   *
   */
  addMonitoredAreaMarker = () => {
    let marker = new window.H.map.Marker({
      lat: this.state.monitoredArea.deviceLatitude,
      lng: this.state.monitoredArea.deviceLongitude
    });
    // add custom data to the marker
    marker.setData(this.state.monitoredArea);

    this.hereMap.current.addObjectToMap(marker);
    marker.addEventListener('tap', this.handleEvent);
  };

  /**
   *
   * @param evt
   */
  handleEvent = (evt) => {
    if (this.props.interactive !== undefined && this.props.interactive === true) return;
    // event target is the marker itself, group is a parent event target
    // for all objects that it contains
    let bubble = new window.H.ui.InfoBubble(evt.target.getPosition(), {
      // read custom data
      content: ReactDOMServer.renderToStaticMarkup(<MonitoredAreaMarkerPopUp
        name={evt.target.getData().name}
        id={evt.target.getData().id}
      />)
    });
    // show info bubble
    this.hereMap.current.addBubbleToUi(bubble);
  };

  /**
   *
   */
  componentWillUnmount() {
    if (this.props.interactive !== undefined && this.props.interactive === false) return;
    this.hereMap.current.removeEventListenerFromMap('pointerup', this.onMapClick);
    this.hereMap.current.removeEventListenerFromMap('dragstart', this.onDragStart);
    this.hereMap.current.removeEventListenerFromMap('dragend', this.onDragEnd);
    this.hereMap.current.removeEventListenerFromMap('drag', this.onDrag);
  }

  /**
   *
   * @param prevProps
   * @param prevState
   * @param snapshot
   */
  componentDidUpdate(prevProps, prevState, snapshot){
    this.updateMap();
  }

  /**
   *
   * @returns {*}
   */
  render() {
    const { t } = this.props;
    let map = null;
    if (this.state.monitoredArea === undefined || this.state.monitoredArea === null) {
      map = <SimpleHereMap width="100%" height="400px" ref={this.hereMap}
      />
    }else{
      map = <SimpleHereMap width="100%" height="400px" ref={this.hereMap}
                                        lat = { Number(this.state.monitoredArea.deviceLatitude)}
                                        lng={Number(this.state.monitoredArea.deviceLongitude)}
                                        zoom={19}
      />
    }
    return (
      <div>
        <div className="box-body">
        {map}
        </div>
        <div className="box-footer no-border">
          <label className="control-label fa fa-info-circle info-icon pull-right"
                 data-toggle="tooltip"
                 title={t('forms.tooltip.map')}/>
          {
            (this.props.interactive === undefined || this.props.interactive === true) ?
              <button
                style={{padding: '6px 50px'}}
                type="submit"
                className="btn btn-primary pull-left"
                onClick={this.clearNewZone}
              >{t('forms.erase')}</button>
              : null
          }
        </div>
      </div>
    )
  }
}

export default withNamespaces()(ZonesMap);