The Bing Maps consumer site (bing.com/maps) has a lot of additional business and POI data that isn't available in the developer API due to restrictions from the data providers. So, you won't see all the same content everywhere. When it comes to the developer API, rarely has anyone asked for detailed business/points of interest on the map as they can easily clutter the map or show competitors. In fact, I think this is only the third time in the past ten years I've come across this request, and I interact with hundreds, if not thousands of users of Bing and Azure Maps every year. That said, there is a lot more businesses/POI data in Bing Maps developer services that don't appear on the map, it just requires a bit of work. However, to prevent people from scraping this data, the API's limit the number of results that can be retrieved by the service to 25. We can get around this however by breaking the map view up into a grid and making multiple searches.
To pull this in you would need to make a request for point of interest data as the map moves from the point of interest data source using the spatial data services, and then load them as points on the map. This means that you would need to decide how you want to display them on the map and write the code to customize this.
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script>
var map, sessionKey, layer, infobox;
var gridSizeDegrees = 0.001; //Approximately 100 meters at the equator
var zoomLimit = 17; //Zoom level user needs to be zoomed into for POI data to be displayed. In cities, anything less than 17 would likely run into service errors due to the volume of requests.
//Cache data by bounding box to make for faster querying.
var poiCache = {};
//https://learn.microsoft.com/en-us/bingmaps/spatial-data-services/public-data-sources/pointsofinterest
var sdsDataSourceUrl = 'https://spatial.virtualearth.net/REST/v1/data/Microsoft/PointsOfInterest?spatialFilter=bbox({bbox})&$top=25&$format=json&key={key}';
function GetMap() {
map = new Microsoft.Maps.Map('#myMap', {});
//Create an infobox to display content for each result.
infobox = new Microsoft.Maps.Infobox(map.getCenter(), { visible: false });
infobox.setMap(map);
//Create a layer for the results.
layer = new Microsoft.Maps.Layer();
map.layers.insert(layer);
//Add a click event to the layer to show an infobox when a pushpin is clicked.
Microsoft.Maps.Events.addHandler(layer, 'click', function (e) {
var m = e.target.metadata;
infobox.setOptions({
title: m.DisplayName,
description: `Entity Type: ${EntityTypeNameTable[m.EntityTypeID]}<br/>${m.AddressLine}<br/>${m.Locality}`,
location: e.target.getLocation(),
visible: true
});
});
//Get session key from map to make non-billable requests to the REST APIs.
map.getCredentials((sessionId) => {
sessionKey = sessionId;
//Add an event handler for when the map moves.
Microsoft.Maps.Events.addHandler(map, 'viewchangeend', getNearByLocations);
//Trigger an initial search.
getNearByLocations();
});
}
function getNearByLocations() {
//Hide infobox.
infobox.setOptions({ visible: false });
//Remove any existing data from the layer.
layer.clear();
var zoom = map.getZoom();
//Make sur ethe user is zoomed in, otherwise there will be too much data to load.
if(zoom >= zoomLimit) {
//Calculate grid cells to fill the map view.
var gridCells = getGrid();
//Loop through the grid cells and create requests and load data. Use promises to allow async parrallel requests.
var requests = [];
for(var i = 0;i < gridCells.length; i++){
var bbox = gridCells[i];
//Check the cache to see if we alreayd loaded data for this bounding box.
var cachedData = poiCache[bbox];
if(cachedData) {
layer.add(cachedData);
} else {
//Add a place holder in the cache to ensure quick movements doesn't result in multiple requests for the same cell.
poiCache[bbox] = [];
requests.push(bbox);
}
}
document.getElementById('output').innerHTML = `Number of points on the map ${layer.getPrimitives().length}`;
//If the user is zoomed out, only load data from the cache.
//Request data synchronously to reduce chances of exceeding QPS limits of Bing Maps account.
if(requests.length > 0){
makeRequest(requests, 0);
}
}
}
function makeRequest(requests, idx) {
var bbox = requests[idx];
var requestUri = sdsDataSourceUrl.replace('{bbox}', bbox).replace('{key}', sessionKey);
fetch(requestUri)
.then(res => res.json())
.then(response => {
if(response && response.d && response.d.results && response.d.results.length > 0){
var results = [];
//Convert the results to Bing Maps points.
for(var j = 0;j < requests.length; j++){
var r = response.d.results[j];
if(r && typeof r.Latitude == 'number' && typeof r.Longitude == 'number') {
var pin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(r.Latitude, r.Longitude), { /* Optionally customize pin based on data. */ });
//Store the metadata for the result.
pin.metadata = r;
results.push(pin);
}
}
//Cache the results and add to the map.
poiCache[bbox] = results;
layer.add(results);
console.log(results.length)
}
//Request next set of data.
idx++;
if(idx < requests.length) {
makeRequest(requests, idx);
}
document.getElementById('output').innerHTML = `Number of points on the map ${layer.getPrimitives().length}`;
});
}
function getGrid() {
var bounds = map.getBounds();
var west = bounds.getWest();
var east = bounds.getEast();
var south = bounds.getSouth();
var north = bounds.getNorth();
//Calculate row and column indices of grid.
var startCol = Math.round(west / gridSizeDegrees) - 1;
var endCol = Math.round(east / gridSizeDegrees) + 1;
var startRow = Math.round(south / gridSizeDegrees) - 1;
var endRow = Math.round(north / gridSizeDegrees) + 1;
//Round number of significant digits to reduce floating point numbers resulting in duplicate areas.
west = Math.round(west*1000)/1000;
south = Math.round(south*1000)/1000;
east = Math.round(east*1000)/1000;
north = Math.round(north*1000)/1000;
//Loop over the bounding area and calculate smaller grid cells.
var cells = [];
for(var i = startCol; i < endCol; i++) {
for(var j = startRow; j < endRow; j++) {
cells.push(`${roundSigDigits(j * gridSizeDegrees)},${roundSigDigits(i * gridSizeDegrees)},${roundSigDigits((j + 1) * gridSizeDegrees)},${roundSigDigits((i + 1) * gridSizeDegrees)}`);
}
}
return cells;
}
function roundSigDigits(num) {
return Math.round(num * 1000)/1000;
}
//https://learn.microsoft.com/en-us/bingmaps/spatial-data-services/public-data-sources/poi-entity-types
var EntityTypeNameTable = {
6: 'Agricultural Structure',
4581: 'Airport',
8: 'Airport Runway',
237: 'Airport Terminal',
7996: 'Amusement Park',
9537: 'Apparel Store (Clothing Store)',
3578: 'ATM',
5511: 'Auto Dealership',
7538: 'Auto Service & Maintenance',
6000: 'Bank',
9995: 'Bookstore',
9999: 'Border Post (Border Crossing)',
7933: 'Bowling Alley/Centre',
19: 'Bridge',
4170: 'Bus Station',
5000: 'Business Facility (Administrative/Office Building, Business Center and Factory)',
9517: 'Camp (Campground)',
7985: 'Casino',
9591: 'Cemetery',
7832: 'Cinema',
9121: 'City Hall',
9996: 'Coffee Shop',
7994: 'Community Center (Civic/Community Centre)',
9987: 'Consumer Electronics Store',
9535: 'Convenience Store',
7990: 'Convention Center (Convention/Exhibition Centre)',
9211: 'Court House',
220: 'Currency Exchange',
45: 'Dam',
9545: 'Department Store',
54: 'Educational Structure',
9993: 'Embassy',
275: 'Fast Food',
4482: 'Ferry Terminal',
193: 'Financial Structure',
270: 'Fire Station',
59: 'Fish Hatchery',
5540: 'Gas Station (Petrol/Gasoline Station)',
7992: 'Golf Course',
9525: 'Government Structure (Government Office)',
5400: 'Grocery Store',
73: 'Heliport',
8200: 'Higher Education Facility (Higher Education)',
5999: 'Historical Monument (Battlefield, Fort and Historical Site)',
9986: 'Home Improvement (Hardware and Home Furnishing Store)',
8060: 'Hospital',
7011: 'Hotel',
7998: 'Ice Skating Rink',
9991: 'Industrial Zone',
7389: 'Information Center (Tourist Information)',
87: 'Junction',
8231: 'Library',
94: 'Lighthouse',
4493: 'Marina',
9583: 'Medical Service (Doctor Office and Medical Structure)',
9715: 'Military Base',
5571: 'Motorcycle Dealership',
238: 'Multi Modal Station',
8410: 'Museum',
9998: 'Neighborhood (Hamlet)',
5813: 'Nightlife (Bar, Discotheque, Jazz Club, Karaoke Club and Private Club)',
114: 'Observation Point',
9988: 'Office Supply Store (Office Supply & Services Store)',
7013: 'Other Accommodation (Bed and Breakfast, Cabin, Hostel, Motel and Resort)',
7522: 'Park & Ride',
7947: 'Park (Park/Recreation Area)',
7521: 'Parking Garage/House',
7929: 'Performing Arts (Comedy Club and Theater)',
303: 'Personal Care Facility',
9565: 'Pharmacy',
9992: 'Place of Worship (Church, Mission, Mosque, Temple and Religious Structure)',
9221: 'Police Station',
4444: 'Populated Place',
280: 'Port',
9530: 'Post Office',
137: 'Prison',
4013: 'Railway Station',
273: 'Railway Station Entrance',
142: 'Recreational Structure',
7510: 'Rental Car Agency',
9590: 'Residential Structure (Residential Area/Building)',
7897: 'Rest Area',
5800: 'Restaurant',
153: 'Ruin',
8211: 'School',
156: 'Scientific Research Base',
6512: 'Shopping (Market and Shopping Center)',
7012: 'Ski Area (Ski Resort)',
9567: 'Specialty Store (Hobby Store, Pet Store and Warehouse Club)',
9568: 'Sporting Goods Store',
7940: 'Sports Complex (Playing Field and Race Track)',
7997: 'Stadium (Sports Centre)',
259: 'Store',
283: 'Tollgate',
236: 'Trailhead',
262: 'Tramway',
7999: 'Transportation Service (Metro Station, Railway and Transportation Structure)',
9593: 'Transportation Service (Metro Station, Railway and Transportation Structure)',
9719: 'Truck Dealership',
9522: 'Truck Stop (Truck Stop/Plaza)',
177: 'Tunnel',
8699: 'Vehicle Organization Office (Automobile Club)',
304: 'Veterinarian',
9710: 'Weigh Station',
2084: 'Winery',
9718: 'Zoo (Animal Park)'
};
</script>
</head>
<body>
<div id="myMap" style="position:relative;width:100%;height:600px;"></div><br/>
<div id="output"></div>
<script src="https://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=<Your Bing Maps Key>"></script>
</body>
</html>
Here is a live running version of this code sample: https://rbrundritt.azurewebsites.net/Demos/BingMaps/BingMapsDynamicPoi.html