Superimposed Pins on Bing Maps - How to display metadata of both or move pins apart at a certain zoom - maps

I am generating an HTML/Javascript file from an Access365 database which plots pins on a Bing Map. Each of the pins has associated metadata about the location it is pinning which is viewed by clicking the pin. However, there can be pins which are at exactly the same location (lat and long) and only the metadata for the top pin is available.
How do I either show the combined metadata or have the pins move apart a bit say when the the user mouses over them? Does anyone know how to do that? I've gone round in circles looking at the MS documentation and can't find anything to help.
P.S. There is also a clusterLayer. If that needs removing to solve the problem that's Ok but it would be better if it could stay.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<!-- Reference to the Bing Maps SDK -->
<script type='text/javascript'
src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=[zzz]'
async defer></script>
<script type='text/javascript'>
function GetMap()
{
var map = new Microsoft.Maps.Map('#myMap', {center: new Microsoft.Maps.Location(53.50632, -7.2714), zoom:8});
var ourBlue = 'rgb(108, 162, 212)';
//Create an infobox at the center of the map but don't show it.
infobox = new Microsoft.Maps.Infobox(map.getCenter(), {
visible: false
});
//Assign the infobox to a map instance.
infobox.setMap(map);
var theLocations = [3];
var thePins = [3];
theLocations[0] = new Microsoft.Maps.Location(53.41, -7.1);
theLocations[1] = new Microsoft.Maps.Location(53.42, -7.1);
theLocations[2] = new Microsoft.Maps.Location(53.43, -7.1);
for (var i = 0; i < theLocations.length; i++){
var pin = new Microsoft.Maps.Pushpin(theLocations[i]);
pin.metadata = {
title: 'Pin ' + i, description: 'Description for pin' + i
};
Microsoft.Maps.Events.addHandler(pin, 'click', pushpinClicked);
Microsoft.Maps.Events.addHandler(pin, 'mouseover', splitOverlap);
Microsoft.Maps.Events.addHandler(pin, 'mouseout', function (e) {
e.target.setOptions({ color:'purple' });
});
thePins[i] = pin; //add pin to array of pins
}
Microsoft.Maps.loadModule("Microsoft.Maps.Clustering", function(){
clusterLayer = new Microsoft.Maps.ClusterLayer(thePins);
map.layers.insert(clusterLayer);
});
}
function splitOverlap(e) {
var ourBlue = 'rgb(108, 162, 212)';
e.target.setOptions({color:ourBlue});
}
}
function pushpinClicked(e) {
//Make sure the infobox has metadata to display.
if (e.target.metadata) {
//Set the infobox options with the metadata of the pushpin.
infobox.setOptions({
location: e.target.getLocation(),
title: e.target.metadata.title,
description: e.target.metadata.description,
visible: true
});
}
}
</script>
<script type='text/javascript' src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=Arvr3LDJsmNB-2OGHl_egpbP9RbwsYKGKrktnPBC06G38T9q3CzsfmwK6GNoW7R_' async defer></script>
</head>
<body>
<div id="myMap" style="position:relative;width:600px;height:400px;"></div>
</body>
</html>

When you have clusters and you want to see individual metadate for items within the cluster there are two common approaches:
Have a popup that shows the first location metadata and buttons to step/page/tab through each item in the cluster.
Use the spider cluster visualization: https://bingmapsv8samples.azurewebsites.net/#Clustering_SpiderClusters

Related

HERE maps - how to get all visible clusters?

I want to get array of currently visible clusters and then get each point data. I create method in React and it seems that method in theme for getClusterPresentation returns all possible clusters for all map zooms. How to get clusters data? This is my code:
const dataPoints = points.map(
point => new H.clustering.DataPoint(point.lat, point.lng, undefined, point),
);
const clusteredDataProvider = new H.clustering.Provider(dataPoints, {
clusteringOptions: {
eps: 32,
minWeight: 2,
},
});
const defaultTheme = clusteredDataProvider.getTheme();
clusteredDataProvider.setTheme({
getClusterPresentation: cluster => {
const clusterMarker = defaultTheme.getClusterPresentation(cluster);
return clusterMarker;
},
getNoisePresentation: noisePoint => {},
});
const layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
map.addLayer(layer);
Please check the below code related to Marker Clustering. And you check same example in our Guide
/**
* Display clustered markers on a map
*
* 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 {Object[]} data Raw data that contains airports' coordinates
*/
function startClustering(map, data) {
// First we need to create an array of DataPoint objects,
// for the ClusterProvider
var dataPoints = data.map(function (item) {
return new H.clustering.DataPoint(item.latitude, item.longitude);
});
// Create a clustering provider with custom options for clusterizing the input
var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
clusteringOptions: {
// Maximum radius of the neighbourhood
eps: 32,
// minimum weight of points required to form a cluster
minWeight: 2
}
});
// Create a layer tha will consume objects from our clustering provider
var clusteringLayer = new H.map.layer.ObjectLayer(clusteredDataProvider);
// To make objects from clustering provder visible,
// we need to add our layer to the map
map.addLayer(clusteringLayer);
}
/**
* Boilerplate map initialization code starts below:
*/
// 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(30.789, 33.790),
zoom: 2,
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);
// Step 5: cluster data about airports's coordinates
// airports variable was injected at the page load
startClustering(map, airports);
#map {
width: 95%;
height: 450px;
background: grey;
}
#panel {
width: 100%;
height: 400px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Marker Clustering</title>
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
<link rel="stylesheet" type="text/css" href="demo.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<link rel="stylesheet" type="text/css" href="../template.css" />
<script type="text/javascript" src='../test-credentials.js'></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-clustering.js"></script>
<script type="text/javascript" src="./data/airports.js"></script>
</head>
<body id="markers-on-the-map">
<div class="page-header">
<h1>Marker Clustering</h1>
<p>Cluster multiple markers together to better visualize the data</p>
</div>
<p>This example displays a map showing the distribution of
airports across the world. The locations were obtained by using
the OpenFlights Airport Database.
Instead of adding a marker for each location, the data has been clustered,
and individual airports are only shown at higher zoom levels.</p>
<div id="map"></div>
<h3>Code</h3>
<p>Marker clustering requires the presence of the <code>mapsjs-clustering</code> module of the API.
The <code>H.clustering.Provider</code> class is used to load in data points and prepare them for clustering.
The result is added to the map as an additional layer using the <code>map.addLayer()</code> method.</p>
<script type="text/javascript" src='demo.js'></script>
</body>
</html>

Bing Map V8 Cluster Pass real time data

I am just starting with Bing map. Gone through few examples in official documentation.
Example from Bing map V8 official documentation
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script type='text/javascript'
src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap' async defer></script>
<script type="text/javascript">
var map, clusterLayer;
function GetMap() {
map = new Microsoft.Maps.Map('#myMap',{
credentials: 'Your Bing Maps Key',
zoom: 3
});
Microsoft.Maps.loadModule("Microsoft.Maps.Clustering", function () {
//Generate 3000 random pushpins in the map view.
var pins = Microsoft.Maps.TestDataGenerator.getPushpins(3000, map.getBounds());
//Create a ClusterLayer with options and add it to the map.
clusterLayer = new Microsoft.Maps.ClusterLayer(pins, {
clusteredPinCallback: customizeClusteredPin
});
map.layers.insert(clusterLayer);
});
}
function customizeClusteredPin(cluster) {
//Add click event to clustered pushpin
Microsoft.Maps.Events.addHandler(cluster, 'click', clusterClicked);
}
function clusterClicked(e) {
if (e.target.containedPushpins) {
var locs = [];
for (var i = 0, len = e.target.containedPushpins.length; i < len; i++) {
//Get the location of each pushpin.
locs.push(e.target.containedPushpins[i].getLocation());
}
//Create a bounding box for the pushpins.
var bounds = Microsoft.Maps.LocationRect.fromLocations(locs);
//Zoom into the bounding box of the cluster.
//Add a padding to compensate for the pixel area of the pushpins.
map.setView({ bounds: bounds, padding: 100 });
}
}
</script>
</head>
<body>
<div id="myMap" style="position:relative;width:600px;height:400px;"></div>
</body>
</html>
In above bing map cluster example how to replace the TestDataGenerator data with realtime data JSON like below
mapData = [{"Name":"Point: 0","Latitude":22.0827,"Longitude":80.2707},
{"Name":"Point: 1","Latitude":24.0827,"Longitude":80.2707},
{"Name":"Point: 2","Latitude":26.0827,"Longitude":80.2707},
{"Name":"Point: 3","Latitude":28.0827,"Longitude":80.2707},
{"Name":"Point: 4","Latitude":20.0827,"Longitude":80.2707},
{"Name":"Point: 5","Latitude":22.0827,"Longitude":82.2707},
{"Name":"Point: 6","Latitude":30.0827,"Longitude":80.2707},
{"Name":"Point: 7","Latitude":22.0827,"Longitude":84.2707},
{"Name":"Point: 8","Latitude":32.0827,"Longitude":84.2707},
{"Name":"Point: 9","Latitude":18.0827,"Longitude":80.2707}];
When I pass above object in ClusterLayer I am getting following error
Uncaught TypeError: i[t].getLocation is not a function(…)
You have to loop through your data and turn it into pushpins. Here's a code sample:
var pins = [];
for(var i = 0;i < mapData.length;i++){
var pin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(mapData[i].Latitude, mapData[i].Longitude));
//Store the original data object in the pushpins metadata so that you can access other properties like Name.
pin.metedata = mapData[i];
pins.push(pin);
}
//Now "pins" is an array of pushpins. Add them to the map or to the clustering layer.

geolocation with inteactiv markers

I'm attempting to create a mobile app with the following purposes:
1. display geolocation with watchpostion so that the location marker moves with you across the map.
2. display markers with interactive* infowindows along the map wich are at solid locations.
3. have direction availability so that it shows the route from geolocation to position of the marker of your choice.
* with interactive I mean I wish for the marker to contain an import from database function. The database will display the current price of the Product of interest available on the various markerpoints within the chosen zoom-area and highlight the marker with the lowest price so that you may select to gt direction to the location with the best offer.
I have been able to create the watchposition() map itself and creating an infowindow displaying on a fixed map. However those two are working in to individual Projects, but when I try to put them together the preview goes blank. How to make them work together? I'm using myeclipse 2015 stable version
Here is the code for geolocation I wish to add markers in:
<!DOCTYPE HTML>
<html>
<head>
<title>Geolocation watchPosition()</title>
<!-- for mobile view -->
<meta content='width=device-width, initial-scale=1' name='viewport'/>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js" > </script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3&sensor=false&language=en"></script>
<script type="text/javascript">
// you can specify the default lat long
var map,
currentPositionMarker,
mapCenter = new google.maps.LatLng(57.736585899999994, 12.956377999999999),
map;
// change the zoom if you want
function initializeMap()
{
map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 18,
center: mapCenter,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
}
function locError(error) {
// tell the user if the current position could not be located
alert("The current position could not be found!");
}
// current position of the user
function setCurrentPosition(pos) {
currentPositionMarker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(
pos.coords.latitude,
pos.coords.longitude
),
title: "Current Position"
});
map.panTo(new google.maps.LatLng(
pos.coords.latitude,
pos.coords.longitude
));
}
function displayAndWatch(position) {
// set current position
setCurrentPosition(position);
// watch position
watchCurrentPosition();
}
function watchCurrentPosition() {
var positionTimer = navigator.geolocation.watchPosition(
function (position) {
setMarkerPosition(
currentPositionMarker,
position
);
});
}
function setMarkerPosition(marker, position) {
marker.setPosition(
new google.maps.LatLng(
position.coords.latitude,
position.coords.longitude)
);
}
function initLocationProcedure() {
initializeMap();
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(displayAndWatch, locError);
} else {
// tell the user if a browser doesn't support this amazing API
alert("Your browser does not support the Geolocation API!");
}
}
// initialize with a little help of jQuery
$(document).ready(function() {
initLocationProcedure();
});
</script>
<!-- display the map here, you can changed the height or style -->
<div id="map_canvas" style="height:25em; margin:0; padding:0;"></div>

polymer 1.0 .. importing a catalog element with a custom element throws an error

Had created and used my custom polymer element which is a table. Now, I want to use the check box element from their catalog in my table.
However, I keep getting this error when I reference the check box html file in my index page:
DuplicateDefinitionError: a type with name 'dom-module' is already
registered
This is how I have created my custom element:
<!-- Imports polymer -->
<link rel="import" href="polymer/polymer.html">
<script src="underscore-min.js"></script>
<!-- Defines element markup -->
<dom-module id="custom-table" >
<template>
<style>
ul {list-style-type:none; display:block}
ul li {display:inline; float:left; padding:20px; width:1.5em; border-bottom:1px solid #eee}
</style>
<h2>{{title}}</h2>
<table id="dataTable">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</template>
</dom-module>
<!-- Registers custom element -->
<script>
Polymer({
is: 'custom-table',
// Fires when an instance of the element is created
created: function() {
},
// Fires when the local DOM has been fully prepared
ready: function() {
var context= this;
this.pageNo=0;
this.totalPages=0;
// set the default paging size:
if(this.page== null|| this.page==undefined)
this.page=10;
// delegate the change selection handler to the table body
this.$.tableBody.addEventListener("click",function(e){
if(e.target && e.target.nodeName == "INPUT") ;
{
context.changeSelection(e.target);
}
});
},
// Fires when the element was inserted into the document
attached: function() {},
// Fires when the element was removed from the document
detached: function() {},
// Fires when an attribute was added, removed, or updated
attributeChanged: function(name, type) {
alert("changed");
},
loadData: function(columns,data){
this.data = data;
// add the selected property to the values
for(var i=0;i<this.data.length; i++) { this.data[i].Selected = false;}
this.filteredData=this.data;
this.columns = columns;
//initialize the filteredData
this.filteredData=data;
// calculate the total number of pages
this.totalPages= Math.ceil(data.length/this.page);
this.drawTableHeader();
_.defer(this.applyFilters,this);
_.defer(this.drawTableBody,this);
},
drawTableHeader:function(){
var columns = this.columns;
// load the header
var headTr = document.createElement('tr');
//add a blank header for the check box;
var th=document.createElement('th');
headTr.appendChild(th);
for(var i = 0; i<columns.length ;i++)
{
var td=document.createElement('th');
// if the column is sortable then add the event listener for sorting it
if(columns[i].Sortable)
{
td.addEventListener("click",function(){ this.sortBy(columns[i].Title); });
}
td.innerText = columns[i].Title;
headTr.appendChild(td);
}
this.$.tableHead.appendChild(headTr);
},
drawTableBody: function(context){
// this is a defered function
var context = context;
// get the number of items according to the current page number
var pageItems= context.filteredData.slice((context.page*context.pageNo),((context.page*context.pageNo)+context.page));
console.log(pageItems);
// print the page items
for(var i=0; i < pageItems.length; i++)
{
var currItem = pageItems[i];
var tr= document.createElement("tr");
// add the check box first
var checkbox= document.createElement("input");
checkbox.type="checkbox";
checkbox.checked=pageItems[i].Selected;
var ItemId = currItem.Id;
checkbox.setAttribute("data-ItemId",ItemId-1);
var td=document.createElement('td');
td.appendChild(checkbox);
tr.appendChild(td);
// for every column specified add a column to it
for(var j = 0; j< context.columns.length; j++)
{
var td=document.createElement("td");
td.innerText= pageItems[i][context.columns[j].Title];
tr.appendChild(td);
}
//append the row to the table;
context.$.tableBody.appendChild(tr);
} // end for i
},
applyFilters:function(context){
if(context.filter)
{
alert("filterApplied");
}
},
changeSelection:function(checkbox){
var ItemId = checkbox.getAttribute("data-ItemId");
this.data[ItemId].Selected= checkbox.checked;
console.log(this.data[ItemId]);
},
properties:{
title :String,
columns:Array,
data:Array,
page:Number,
filters:Object,
Selectable:Boolean
}
});
</script>
and here is what my index page looks like:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><my-repo></title>
<!-- Imports polyfill -->
<script src="webcomponents-lite.min.js"></script>
<!-- Imports custom element -->
<link rel="import" href="my-element.html">
<link rel="import" href="bower_components/paper-checkbox/paper-checkbox.html">
</head>
<body unresolved>
<!-- Runs custom element -->
<custom-table title="This is data table"></custom-table>
<script>
document.addEventListener("WebComponentsReady",function(){
var data = [{'Id':1,'firstName':'aman',age:25},{'Id':2,'firstName':'gupta',age:25}];
var cols = [{Title:'firstName',Sortable:true},{Title:'age',Sortable:false}];
var a = document.querySelector('my-element');
a.loadData(cols,data);
});
</script>
</body>
</html>
I've just started out with polymer and I'm not quite sure what's going on here..
Thank you in advance :)
I got what the problem is..
My custom element was referencing a different Polymer.html file.
Silly me :D
I'm using Polymer Starter Kit Yeoman generator on Windows and I had the same problem:
Error: DuplicateDefinitionError: a type with name 'dom-module' is already registered
This error is triggered in Firefox console. Chrome works fine.
The components created with the generator (example: yo polymer:el my-element) have this polymer.html import:
<link rel="import" href="..\..\bower_components/polymer/polymer.html">
The base path is described with "backslash".
In some custom polymer elements I created by myself, I imported polymer.html with:
<link rel="import" href="../../bower_components/polymer/polymer.html">
And I think this lead to a duplication of some kind. To solve the problem, I just changed all automatically created imports, using only forward slashes /.
Hope this helps someone.

Google Map v3 Ground Overlay?

I have spent two days puzzling this and failed. Any assistance will be greatly appreciated.
I need a map centered on -18.975750, 32.669184 in a canvas of 1500px x 900px. I then to need to overlay coverage PNGs (obtained form www.heywhatsthat.com) with set code- transparency.
I have eventually arrived at the following code and it fails. I would like to add more than one PNG bound by it's co-ords, but cant even get one to work.
<script src="http://maps.google.com/maps?file=api&v=3&key=AIzaSyAGbZjXr2wr7dT2P3O5pNo5wvVF3JiaopU&sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
if (GBrowserIsCompatible()) {
var map = new google.maps.MAP(document.getElementById("map_canvas"));
map.setCenter(new GLatLng(-18.975750, 32.669184), 13);
map.setUIToDefault();
var imageBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-19.000417,30.999583),
new google.maps.LatLng(-17.999583,32.000417));
var oldmap = new google.maps.GroundOverlay(
"http://www.earthstation.mobi/cloakpS19E031.png",imageBounds);
oldmap.setMap(map);
}
</script>
</head>
<body onload="initialize()" onunload="GUnload()">
<div id="map_canvas" style="width: 1500px; height: 900px"></div>
</body>
</html>
What am I mssing and please point me in the right direction add multiple png overlays with transparency in the options.
Thanks
Brian
Zimbabwe
You have a lot of issues with your code. It looks like you're trying to migrate from V2 to V3, and you still have V2 methods and objects in your code. You're also not loading the JS APi correctly when you call in the of your HTML.
Here is functional code that displays the overlay using the V3 API, but it looks like the original center coordinates that you used do not place the map at the center of the overlay (you'll need to figure that out yourself). I've added comments where relevant so that you can see where you went astray. Note the call to the API library in the first script tag.
<script src="http://maps.googleapis.com/maps/api/js?key=AIzaSyAGbZjXr2wr7dT2P3O5pNo5wvVF3JiaopU&sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
function initialize() {
//You don't need to use GBrowserIsCompatible, it's only for V2 of the API
//if (GBrowserIsCompatible()) {
//You need to set up options for the map that you reference when you
//instantiate the map object
var myOptions = {
center: new google.maps.LatLng(-18.975750, 32.669184),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//Your code references google.maps.MAP; it's google.maps.Map
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var imageBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-19.000417,30.999583),
new google.maps.LatLng(-17.999583,32.000417));
var oldmap = new google.maps.GroundOverlay(
"http://www.earthstation.mobi/cloakpS19E031.png",imageBounds);
oldmap.setMap(map);
//} <--Your code was missing this closing bracket for the conditional
//But the conditional is not even needed, since GBrowserCompatible is a V2 thing
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width: 1500px; height: 900px"></div>
</body>
</html>

Resources