bing map loading only after resizing the window - javascript

I am not really good at js and trying to use Bing map in our website but it shows the map few times and doesnt show the map most of the time. Below is the code snippet of loading map function, can someone please let me know whats wrong in this function, I am using this from other application:
function loadMap(storeData) {
var coordinates = {};
var map;
var stores = storeData.stores;
if( (typeof stores !== 'undefined') && (typeof stores[0].coordinates !== 'undefined') ) {
coordinates.lat = stores[0].coordinates.lat;
coordinates.lng = stores[0].coordinates.lng;
}else {
coordinates.lat = 33.74831008911133;
coordinates.lng = -84.39111328125;
}
map = new Microsoft.Maps.Map($('#bingMap')[0], {
credentials: 'mykey',
liteMode: true,
enableClickableLogo: false,
center: new Microsoft.Maps.Location(coordinates.lat, coordinates.lng)
});
self.center = new Microsoft.Maps.Location(coordinates.lat, coordinates.lng);
map.setView({zoom: 13});
return map;
}
I have tried below few steps I got from other stackoverflow queries but it didnt help me:-(
setTimeout(this.loadMap(storeData), 2000); Microsoft.Maps.Events.addHandler(map,'resize')

The problem is in your setTimeout call:
You see, setTimeout receives 2 parameters:
A function to execute
Timeout in ms
in your case, you used setTimeout(this.loadMap(storeData), 2000); which doesn't pass the function to the setTimeout but the result of the execution. In addition, this code will also execute this.loadMap immediately and not in 2 seconds.
To solve this, you can just use:
setTimeout(function() { this.loadMap(storeData)}, 2000);
or: (#Sysix's solution)
setTimeout(this.loadMap.bind(this), 2000, storeData);

Related

React Hooks- Best place to call a function that depends on state from a "one time" useEffect?

I'm trying to implement google maps using React Hooks without using any kind of third party libraries. I have written code that allows the script to be generated that fetches the google maps using the url as shown below. But I don't know what is the proper way to call this script generating function (createScriptLoadMap() in our case), as calling it in useEffect that only gets called once seems to give me an error:
You have included the Google Maps JavaScript API multiple times on
this page. This may cause unexpected errors.
I want to know why I'm getting this error and how it can be resolved.
Here is a stackblitz link where I've tried my best make a minimal code for my situation:
https://stackblitz.com/edit/react-hsl4dn?file=Hello.js
Edit: I'm also doing a get request from the useEffect, so it has to be a "one time" useEffect, but I have to call the createScriptLoadMap() one time as well and this function depends on the state set by the useEffect which is doesn't do immediately!
Is there any way such that the get request happens, sets the mapCenter and then the createScriptLoadMap() is executed?
Your useEffect hook does execute only one time. You get that message most likely because of the hot reloading and remounting the Map component.
In order to avoid that, just use the window property you're setting window.initMap to check if the map scripts are loaded:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
index.parentNode.insertBefore(script, index);
window.initMap = true;
}
};
EDIT
As for how to center the map after fetching some data, you should also go with hooks and useEffect as you already have a mapCenter state object. First use script.load to see when script is loaded. Call initMap whether the script is loaded or not to get a google.maps.Map object and save it using state for later use else if script was already loaded just assign the map object:
const createScriptLoadMap = () => {
if (!window.initMap) {
var index = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.onload = () => {
initMap();
};
index.parentNode.insertBefore(script, index);
window.initMap = true;
} else {
initMap();
}
};
Then alter the initMap function to initialize or assigng a google.maps.Map object to out DOM #map element (that won't create a new map if we're already have assigned the element previously):
const initMap = () => {
let googleMap = new google.maps.Map(document.getElementById("map"), {
center: { lat: 37.978534, lng: 23.743830 }, //CURRENTLY STATIC
//center: {mapCenter} //ACTUAL
zoom: 14
});
// Save the map object created for later usage
setMap(googleMap);
// axios.get(`${BASE_URL_LOCAL}/outlet/list?page=1&limit=50`).then(res => {
// let center = averageGeolocation(res.data.data.outlets);
// console.log(props.mapCenter);
// setFetchedData(res);
// setMapCenter(center) //THIS CENTER IS REQUIRED IN LINE 22
// });
// Suppose get an Axios response after 2 seconds
setTimeout(() => {
// Center map at NY Brooklyn using state (effect for mapCenter will be triggered)
setMapCenter({ lat: 40.674282, lng: -73.943060 });
}, 2000);
};
That will create a google.aps.Map object and save it using setMap state for later usage. Now whenever you want to update the center of the map (or any other map properties/methods), just set a useEffect hook to mapCenter state object and then use the map object to alter the map:
const [mapCenter, setMapCenter] = useState({});
const [fetchedData, setFetchedData] = useState({});
const [map, setMap] = useState();
useEffect(() => {
createScriptLoadMap();
}, []);
useEffect(() => {
if(map) {
map.setCenter(mapCenter)
}
}, [mapCenter]);
Check my forked and updated stackblitz. You'll see that the map starts from Athens and after 2 seconds delay (suppose we have an asynchronus Axios call) it centers the map to NY Brooklyn. (I created my Google Maps API key for demonstration, I'll delete after your response).
https://stackblitz.com/edit/react-ohtdra?file=Hello.js
Hooks can't help you in this case. Even if you return clean-up function from useEffect and remove element from head. Because this script creates a ton of other elements in head (like styles) and just removing one element will not help.
I need to say that it's not right to do stuff without introducing a React into it, but if you need so, you can. E.g. add something to a script tag and check this next time and skip createScriptLoadMap. Like this:
const createScriptLoadMap = () => {
var gScript = document.querySelector("script[data-setted]");
var isSetted = gScript && gScript.getAttribute("data-setted");
if (!isSetted) {
var index = document.getElementsByTagName("script")[0];
var script = document.createElement("script");
script.src =
"https://maps.googleapis.com/maps/api/js?key=AIzaSyDurZQBXjtSzKeieXwtFeGe-jhZu-HEGQU&libraries=drawing&callback=initMap";
script.async = true;
script.defer = true;
script.setAttribute("data-setted", true);
index.parentNode.insertBefore(script, index);
window.initMap = initMap;
} else {
// I'm not sure about this, can cause a problem
// but without it stackblitz doesn't render anything
initMap();
}
};

Mapbox.js: Clear a custom legend?

I am using Mapbox.js with browserify. I want to create a module that creates a map if it does not already exist, but updates it if it does.
I have got almost all the way there, but I'm having problems with updating the custom legend. I only seem to be able to append to it, and can't clear it.
This is my code:
var analyseMap = {
setUpMap: function(options) {
var _this = this;
L.mapbox.accessToken = 'mytoken';
if (typeof this.map === 'undefined') {
this.map = L.mapbox.map('map', 'mapbox.streets').setView([53.1, -2.2], 6);
this.layerGroup = L.layerGroup().addTo(this.map);
} else {
this.layerGroup.clearLayers();
}
var geojson_url = "/api/1.0/myurlbasedonoptions";
$.getJSON(geojson_url, function(boundaries) {
var orgLayer = L.geoJson(boundariesWithData, { style: getStyle });
_this.layerGroup.addLayer(orgLayer);
_this.map.fitBounds(orgLayer.getBounds());
var popup = new L.Popup({ autoPan: false });
var legendHtml = _this.getLegendHTML(options);
_this.map.legendControl.removeLegend();
_this.map.legendControl.addLegend(legendHtml);
Then I call it from another module like this:
// on initialise and update
map.setUpMap(someOptions);
This works just great the first time I call it. However, on update, the map updates and the layers update nicely, but I end up with two legends.
How can I clear the existing legend before adding the new one?
The documentation suggests that I need to know the value of the legend HTML in order to remove it, which seems strange.
OK, I fixed it by doing this:
if (typeof _this.legendHtml !== 'undefined') {
_this.map.legendControl.removeLegend(_this.legendHtml);
}
_this.legendHtml = _this.getLegendHTML(_this.quintiles, options);
_this.map.legendControl.addLegend(_this.legendHtml);
Would still be interested to see if anyone has a more elegant solution though!

Google Maps v3 -> Database/mysql -> AJAX -> LOOP -> AJAX -> Loop

a) the real joke first:
Finally buying Microsoft stuff over the years paid off..
Trying to debug using Chrome, my map was showing just one marker. It was on the correct latlng for the period of the setTimeout, blinks for a small fraction of second, back to same position.
At a certain time, shot code to Explorer by mistake....... voila... code iterates through the database, but does not use the setTimeout for each marker. At Explorer, setTime out (Ex, 5 seconds), means all the markers will show up, with the right infowindow, but it's 5 seconds for all markers. The longer the time, the longer it will stay in one of them (alwyas the same one), going really fast on the other markers. At chrome, the iteration was so fast I could not see the iteration thru the other latlng's. It was just a blink.
b) I guess the problem is that the getJson (or Ajax), using either for loop or $.each (I used all sorts of combinations.....) is combined with the other loop inside the function changeMarker. So there are two loops going on at the same time. However, I don't know how to fix it. If I close the Ajax (or getJson) right after the $.each or for loop, the rest of the code doesn't get the values. Nothing happens (just my alert, which is for debugging purposes).
No, I don't fully understand closures. Yes, I read a bunch of stuff, the main one here, but also here and here and there. but still didn't figure it out to :/
c) Not easy being a newbie, trying to solve a problem for days... and not getting it solved.
Any help will be greatly appreciated!
Here is the code - omitted a long section where map gets personal options.
var BERLIN = new google.maps.LatLng(-32.517683, -46.394393);
var map = null;
var marker = null;
var index = 0;
var infoWindow = null;
var latlng ;
var MY_MAPTYPE_ID = 'custom_style';
function initialize() {
//personal options not included here.
var customMapType = new google.maps.StyledMapType(featureOpts, styledMapOptions);
map.mapTypes.set(MY_MAPTYPE_ID, customMapType);
$.getJSON('php/locationsJson.php',function(json){
$.each( json, function(i, item) {
var lat = this.lat;
var lng = this.lng;
var location = new google.maps.LatLng(json[i].lat,json[i].lng);
alert( json[i].lat +','+json[i].lng );
function dropMarker (map, pos){
return new google.maps.Marker({
map: map,
position: location,
draggable: false,
}); // return
}
function changeMarker() {
if (marker) {
infoWindow.close();
marker.setMap(null);
}
var pos = location[index];
marker = dropMarker(map,pos);
var contentString = ('lat: ' + location.lat() + '<br />' + 'lng: ' + location.lng())
infoWindow.setContent(contentString);
setTimeout(function () {
infoWindow.open(map, marker);
}, 100);
index = (index + 1) % json.length;
setTimeout(function () {
changeMarker();
}, 4000);
}
var customMapType = new google.maps.StyledMapType(featureOpts, styledMapOptions);
infoWindow = new google.maps.InfoWindow()
changeMarker();
}); //$.each
}); //end of getjson
} //end of initialized
Here is fiddle (thanks for the help to get to that point) for the code BEFORE the AJAX. I tried to add the json file in the fiddle, but it's too complex to add json inside the fiddle.
Thank you again for your time.
As your approach in the fiddle works fine, you don't need much modifications to
achieve it via ajax.
Instead of starting the timeouts inside the loop use the loop only to populate the NEIGBORHOODS-array, and after the loop call changeMarker()
The success-callback for $.getJSON:
function(json){
NEIGBORHOODS=[];
$.each(json,
function(){
NEIGBORHOODS.push(new google.maps.LatLng(this.lat,this.lng));
});
changeMarker();
}
The rest of the code may stay as it is in the fiddle.
Working fiddle with ajax-request: http://jsfiddle.net/doktormolle/CVECG/
(Note: In the fiddle I've used $.post() because jsfiddle requires post-requests for JSON, you can use $.getJSON in your application)

OpenLayers destroyFeatures() doesn't destroy features when using a clustering strategy

A similar issue to that described in OpenLayers: destroyed features reappear after zooming in or out
Call destroyFeatures() or removeAllFeatures() (or both, in either order) on a vector layer. The features disappear from view. But then zoom in or out, and they reappear. For me, this only happens if a clustering strategy is being used. It almost seems as if the cluster feature is destroyed but the underlying features represented by that cluster are not, so, when you zoom in or out the clustering is recalculated from those underlying features and re-drawn.
Googling reveals a number of conversations among the developers of OpenLayers a few years ago concerning issues with destroyFeatures(). It's surprising that, even now, the issues don't seem to have been fully resolved.
I can get around the problem by destroying the whole layer (using destroy()) and then recreating it when needed. That's OK in my case, but I can imagine cases where such a blunderbuss approach might not be desirable.
In response to the request for a code sample, here is an abbreviated version of the code in the version that is working (ie, using destroy()). In the non-working version, I called destroyFeatures() instead (and did not set the layer to null). As stated above, this would erase the features initially, but if I then zoomed in or out using this.map.zoomIn(), the features would reappear.
Note 1: the functions are called from Objective-C via the JavaScript bridge.
Note 2: the JavaScript was generated using CoffeeScript.
(function() {
var addSightingsLayer, displayFeaturesForSightingsWithGeoJSONData, geoJSONFormat, load, map, projectionSphericalMercator, projectionWGS84, removeSightingsLayer, sightingsLayer;
addSightingsLayer = function() {
var context, layerStyle, layerStyleSelected, style, styleMap, styleSelected, yerClusteringStrategy;
if (!(this.sightingsLayer === null)) return;
yerClusteringStrategy = new OpenLayers.Strategy.Cluster({
distance: 10,
threshold: 2
});
this.sightingsLayer = new OpenLayers.Layer.Vector('Sightings', {
strategies: [yerClusteringStrategy]
});
this.map.addLayer(this.sightingsLayer);
style = {
// Here I define a style
};
context = {
// Here I define a context, with several functions depending on whether there is a cluster or not, eg:
dependentLabel: function(feature) {
if (feature.cluster) {
return feature.attributes.count;
} else {
return feature.attributes.name;
}
}, ....
};
layerStyle = new OpenLayers.Style(style, {
context: context
});
styleSelected = {
//...
};
layerStyleSelected = new OpenLayers.Style(styleSelected, {
context: context
});
styleMap = new OpenLayers.StyleMap({
'default': layerStyle,
'select': layerStyleSelected
});
this.sightingsLayer.styleMap = styleMap;
};
removeSightingsLayer = function() {
if (this.sightingsLayer === null) return;
this.sightingsLayer.destroy();
return this.sightingsLayer = null;
};
displayFeaturesForSightingsWithGeoJSONData = function(geoJSONData) {
if (this.sightingsLayer === null) JFOLMap.addSightingsLayer();
return this.sightingsLayer.addFeatures(this.geoJSONFormat.read(geoJSONData));
};
load = function() {
var lat, lon, osmLayer, zoom;
lat = ...;
lon = ...;
zoom = ...;
this.map = new OpenLayers.Map('mapDiv', {
controls: ...,
eventListeners: ...
});
osmLayer = new OpenLayers.Layer.OSM();
this.map.addLayer(osmLayer);
return this.map.setCenter(new OpenLayers.LonLat(lon, lat).transformWGS84ToSphericalMercator(), zoom);
};
OpenLayers.LonLat.prototype.transformWGS84ToSphericalMercator = function() {
return this.transform(JFOLMap.projectionWGS84, JFOLMap.projectionSphericalMercator);
};
OpenLayers.LonLat.prototype.transformSphericalMercatorToWGS84 = function() {
return this.transform(JFOLMap.projectionSphericalMercator, JFOLMap.projectionWGS84);
};
map = null;
sightingsLayer = null;
sightingsPopoverControl = null;
projectionWGS84 = new OpenLayers.Projection('EPSG:4326');
projectionSphericalMercator = new OpenLayers.Projection('EPSG:900913');
geoJSONFormat = new OpenLayers.Format.GeoJSON({
'externalProjection': projectionWGS84,
'internalProjection': projectionSphericalMercator
});
this.JFOLMap = {
map: map,
sightingsLayer: sightingsLayer,
projectionSphericalMercator: projectionSphericalMercator,
projectionWGS84: projectionWGS84,
geoJSONFormat: geoJSONFormat,
load: load,
addSightingsLayer: addSightingsLayer,
removeSightingsLayer: removeSightingsLayer,
displayFeaturesForSightingsWithGeoJSONData: displayFeaturesForSightingsWithGeoJSONData,
};
}).call(this);
Try this, it worked for me
layer.removeAllFeatures();
layer.destroyFeatures();//optional
layer.addFeatures([]);

How to display loading status with preloader and multiple images?

I am building a slideshow with a few hundred images and would like to build a nice loading bar, so the idea was to preload the images using JavaScript, then initialize the rest of the UI afterwords.
Preloading the images is not a problem, but getting the browser to update the status as things load is. I've tried a few things, but the browser will only repaint the display after it finishes.
I've even tried the script from this question, but I get the same results.
Here's what I've got so far (imgList is an array of filenames. I'm using Prototype.)
var imageBuf = []
var loadCount = 0
$('loadStatus').update("0/"+imgList.length)
function init() {
imgList.each(function(element){
imageBuf[element] = new Image()
//imageBuf[element].onload = window.setTimeout("count()",0) // gives "not implemented" error in IE
imageBuf[element].onload = function(){count()}
imageBuf[element].src = "thumbs/"+element
})
}
function count() {
loadCount++
$('loadStatus').update(loadCount+"/"+imgList.length)
}
init()
Try using the function from my answer to this question:
function incrementallyProcess(workerCallback, data, chunkSize, timeout, completionCallback) {
var itemIndex = 0;
(function() {
var remainingDataLength = (data.length - itemIndex);
var currentChunkSize = (remainingDataLength >= chunkSize) ? chunkSize : remainingDataLength;
if(itemIndex < data.length) {
while(currentChunkSize--) {
workerCallback(data[itemIndex++]);
}
setTimeout(arguments.callee, timeout);
} else if(completionCallback) {
completionCallback();
}
})();
}
// here we are using the above function to take
// a short break every time we load an image
function init() {
incrementallyProcess(function(element) {
imageBuf[element] = new Image();
imageBuf[element].onload = function(){count()};
imageBuf[element].src = "thumbs/"+element;
}, imgList, 1, 250, function() {
alert("done loading");
});
}
You may want to modify the chunk size parameter as well as the length of the timeout to get it to behave just like you want it to. I am not 100% sure this will work for you, but it is worth a try...

Categories

Resources