I'm showing using a bing map on my web page and I'm trying to use a custom pin for the location but it won't show up. My project is ASP.Net Core, My image is stored in wwwroot/images and this JavaScript is in wwwroot/js/site.js. I'm not sure if my path is just wrong or what.
var renderRequestsMap = function (divIdForMap, requestData) {
if (requestData) {
var bingMap = createBingMap(divIdForMap);
addRequestPins(bingMap, requestData);
}
}
function createBingMap(divIdForMap) {
var map = new Microsoft.Maps.Map(
document.getElementById(divIdForMap), {
credentials: BingMapKey,
zoom: 2
});
// tile url from Iowa Environmental Mesonet of Iowa State University
var urlTemplate = 'https://mesonet.agron.iastate.edu/cache/tile.py/1.0.0/nexrad-n0q-{timestamp}/{zoom}/{x}/{y}.png';
var timestamps = ['900913-m50m', '900913-m45m', '900913-m40m', '900913-m35m', '900913-m30m', '900913-m25m', '900913-m20m', '900913-m15m', '900913-m10m', '900913-m05m', '900913'];
var tileSources = [];
for (var i = 0; i < timestamps.length; i++) {
var tileSource = new Microsoft.Maps.TileSource({
uriConstructor: urlTemplate.replace('{timestamp}', timestamps[i])
});
tileSources.push(tileSource);
}
var animatedLayer = new Microsoft.Maps.AnimatedTileLayer({ mercator: tileSources, frameRate: 500 });
map.layers.insert(animatedLayer);
return map;
}
function addRequestPins(bingMap, requestData) {
var locations = [];
$.each(requestData, function (index, data) {
var location = new Microsoft.Maps.Location(data.lat, data.long);
locations.push(location);
var order = index + 1;
alert(data.pinurl);
var pin = new Microsoft.Maps.Pushpin(location, { icon: 'images/low-risk-south-pin.png' });
bingMap.entities.push(pin);
});
var rect = Microsoft.Maps.LocationRect.fromLocations(locations);
bingMap.setView({ bounds: rect, padding: 80 });
}
Also when the map loads it is super zoomed into my pin and whatever I do I can't get it to start with a far out zoom.
If you can, take a look at the network trace. Here you should see where the images are being requested from and will help you verify if it is requesting from the right location.
As for the zooming, you are calculating in the LocationRect from the locations of the pins and then setting the map view based on that. Sounds like that is working as expected. If you don't want to zoom in on your pins, remove that code.
Related
I'm currently working on an internal CRM application for my company. Its built with Blazor, and utilizes the Azure Maps javascript SDK. I'm using Azure Maps to visualize Map Items collected by player's of my company's game. All of the map setup is done in javascript according to the documentation provided by Microsoft.
The map has a symbol layer for displaying points, and a line layer for rendering the path traveled by a given player. Additionally, we are generating a summary of the player's collection stats (speed, distance, and time between collections). The page this component lives on has a DateRangePicker, a drop down menu of different item types, and a button to run the query. Currently, users of this web page can change the date range and the item type and rerun the query. This causes the Summary as well as the map with all points and the line to be re-rendered correctly. However, when the map is re-rendered, the popups attached to the points are no longer on the map, or are at least unresponsive to mouse events. If anyone has any ideas on how to keep and update my popups across map renderings, they would be greatly appreciated.
I was able to solve for this problem disposing of any map resources before attempting to create and render another map. This ensures that any html from previous map renders do not get in the way of the current map.
window.adminMap = (() => {
let map;
let datasource;
let point;
let popup;
const setupItemsMap = (subscriptionKey, mapItems) => {
if (map != null) {
map.dispose();
}
map = new atlas.Map('itemsMap', {
center: [mapItems[0].coordinate.long, mapItems[0].coordinate.lat],
centerOffest: [0, 0],
zoom: 15,
pitch: 90,
style: 'road',
language: 'en-US',
view: 'Auto',
authOptions: {
authType: 'subscriptionKey',
subscriptionKey: subscriptionKey
}
});
map.events.add('ready', () => {
//Create a data source and add it to the map.
datasource = new atlas.source.DataSource(null, {
lineMetrics: true
});
map.sources.add(datasource);
var points = [];
mapItems.forEach(function (mapItem) {
point = new atlas.data.Feature(new atlas.data.Point([mapItem.coordinate.long, mapItem.coordinate.lat]), {
collectedAt: mapItem.collectedAt,
speed: 0
});
points.push(point);
});
points.forEach(function (point) {
let previous;
let current = points.indexOf(point);
if (current > 0) {
previous = current - 1;
point.properties.speed = atlas.math.getSpeedFromFeatures(points[previous], points[current], "collectedAt", "milesPerHour", 1);
}
else {
point.properties.speed = 0;
}
});
datasource.add(points);
var line = createLineFrom(points);
datasource.add(line);
//Calculate a color gradient expression based on the speed of each data point.
var speedGradient = calculateGradientExpression(points, line);
//Create a line layer and pass in a gradient expression for the strokeGradient property.
map.layers.add(new atlas.layer.LineLayer(datasource, null, {
strokeWidth: 5,
strokeGradient: speedGradient
}));
//Create a layer to render each data point along the path.
var pointLayer = new atlas.layer.SymbolLayer(datasource, null, {
//Only render point data in this layer, not the points of the line.
filter: ['==', ['geometry-type'], 'Point']
});
//Create a popup. *This needs to update with the rest of the map!
popup = new atlas.Popup();
popup.open(map);
map.layers.add(pointLayer);
map.events.add('click', pointLayer, pointClicked);
});
}
Glad to here you resolved your issue and posted the answer for others. One recommendation, when ever possible it is best to reuse the map instance and layers. You could run the code above once in your app, then in the future use datasource.setShapes and pass in the new points. This would automatically update the data on the map. If the popup is open when adding new data, simply close it. It also looks like some optimizations can be done around your point conversion code/speed calculations. Here is a modified version of your code that may provide some performance enhancements.
window.adminMap = (() => {
let map;
let datasource;
let point;
let popup;
let lineLayer;
const setupItemsMap = (subscriptionKey, mapItems) => {
if (map != null) {
map.dispose();
}
map = new atlas.Map('itemsMap', {
center: [mapItems[0].coordinate.long, mapItems[0].coordinate.lat],
centerOffest: [0, 0],
zoom: 15,
pitch: 90,
style: 'road',
language: 'en-US',
view: 'Auto',
authOptions: {
authType: 'subscriptionKey',
subscriptionKey: subscriptionKey
}
});
map.events.add('ready', () => {
//Create a data source and add it to the map.
datasource = new atlas.source.DataSource(null, {
lineMetrics: true
});
map.sources.add(datasource);
lineLayer = new atlas.layer.LineLayer(datasource, null, {
strokeWidth: 5,
//strokeGradient: speedGradient
});
//Create a line layer and pass in a gradient expression for the strokeGradient property.
map.layers.add(lineLayer);
//Create a layer to render each data point along the path.
var pointLayer = new atlas.layer.SymbolLayer(datasource, null, {
//Only render point data in this layer, not the points of the line.
filter: ['==', ['geometry-type'], 'Point']
});
//Create a popup. *This needs to update with the rest of the map!
popup = new atlas.Popup();
popup.open(map);
map.layers.add(pointLayer);
map.events.add('click', pointLayer, pointClicked);
//Add mapItems to data source.
setMapItems(mapItems);
});
}
const setMapItems = (mapItems) => {
let points = [];
let previous;
let point;
for(let i = 0, len = mapItems.length; i < len; i++){
point = new atlas.data.Feature(new atlas.data.Point([mapItem.coordinate.long, mapItem.coordinate.lat]), {
collectedAt: mapItem.collectedAt,
speed: 0
});
if(previous){
point.properties.speed = atlas.math.getSpeedFromFeatures(previous, point, "collectedAt", "milesPerHour", 1);
}
points.push(point);
previous = point;
}
var line = createLineFrom(points);
//Calculate a color gradient expression based on the speed of each data point.
lineLayer.setOptions({
strokeGradient: calculateGradientExpression(points, line)
});
points.push(line);
//Add data to data source using setShapes. This clears and adds the data and only triggers one re-render of the map.
datasource.setShapes(points);
if(popup){
popup.close();
}
}
}
I have a map where I need to show some locations based on the year the has selected. There are about 100k locations per year.
Everything works fine during the first load (year 2015). But if the user chooses another year (let's say year 2016), the browser will freeze.
Below is my code. I am callin markers.clearLayers() in order to remove the current points (from year 2015) and update the map with the new ones (year 2016).
var map = L.map('map', {
center: latlng,
zoom: 10,
layers: [tiles]
});
var markers = L.markerClusterGroup({
chunkedLoading: true
});
fetchLocations(yearSlider.value)
function fetchLocations(year) {
markers.clearLayers();
fetch('http://localhost:3003/locations/year/' + year)
.then(response => response.json())
.then(json => {
for (var i = 0; i < json.length; i++) {
const geo = json[i].geo;
const coords = geo.coordinates;
var marker = L.marker(L.latLng(coords[1], coords[0]), {
title: "abc"
});
marker.bindPopup("abc");
markers.addLayer(marker);
}
map.addLayer(markers);
});
}
First way is to reduce the visible markers for just what is on screen. This will increase a lot of performance.
var markers = new L.MarkerClusterGroup(....);
// Remove everything outside the current view (Performance)
markers._getExpandedVisibleBounds = function () {
return mcg._map.getBounds();
};
Second way is increasing maxClusterRadius will give you less clusters over the map. This also improve dragging performance.
Other freezing issue might be creating L.marker(...) of 100k locations.
Editted:
fetchLocations(yearSlider.value)
var map;
function initMap () {
// Reset whole map and redraw it.
if (map) {
map.off();
map.remove();
}
// Create a new leaflet map
map = L.map('map', {
center: latlng,
zoom: 10,
layers: [tiles]
});
return;
}
function fetchLocations(year) {
initMap();
fetch('http://localhost:3003/locations/year/' + year)
.then(response => response.json())
.then(json => {
// Create new marker cluster group
var markers = L.markerClusterGroup({
chunkedLoading: true
});
for (var i = 0; i < json.length; i++) {
const geo = json[i].geo;
const coords = geo.coordinates;
var marker = L.marker(L.latLng(coords[1], coords[0]), {
title: "abc"
});
marker.bindPopup("abc");
markers.addLayer(marker);
}
// Add individual group to the map
map.addLayer(markers);
});
}
The issue is that you are individually adding Markers to your Cluster Group while the latter is already on map:
function () {
// On next calls, `markers` is still on map
for (var i = 0; i < json.length; i++) {
markers.addLayer(marker); // synchronously processes when the group is on map
}
map.addLayer(markers); // on first call, `markers` is not yet on map
}
You can accumulate your markers in an array within your loop, then once it is full, you use markers.addLayers(arrayOfMarkers)
Or remove your markers group from map at the beginning of your function, so that it internally accumulates your Markers and batch processes them when it is added on map at the end.
Even though parndepu's solution also works, I managed to solve the problem by simply adding map.removeLayer(markers); after markers.clearLayers();.
I have a list of restaurants each of which has a fixed location which is stored in GeoFire. Now I want to display the restaurants within a fixed radius of user location. How can I achieve this using GeoFire. The documentation here, https://github.com/firebase/geofire-js/blob/master/docs/reference.md#geofiresetkeyorlocations-location mostly suggests solutions in which geofire 'keys' are moving which are handled using key_entered, key_exited etc. But I'm not able to figure out how to access the keys in proximity.
My controller code looks like:
var ref = firebase.database().ref();
var geoFire = new GeoFire(ref.child("geofire"));
var posOptions = {timeout: 10000, enableHighAccuracy: false};
$cordovaGeolocation.getCurrentPosition(posOptions).then(function (position) {
$scope.rest= [];
$scope.filteredRest = [];
var geoQuery = geoFire.query({
center: [position.coords.latitude, position.coords.longitude],
radius: 15
});
geoQuery.on("key_entered", function(key, location, distance) {
console.log(key);
$scope.filteredRest.push(key);
});
geoQuery.on("ready", function() {
geoQuery.cancel();
});
console.log($scope.filteredRest);
The key is printed on console but is not pushed to filteredRest array.
I'm looking to achieve 'Example usage' in following link:
https://github.com/firebase/geofire-java#example-usage
There are similar questions but most of them are in ios/swift and the answers are not very informative. I'm using GeoFire-JavaScript. Any help would be appreciated.
To handle when all the initial keys are synchronized, put the code into the ready handler. For example, say you want to print all the initial keys:
var ref = firebase.database().ref();
var geoFire = new GeoFire(ref.child("geofire"));
var posOptions = {timeout: 10000, enableHighAccuracy: false};
$cordovaGeolocation.getCurrentPosition(posOptions).then(function (position) {
$scope.rest= [];
$scope.filteredRest = [];
var geoQuery = geoFire.query({
center: [position.coords.latitude, position.coords.longitude],
radius: 15
});
geoQuery.on("key_entered", function(key, location, distance) {
$scope.filteredRest.push(key);
});
geoQuery.on("ready", function() {
geoQuery.cancel();
console.log($scope.filteredRest);
});
I have developed a 3D viewer of buildings. What I'm trying to add now is the selection of the content of a WMS (Web Map Service) below the building entities.
Basically, I want to be able to select the building at the position were the user left clicks. The colour of the building should change (which works). And I want to retrieve the information of the Web Map Service at the position were the user clicked.
This is what I have coded so far:
var pickColor = Cesium.Color.CYAN.withAlpha(0.7);
var selectedEntity = new Map();
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function(click) {
var pickedObject = viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject)) {
var entityId = pickedObject.id._id;
var oldColor = buildingMap.get(entityId).polygon.material.color;
buildingMap.get(entityId).polygon.material.color = pickColor;
selectedEntity.set(entityId, oldColor);
var currentLayer = viewer.scene.imageryLayers.get(1);
if (typeof currentLayer !== 'undefined') {
var info = currentLayer._imageryProvider._tileProvider.getTileCredits(click.position.x, click.position.y, 0);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
However, my variable "info" stays undefined, whereas I expect it to return an array.
For WMS you need to use the wms GetFeatureInfo capability:
var pickRay = viewer.camera.getPickRay(windowPosition);
var featuresPromise = viewer.imageryLayers.pickImageryLayerFeatures(pickRay, viewer.scene);
if (!Cesium.defined(featuresPromise)) {
console.log('No features picked.');
} else {
Cesium.when(featuresPromise, function(features) {
// This function is called asynchronously when the list if picked features is available.
console.log('Number of features: ' + features.length);
if (features.length > 0) {
console.log('First feature name: ' + features[0].name);
}
});
}
I have created a polyline using a coordinate array with code adapted from
https://google-developers.appspot.com/maps/documentation/javascript/examples/polyline-simple
Although the first (and probably worst) method to make the line was just a huge list of lat/lng points. Still learning the programming tricks, I apologize. Im a geographer not a programmer!
I want to get the elevation from that line and create an elevation profile graph.
Im new to JS and not sure how to debug whats not working. I cant seem to populate the path array with the coordinates from the polyline.
Its currently set to push the bikeCourseCoordinates to a new array that will then be used as a path. I tried it just using the bikeCourseCoordinates array as the 'path' but that didnt work either.
Online (but not working version) here:
http://geography.uoregon.edu:50000/bentesting/map_try3.html
function drawPath() {
// Create a new chart in the elevation_chart DIV.
chart = new google.visualization.ColumnChart(document.getElementById('elevation_chart'));
var path = new Array;
path.push(bikeCourseCoordinates);
// Create a PathElevationRequest object using this array.
var pathRequest = {
'path': path,
'samples': 256
}
// Initiate the path request.
elevator.getElevationAlongPath(pathRequest, plotElevation);
}
// Takes an array of ElevationResult objects, draws the path on the map
// and plots the elevation profile on a Visualization API ColumnChart.
function plotElevation(results, status) {
if (status == google.maps.ElevationStatus.OK) {
elevations = results;
// Extract the elevation samples from the returned results
// and store them in an array of LatLngs.
var elevationPath = [];
for (var i = 0; i < results.length; i++) {
elevationPath.push(elevations[i].location);
}
// Extract the data from which to populate the chart.
// Because the samples are equidistant, the 'Sample'
// column here does double duty as distance along the
// X axis.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Sample');
data.addColumn('number', 'Elevation');
for (var i = 0; i < results.length; i++) {
data.addRow(['', elevations[i].elevation]);
}
// Draw the chart using the data within its DIV.
document.getElementById('elevation_chart').style.display = 'block';
chart.draw(data, {
width: 640,
height: 200,
legend: 'none',
titleY: 'Elevation (m)'
});
}
}
Are you trying to reproduce the third exemple on that page ? https://developers.google.com/maps/customize
If yes i have done it but without the graph over effects
here is my code
this goes in your head
script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"
script type="text/javascript" src="https://www.google.com/jsapi"
script src="https://www.google.com/uds/?file=visualization&v=1&packages=columnchart" type="text/javascript"
and this in the footer just before body tag
var elevator;
var map;
var chart;
var infowindow = new google.maps.InfoWindow();
var polyline;
var mapCenter = new google.maps.LatLng(-21.745585792425,165.91141052497);
// Load the Visualization API and the columnchart package.
google.load("visualization", "1", {packages: ["columnchart"]});
function initialize() {
var mapOptions = {
center: mapCenter,
zoom: 13,
mapTypeId: 'terrain'
};
map = new google.maps.Map(document.getElementById("map"), mapOptions);
// Create an ElevationService.
elevator = new google.maps.ElevationService();
// Draw the path, using the Visualization API and the Elevation service.
drawPath();
}
function drawPath() {
// Create a new chart in the elevation_chart DIV.
chart = new google.visualization.ColumnChart(document.getElementById('elevation-chart'));
var path = bikeCourseCoordinates;
// Create a PathElevationRequest object using this array.
// Ask for 256 samples along that path.
var pathRequest = {
'path': path,
'samples': 256
}
// Initiate the path request.
elevator.getElevationAlongPath(pathRequest, plotElevation);
}
function plotElevation(results, status) {
if (status == google.maps.ElevationStatus.OK) {
elevations = results;
// Extract the elevation samples from the returned results
// and store them in an array of LatLngs.
var elevationPath = [];
for (var i = 0; i < results.length; i++) {
elevationPath.push(elevations[i].location);
}
// Display a polyline of the elevation path.
var pathOptions = {
path: elevationPath,
strokeColor: '#0000CC',
opacity: 0.9,
map: map
}
polyline = new google.maps.Polyline(pathOptions);
// Extract the data from which to populate the chart.
// Because the samples are equidistant, the 'Sample'
// column here does double duty as distance along the
// X axis.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Sample');
data.addColumn('number', 'Elevation');
for (var i = 0; i < results.length; i++) {
data.addRow(['', elevations[i].elevation]);
}
// Draw the chart using the data within its DIV.
document.getElementById('elevation-chart').style.display = 'block';
chart.draw(data, {
width: 960,
height: 300,
legend: 'none',
titleY: 'Elevation (m)'
});
}
}
initialize();
I couldn't find the problem but here are some observations which might help:
I tried it just using the bikeCourseCoordinates array as the 'path'
According to the Maps API, pathRequest should be be:
var pathRequest = {
'path': bikeCourseCoordinates,
'samples': 256
}
Further, I think the following initial part, ie:
var whitney = new google.maps.LatLng(36.578581, -118.291994);
...
...
var panamintsprings = new google.maps.LatLng(36.339722, -117.467778);
var badwater = new google.maps.LatLng(36.23998, -116.83171);
var bikeCourseCoordinates = [
new google.maps.LatLng(47.67609435030702, -116.7896032333374),
which comes directly within the first inline <script> tag should be called only after the maps library is loaded. I would put it all into another function say myInit and then call myInit from within your current function named initialize
The reason for this above is that although you are including the script tag to include the maps api maps.googleapis.com/maps/api/js?sensor=false the browser will continue to execute the next script block containing whitney= new google.maps.Lat because maps.googleapis.com is an external script and I think these external scripts are loaded asynchronously.