Page 1 of 1

Ajaxmap grouping function

Posted: Mon Apr 13, 2015 8:13 am
by jmamy
Hello community,

I'd like to know, if one of you already developed an ajaxmap function which can group various items on the map, depending on the zoom level.
Something like "Client-side drawing of Points of Interest (Leaflet)" on the PTV code sample browser.

Thanks in advance.

Re: Ajaxmap grouping function

Posted: Fri Apr 17, 2015 1:10 pm
by AndreasJunghans
Hi there,

here's a clustering example with 1000 randomly-generated elements.

Regards,

Andreas

Code: Select all

<!DOCTYPE html>
<html>
  <head>
    <!--[if IE]><script type="text/javascript" src="webcomponent/script/excanvas.js"></script><![endif]-->
    <script type="text/javascript" src="webcomponent/script/qooxdoo/script/qx-transport.js"></script>
    <script type="text/javascript" src=".qxrpc"></script>
    <script type="text/javascript" src="webcomponent/script/map.js"></script>

    <script type="text/javascript">
      var map;
      var elements;
      
      function init() {
        var container =
          document.getElementById("mapContainer");
        map =
          new com.ptvag.webcomponent.map.Map(container);
        map.addEventListener("historyChanged", performClustering);
        window.onresize = function() {
          map.updateSize();
        };

        var minX = 4300000;
        var maxX = 4500000;
        var minY = 5400000;
        var maxY = 5700000;
        elements = [];
        for (var i = 0; i < 1000; ++i) {
          var x = minX + (maxX - minX)*Math.random();
          var y = minY + (maxY - minY)*Math.random();
          elements.push({x:x, y:y});
        }
      }

      function performClustering() {
        var vector = com.ptvag.webcomponent.map.vector;
        var CoordUtil = com.ptvag.webcomponent.map.CoordUtil;
        var VectorLayerClass = com.ptvag.webcomponent.map.layer.VectorLayer;
        var vectorLayer = map.getLayer("vector");
        vectorLayer.startBulkMode();
        vectorLayer.removeElement("markers");
        var markers = new vector.AggregateElement("markers");
        vectorLayer.addElement(markers);
        
        var zoom = map.getZoom();
        var gridWidthPix = 64;
        var clusterMarkerWidthPix = 24;
        var clusterMarkerHeightPix = 24;
        var suPerPixel = CoordUtil.getSmartUnitsPerPixel(zoom);
        var gridWidthSU = suPerPixel*gridWidthPix;
        var clusterMarkerWidthSU = suPerPixel*clusterMarkerWidthPix;
        var clusterMarkerHeightSU = suPerPixel*clusterMarkerHeightPix;
        var clusters = {};
        var elementCount = elements.length;
        for (var i = 0; i < elementCount; ++i) {
          var element = elements[i];
          var x = element.x;
          var y = element.y;
          var gridX = Math.floor(x/gridWidthSU);
          var gridY = Math.floor(y/gridWidthSU);
          var clusterKey = gridX + "_" + gridY;
          var cluster = clusters[clusterKey];
          if (cluster == null) {
            cluster = {
              members: [element],
              x: x, y: y,
              gridX: gridX, gridY: gridY
            };
            clusters[clusterKey] = cluster;
          } else {
            var oldMemberCount = cluster.members.length;
            var newMemberCount = oldMemberCount + 1;
            cluster.x = (cluster.x*oldMemberCount + x)/newMemberCount;
            cluster.y = (cluster.y*oldMemberCount + y)/newMemberCount;
            cluster.members.push(element);
          }
        }

        for (var i = 0; i < 2; ++i) {
          for (var key in clusters) {
            if (clusters.hasOwnProperty(key)) {
              var cluster = clusters[key];
              var memberCount = cluster.members.length;
              if (memberCount == 0) {
                continue;
              }
              var x = cluster.x;
              var y = cluster.y;
              var gridX = cluster.gridX;
              var gridY = cluster.gridY;
              var neighborKeys = [
                (gridX - 1) + "_" + (gridY - 1),
                (gridX - 1) + "_" + gridY,
                (gridX - 1) + "_" + (gridY + 1),
                gridX + "_" + (gridY - 1),
                gridX + "_" + (gridY + 1),
                (gridX + 1) + "_" + (gridY - 1),
                (gridX + 1) + "_" + gridY,
                (gridX + 1) + "_" + (gridY + 1)
              ];
              var neighborKeyCount = neighborKeys.length;
              for (var j = 0; j < neighborKeyCount; ++j) {
                var neighborKey = neighborKeys[j];
                var neighbor = clusters[neighborKey];
                if (neighbor == null) {
                  continue;
                }
                var neighborMemberCount = neighbor.members.length;
                if (neighborMemberCount == 0) {
                  continue;
                }
                var nx = neighbor.x;
                var dx = nx - x;
                if (Math.abs(dx) < clusterMarkerWidthSU) {
                  var ny = neighbor.y;
                  var dy = ny - y;
                  if (Math.abs(dy) < clusterMarkerHeightSU) {
                    if (memberCount >= neighborMemberCount) {
                      var newCount = memberCount + neighborMemberCount;
                      cluster.x = (cluster.x*memberCount +
                                   neighbor.x*neighborMemberCount)/newCount;
                      cluster.y = (cluster.y*memberCount +
                                   neighbor.y*neighborMemberCount)/newCount;
                      cluster.members = cluster.members.concat(
                        neighbor.members);
                      neighbor.members = [];
                    }
                  }
                }
              }
            }
          }
        }
        
        for (var key in clusters) {
          if (clusters.hasOwnProperty(key)) {
            var cluster = clusters[key];
            var memberCount = cluster.members.length;
            if (memberCount == 0) {
              continue;
            }
            if (memberCount == 1) {
              var marker = new vector.ImageMarker(cluster.x, cluster.y);
              markers.addElement(marker);
            } else {
              var circle = new vector.Circle(cluster.x, cluster.y,
                "#FF0000", 24);
              markers.addElement(circle);
              var text = new vector.Text(cluster.x, cluster.y,
                "#FFFFFF", 9,
                VectorLayerClass.ALIGN_MID_HORIZ +
                VectorLayerClass.ALIGN_MID_VERT,
                "" + memberCount);
              markers.addElement(text);
            }
          }
        }
        
        vectorLayer.endBulkMode();
      }
      
      qxp.dev.log.Logger.ROOT_LOGGER.setMinLevel(qxp.dev.log.Logger.LEVEL_INFO);
      qxp.dev.log.Logger.getClassLogger(qxp.core.Init).setMinLevel(qxp.dev.log.Logger.LEVEL_ERROR);
    </script>
  </head>

  <body onload="init()">
    <div id="mapContainer"
      style="position:absolute;left:0;top:0;right:0;bottom:0">
    </div>
  </body>
</html>

Re: Ajaxmap grouping function

Posted: Fri Apr 17, 2015 1:34 pm
by jmamy
Exactly what I was looking for!
Thanks you very much.