如何自定义 HERE 地图集群
How to customize HERE Maps Clusters
在我当前的 Hybrid 应用程序中,我正在从使用 Leaflet and its Clusterer plugin to using the HERE Maps v3 JavaScript API 进行切换。对于习惯了 Leaflet 文档清晰的人来说,HERE 地图文档有时可能相当密集。然而,使用 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 图标图像分配给上面的每个“数据点”?
- 如何自定义实际集群图 pin 本身的外观?
聚类和噪声点在地图上用标记表示。除非另行配置,否则聚类提供程序使用带有权重信息的默认位图标记主题在地图上显示聚类和噪声点。您可以定义自己的自定义主题并将其作为主题 属性.
传递给提供者
请在 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 并在缩放后- 通过其边界框进入该组。
在我当前的 Hybrid 应用程序中,我正在从使用 Leaflet and its Clusterer plugin to using the HERE Maps v3 JavaScript API 进行切换。对于习惯了 Leaflet 文档清晰的人来说,HERE 地图文档有时可能相当密集。然而,使用 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 图标图像分配给上面的每个“数据点”?
- 如何自定义实际集群图 pin 本身的外观?
聚类和噪声点在地图上用标记表示。除非另行配置,否则聚类提供程序使用带有权重信息的默认位图标记主题在地图上显示聚类和噪声点。您可以定义自己的自定义主题并将其作为主题 属性.
传递给提供者请在 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 并在缩放后- 通过其边界框进入该组。