Search code examples
javascripthere-apiheremaps

How to get MapMarker inside a polygon with clustering active


We're looking for a way to get our data points that are inside a polygon using JavaScript HereMaps API

We're adding 4 datapoints to a ClusterLayer / ClusterProvider and a polygon to the map. 3 of the 4 points are within the drawn Polygon (data of the points: a, b, d). Point with data = c is not within the polygon (see jsfiddle)

We tried to use map.getObjectsWithin but this functions only returns the polygon. We assume that this is caused by the different layers.

What's the best way to get the datapoints which are in the bounds of the polygon? We try to avoid additional dependencies to solve this issue.

quick demo: http://jsfiddle.net/4dno0gu2/59/

We found this question, but there wasn't any example, no activity for a long time and no solution. HereMap getObjectsWithin does not show objects in LocalObjectProvider


Solution

  • Yeah the method getObjectsWithin doesn't work for ObjectProvider although this implemented in JS API but simple this functionality is not a public method, apologies for it.

    Short description of workaround:

    1. obtain the bounding box of the polygon
    2. request all types of objects where you interested in for this bounding box from the provider (requestOverlays, requestSpatials, requestMarkers etc.) - in your case requestMarkers
    3. filter out all objects which doesn't intersect with the polygon

    Code:

    /**
     * Adds a polygon to the map
     *
     * @param  {H.Map} map      A HERE Map instance within the application
     */
    function addPolygonToMap(map) {
      var geoStrip = new H.geo.LineString(
        [48.8, 13.5, 100, 48.4, 13.0, 100, 48.4, 13.5, 100]
      );
      
      var polygon = new H.map.Polygon(geoStrip, {
          style: {
            strokeColor: '#829',
            lineWidth: 8
          },
          data: 'polygon'
        });
      //map.addObject(polygon);
      return polygon;
    }
    
    function logObjectsInPolygon(map, polygon){
     var geoPolygon = polygon.getGeometry();
     map.getObjectsWithin(geoPolygon, (o) => {
                    console.log('found mapObjects: '+ o.length);
            o.forEach(x=>{
                if(typeof x.getData === 'function'){
                console.log(x.getData());
                }
            });
      });
    }
    
    function isPointInPolygon(testPoint, polygPoints) {
        let result = false;
        let j = polygPoints.length - 1;
        for(i=0,len=j+1; i<len; i++){
                let p = polygPoints[i];
            let lP = polygPoints[j];
            if(p.y < testPoint.y && lP.y >= testPoint.y || lP.y < testPoint.y && p.y >= testPoint.y){
                if((p.x + (testPoint.y - p.y) / (lP.y - p.y) * (lP.x - p.x)) < testPoint.x){
                    result = !result;
                }
            }
            j = i;
         }
       return result;
    }
    
    
    /**
     * Boilerplate map initialization code starts below:
     */
    
    //Step 1: initialize communication with the platform
    var platform = new H.service.Platform({
      apikey: 'H6XyiCT0w1t9GgTjqhRXxDMrVj9h78ya3NuxlwM7XUs',
      useCIT: true,
      useHTTPS: true
    });
    var defaultLayers = platform.createDefaultLayers();
    
    //Step 2: initialize a map - this map is centered over Europe
    var map = new H.Map(document.getElementById('map'),
      defaultLayers.raster.normal.map,{
      center: {lat:48.5, lng:13.45},
      zoom: 10
    });
    
    //Step 3: make the map interactive
    // MapEvents enables the event system
    // Behavior implements default interactions for pan/zoom (also on mobile touch environments)
    var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    
    // Create the default UI components
    var ui = H.ui.UI.createDefault(map, defaultLayers);
    
    //this marker should go to clusters if there is more data points
    var dataPoints = [];
    dataPoints.push(new H.clustering.DataPoint(48.5, 13.45,{}, 'a'));
    dataPoints.push(new H.clustering.DataPoint(48.5001, 13.45,{}, 'b'));
    dataPoints.push(new H.clustering.DataPoint(48.5002, 13.51,{}, 'c')); // not in Polygon
    dataPoints.push(new H.clustering.DataPoint(48.53, 13.45,{}, 'd'));
    var clusteredDataProvider = new H.clustering.Provider(dataPoints);
    var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
    map.addLayer(layer);
    
    // createPolygon to select clustered (noise) points 
    var polygon = addPolygonToMap(map);
    let extPoly = polygon.getGeometry().getExterior();
    let seqPointsPoly = [];
    extPoly.eachLatLngAlt((lat, lng, alt, idy) => {
        seqPointsPoly.push( {y: lat, x: lng});
    });
    
    console.log("seqPointsPoly:", seqPointsPoly);
    
    map.addEventListener("tap", (e) => {
        let pBbox = polygon.getBoundingBox();
      let arrPnts = clusteredDataProvider.requestMarkers(pBbox);
      for(let i=0,len=arrPnts.length; i<len; i++){
        let m = arrPnts[i];
        let p = {y: m.getGeometry().lat, x: m.getGeometry().lng};
        let clustData = m.getData();
        if(!clustData.getData){
            console.log("cluster: is in polygon:", isPointInPolygon(p, seqPointsPoly));
        } else if(clustData.getData){
            console.log("nois: is in polygon:", clustData.getData(), m.getGeometry(), isPointInPolygon(p, seqPointsPoly));
        }else{
            console.log("unknown type");
        }
        
      }
        console.log("clusteredDataProvider:", pBbox, clusteredDataProvider.requestMarkers(pBbox));
        // Our expected logging is: points a, b, d
        //logObjectsInPolygon(map, polygon);
    });
    

    Worked example (tap on map to start processing): http://jsfiddle.net/m1ey7p2h/1/