【问题标题】:How to customize HERE Maps Clusters如何自定义 HERE 地图集群
【发布时间】:2021-01-19 13:15:39
【问题描述】:

在我当前的混合应用程序中,我正在从使用Leaflet 及其Clusterer plugin 切换到使用HERE Maps v3 JavaScript API。对于习惯了 Leaflet 文档的清晰度的人来说,HERE Maps 文档有时会相当密集。然而,使用 HERE 的过程相当简单。但是,我真的很怀念 Leaflet 的一项功能:

在 Leaflet 中,当您向集群添加标记时,可以为每个标记分配自定义地图图钉。此外,您可以非常轻松地自定义用于表示集群本身的引脚。在 HERE 中,文档表明以下内容

 var dataPoints = [];
 //create an array for the clustered marker datapoints

 dataPoints.push(new H.clustering.DataPoint(43.25539364396839, -79.07150530321474));
 dataPoints.push(new H.clustering.DataPoint(43.255434408174246, -79.07175552759227));    
 dataPoints.push(new H.clustering.DataPoint(43.25557588373579, -79.07203209137799));
 dataPoints.push(new H.clustering.DataPoint(43.25567419706804, -79.07218354297491));  
 //populate that array

 var clusteredDataProvider = new H.clustering.Provider(dataPoints);
 //create a cluster data provider and assign it the freshly created data points

 var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
  //create a new layer that uses this provider

 map.addLayer(layer);
 //inject this layer into the map

这行得通。但是,它给我留下了三个未解决的问题

  • 如何使集群图标在点击时“爆炸”,以便地图自动放大到集群地图图钉并显示每个单独的地图图钉及其分配的图标?
  • 如何将单独的 PNG 图标图像分配给上述每个“数据点”?
  • 如何自定义实际集群地图图钉本身的外观?

【问题讨论】:

    标签: javascript leaflet here-api markerclusterer


    【解决方案1】:

    集群和噪声点在地图上用标记表示。除非另有配置,否则聚类提供程序使用带有权重信息的默认位图标记主题在地图上显示聚类和噪声点。您可以定义自己的自定义主题并将其作为主题属性传递给提供者。

    请在https://developer.here.com/documentation/maps/3.1.22.0/dev_guide/topics/clustering.html阅读此文档

    自定义主题在实现接口 H.clustering.ITheme 的对象中定义,如下代码所示。

        /**
     * Make clustering of markers with a custom theme
     *
     * Note that the maps clustering module https://js.api.here.com/v3/3.1/mapsjs-clustering.js
     * must be loaded to use the Clustering
     *
     * @param {H.Map} map A HERE Map instance within the application
     * @param {H.ui.UI} ui Default ui component
     * @param {Function} getBubbleContent Function returning detailed information about photo
     * @param {Object[]} data Raw data containing information about each photo
     */
    function startClustering(map, ui, getBubbleContent, data) {
      // First we need to create an array of DataPoint objects for the ClusterProvider
      var dataPoints = data.map(function(item) {
        // Note that we pass "null" as value for the "altitude"
        // Last argument is a reference to the original data to associate with our DataPoint
        // We will need it later on when handling events on the clusters/noise points for showing
        // details of that point
        return new H.clustering.DataPoint(item.latitude, item.longitude, null, item);
      });
    
      // Create a clustering provider with a custom theme
      var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
        clusteringOptions: {
          // Maximum radius of the neighborhood
          eps: 64,
          // minimum weight of points required to form a cluster
          minWeight: 3
        },
        theme: CUSTOM_THEME
      });
      // Note that we attach the event listener to the cluster provider, and not to
      // the individual markers
      clusteredDataProvider.addEventListener('tap', onMarkerClick);
    
      // Create a layer that will consume objects from our clustering provider
      var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
    
      // To make objects from clustering provider visible,
      // we need to add our layer to the map
      map.addLayer(layer);
    }
    
    // Custom clustering theme description object.
    // Object should implement H.clustering.ITheme interface
    var CUSTOM_THEME = {
      getClusterPresentation: function(cluster) {
        // Get random DataPoint from our cluster
        var randomDataPoint = getRandomDataPoint(cluster),
          // Get a reference to data object that DataPoint holds
          data = randomDataPoint.getData();
    
        // Create a marker from a random point in the cluster
        var clusterMarker = new H.map.Marker(cluster.getPosition(), {
          icon: new H.map.Icon(data.thumbnail, {
            size: {w: 50, h: 50},
            anchor: {x: 25, y: 25}
          }),
    
          // Set min/max zoom with values from the cluster,
          // otherwise clusters will be shown at all zoom levels:
          min: cluster.getMinZoom(),
          max: cluster.getMaxZoom()
        });
    
        // Link data from the random point from the cluster to the marker,
        // to make it accessible inside onMarkerClick
        clusterMarker.setData(data);
    
        return clusterMarker;
      },
      getNoisePresentation: function (noisePoint) {
        // Get a reference to data object our noise points
        var data = noisePoint.getData(),
          // Create a marker for the noisePoint
          noiseMarker = new H.map.Marker(noisePoint.getPosition(), {
            // Use min zoom from a noise point
            // to show it correctly at certain zoom levels:
            min: noisePoint.getMinZoom(),
            icon: new H.map.Icon(data.thumbnail, {
              size: {w: 20, h: 20},
              anchor: {x: 10, y: 10}
            })
          });
    
        // Link a data from the point to the marker
        // to make it accessible inside onMarkerClick
        noiseMarker.setData(data);
    
        return noiseMarker;
      }
    };
    
    
    /**
     * Boilerplate map initialization code starts below:
     */
    // Helper function for getting a random point from a cluster object
    function getRandomDataPoint(cluster) {
      var dataPoints = [];
    
      // Iterate through all points which fall into the cluster and store references to them
      cluster.forEachDataPoint(dataPoints.push.bind(dataPoints));
    
      // Randomly pick an index from [0, dataPoints.length) range
      // Note how we use bitwise OR ("|") operator for that instead of Math.floor
      return dataPoints[Math.random() * dataPoints.length | 0];
    }
    
    /**
     * CLICK/TAP event handler for our markers. That marker can represent either a single photo or
     * a cluster (group of photos)
     * @param {H.mapevents.Event} e The event object
     */
    function onMarkerClick(e) {
      // Get position of the "clicked" marker
      var position = e.target.getGeometry(),
        // Get the data associated with that marker
        data = e.target.getData(),
        // Merge default template with the data and get HTML
        bubbleContent = getBubbleContent(data),
        bubble = onMarkerClick.bubble;
    
      // For all markers create only one bubble, if not created yet
      if (!bubble) {
        bubble = new H.ui.InfoBubble(position, {
          content: bubbleContent
        });
        ui.addBubble(bubble);
        // Cache the bubble object
        onMarkerClick.bubble = bubble;
      } else {
        // Reuse existing bubble object
        bubble.setPosition(position);
        bubble.setContent(bubbleContent);
        bubble.open();
      }
    
      // Move map's center to a clicked marker
      map.setCenter(position, true);
    }
    
    // Step 1: initialize communication with the platform
    // In your own code, replace variable window.apikey with your own apikey
    var platform = new H.service.Platform({
      apikey: window.apikey
    });
    var defaultLayers = platform.createDefaultLayers();
    
    // Step 2: initialize a map
    var map = new H.Map(document.getElementById('map'), defaultLayers.vector.normal.map, {
      center: new H.geo.Point(50.426467222414374, 6.3054632497803595),
      zoom: 6,
      pixelRatio: window.devicePixelRatio || 1
    });
    // add a resize listener to make sure that the map occupies the whole container
    window.addEventListener('resize', () => map.getViewPort().resize());
    
    // 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));
    
    // Step 4: create the default UI component, for displaying bubbles
    var ui = H.ui.UI.createDefault(map, defaultLayers);
    
    /**
     * Merges given data with default bubble template and returns resulting HTML string
     * @param {Object} data Data holding single picture information
     */
    function getBubbleContent(data) {
      return [
        '<div class="bubble">',
          '<a class="bubble-image" ',
            'style="background-image: url(', data.fullurl, ')" ',
            'href="', data.url, '" target="_blank">',
          '</a>',
          '<span>',
            // Author info may be missing
            data.author ? ['Photo by: ', '<a href="//commons.wikimedia.org/wiki/User:',
              encodeURIComponent(data.author), '" target="_blank">',
              data.author, '</a>'].join(''):'',
            '<hr/>',
            '<a class="bubble-footer" href="//commons.wikimedia.org/" target="_blank">',
              '<img class="bubble-logo" src="data/wikimedia-logo.png" width="20" height="20" />',
              '<span class="bubble-desc">',
              'Photos provided by Wikimedia Commons are <br/>under the copyright of their owners.',
              '</span>',
            '</a>',
          '</span>',
        '</div>'
      ].join('');
    }
    
    // Step 5: request data that will be visualized on a map
    startClustering(map, ui, getBubbleContent, photos);
    

    这个工作示例请在 JsFiddle 上尝试:https://jsfiddle.net/gh/get/jquery/2.1.0/heremaps/maps-api-for-javascript-examples/tree/master/custom-cluster-theme

    这个例子的数据从https://heremaps.github.io/maps-api-for-javascript-examples/custom-cluster-theme/data/photos.js加载

    点击时集群图标如何“爆炸”你可以看到这个例子的来源https://tcs.ext.here.com/examples/v3.1/cluster_marker_spider。请注意加载的附加 js 模块“ClusterMarkerSpider-...”

    如何放大集群地图引脚的“爆炸” - 请注意“forEachDataPoint”(在 Spider 示例中)创建了所有标记,您可以简单地将其推送到某些 H.map.Group 并在放大后通过它的边界框到这个组。

    【讨论】:

      猜你喜欢
      • 2018-03-08
      • 2022-11-07
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      • 2019-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多