How do I speed up infoBox.open() on FusionTables layer click? - javascript

Setup
I have a polygon map of all the Provinces in South Africa.
This is then pulled into a v3 Google Map as a FusionTablesLayer as follows:
// The Google Map object
map = new google.maps.Map( mapCanvas, mapOptions );
// The FusionTables layer
layer['provinces'] = new google.maps.FusionTablesLayer({
query: {
select: '*',
from: '16L-UK_1OZxGw6DlKR8V8yP4XZrtmNdOMugRRNrQ'
},
clickable: true,
suppressInfoWindows: true // Hide the default FusionTables InfoWindow
});
Right after that, I attach a click event listener to the FusionTablesLayer, and build a custom InfoBox object as follows:
google.maps.event.addListener(layer['provinces'], 'click', function(e){
/*
Here I build the infoBox HTML using e.row[]
eg: html = '<div>' + e.row['Province'].value + '</div>';
*/
// Attach the infoBox to the click
infoBox.setContent(html);
infoBox.setPosition(e.latLng);
infoBox.open(map);
});
After that, I render the layer on the map:
// Render the layer on the map
layer['provinces'].setMap(map);
All of this works. No problem.
Problem
The click event returns all the columns in the respective row of the FusionTable, and attaches it to the variable e above.
Now, each row in the FusionTable has a very long KML string - from 114kb to 2.5MB - and this is returned in e.infoWindowHtml as well as e.row['Polygon'].value.
e: (Object)
infoWindowHtml: "_Very_ long string in here."
latLng: Q
pixelOffset: T
row: (Object)
Number: (Object)
Polygon: (Object)
Province: (Object)
The request doesn't take very long due to heavy caching on Google's side, but after clicking on a Province, it takes almost 5 seconds for the infoBox to pop up.
tl;dr
The infoBox.open(map) method, after clicking on a FusionTables polygon, is very slow. How do I speed it up?
Update
The data is cached after the first click. Is there a way to cache the data before the first click?
Alternatively, is there a way to limit the returned variables attached to e, i.e.: remove the 'Polygon' data from the click request?

I found the answer by chance.
You can customise what gets returned by selecting the proper columns in the Change info window layout... window.
Map tab Tools > Change info window layout...
I deselected 'Polygon', and left 'Number' and 'Province' selected.
The columns you select are then attached to e in the click eventListener:
e: (Object)
infoWindowHtml: "Better string"
latLng: Q
pixelOffset: T
row: (Object)
Number: (Object)
Province: (Object)
Important Note (here's the luck)
After selecting the proper columns, you need to change the FusionTable data in some meaningful way to clear the strong caching on Google's side.
The columns selected in the Automatic tab are returned.
It seems that the Custom tab gets ignored.

Its speedy the second time to open the popup, so I presume it gets cached. The KML's are rather large tho, thats your bottleneck - can't you simplify them? For a decent overview you don't need that much detail. That should speed it up. (try replacing one province with just a rectangle and see if that helps)

Related

How to display GPX track names (and or descriptions) in a clickable popup when using the Leaflet.TimeDimension plugin

my coding knowledge is next to nothing, but I am having heaps of fun trying to learn.
I have been amending the code from Leaflet TimeDimension example 9: http://apps.socib.es/Leaflet.TimeDimension/examples/example9.html to try and show animal movement through time on a map created in Leaflet.
I have a self written GPX file and KML track containing the individual animal's lats and longs through time. I have also added the time dimension so that I can play the time slider and my custom icons move across the map as I would like. However, I have now hit a wall.
I would like to be able to click on a marker (at any stage during the time sliders duration [marker position moves]) and bring up information about that marker in a popup. This would be easy if there were actual markers (marker.bindPopup()) but the markers are created from a track in a GPX file:
example of an animals track in the GPX file:
...
<trk><name>THE DESCRIPTION I WANT TO DISPLAY IN A POPUP</name><number>2</number><trkseg>
<trkpt lat="-40" lon="120"><ele>2</ele><time>2018-01-06T10:09:57Z</time></trkpt>
<trkpt lat="-41" lon="122"><ele>2</ele><time>2018-01-21T16:45:57Z</time></trkpt>
</trkseg></trk>
...
and I do not know how to call the name or the description of one of these tracks and bind the information in a popup (there are multiple sets of tracks [each with a unique name and description] in the single GPX file).
There are two main solutions I have been looking into (that I think might be possible) but can't quite get either to work.
use get_name() from the GPX.js script: https://github.com/mpetazzoni/leaflet-gpx
It says:
If you want to display additional information about the GPX track, you can do so in the 'loaded' event handler, calling one of the following methods on the GPX object e.target:
get_name(): returns the name of the GPX track
Here is the demo code: https://git.nxfifteen.rocks/rocks/core/raw/056ddab410a40496a917cb150be8d92f4cc205cf/map.php but this code is well beyond my understanding and I haven't been able to figure out how to make it work
I have tried:
function onClick(e) {
alert(this.get_name());
}
but it doesn't work (not sure if its heading in the right direction or not).
Somehow in the customLayer make the bindPopup link to the name of the GPX track
Here is the section of code in my .js file that calls and loads the GPX and KML layer:
var customLayer = L.geoJson(null, {
pointToLayer: function (feature, latLng) {
if (feature.properties.hasOwnProperty('last')) {
return new L.Marker(latLng, {
icon: icon
}).bindPopup ("yo");
}
return L.circleMarker(latLng).bindPopup ("hey");
}
});
var gpxLayer = omnivore.gpx('data/data.gpx', null, customLayer).on('ready', function(e) {
map.fitBounds(gpxLayer.getBounds(), {
paddingTopLeft: [160, 160],
paddingBottomRight: [100, 100]
});
});
var gpxTimeLayer = L.timeDimension.layer.geoJson(gpxLayer, {
updateTimeDimension: true,
addlastPoint: true,
waitForReady: true
});
var kmlLayer = omnivore.kml('data/data.kml');
var kmlTimeLayer = L.timeDimension.layer.geoJson(kmlLayer, {
updateTimeDimension: true,
addlastPoint: true,
waitForReady: true
});
gpxTimeLayer.addTo(map);
In the variable: customLayer that is linked to the GPX track using omnivore, I can add a clickable popup that says "yo" to every animals marker. I can pause the time slider at any point and when I click a marker "yo" popus up (almost what I need).
I was wondering if there is some way to, instead of it saying "yo", tell it to return the name of the track that has been clicked, so that each marker will display its respective name (the name will contain the description I want it to show).
Sorry this is such a long winded question, and thank you in advance for any solutions, tip or pointers on whether my attempts just need a bit of tweaking, or if there is a different better solution out there.

How to modify or remove some existing data in GeoJSON Leaflet object?

Recently I asked about referencing the data of an existing GeoJSON Leaflet object. My Leaflet map consists of data coming in a stream to my GeoJSON Leaflet object. User inputs can change a filter for the GeoJSON data, so to make the filter apply to both the existing and new data I am keeping track of my data in an array called myFeatures. Whenever the filters change or an item in myFeatures changes, I do the following:
myGeoJson.clearLayers();
myGeoJson.addData(myFeatures);
This is working to make my map update according to the newly updated feature data or the changes in the filter.
I am applying pop-ups to the GeoJSON object when I initialize my GeoJSON object:
var myGeoJson = L.geoJson(myFeatures, {
style: function(feature) {
...
},
pointToLayer: function(feature, latlng) {
return L.circleMarker(latlng, geojsonMarkerOptions);
},
filter: function(feature, layer) {
...
},
onEachFeature: function(feature, layer) {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
}
});
When I click on an individual feature, the pop-up appears. However, the pop-up dismisses pretty quickly, thanks to clearLayers and addData being called. :(
Is there some kind of way to stop the pop-up dismissing in this situation?
Or - better question - is there a way to modifying existing data in a GeoJSON object or remove some (not all) data from a GeoJSON object?
To provide some context, my GeoJSON shows circle markers for each feature. The circle markers are colored based on a property of the feature. The property can actually change over time, so the marker's styling needs to be updated. A marker also times out after a while and needs to be removed from the map, but the other markers need to stay on the map.
There are for sure better ways to do that, but if you don't want to modify your code architecture too much, you could just create your popups in a specific layer, which you won't clear when you add your new data.
To give you an idea (markers play below the role of myGeoJson in your example):
var popup_id = {};
var popup_layer = new L.layerGroup();
var markers = new L.layerGroup();
$.each(testData, function(index, p) {
var marker = L.marker(L.latLng(p.lat, p.lon));
markers.addLayer(marker);
popup = new L.popup({offset: new L.Point(0, -30)});
popup.setLatLng(L.latLng(p.lat, p.lon));
popup.setContent(p.text);
popup_id[p.id] = popup;
marker.on('click', function() {
popup_id[p.id].openPopup();
popup_layer.addLayer(popup_id[p.id]);
markers.clearLayers();
})
});
popup_layer.addTo(map);
markers.addTo(map);
You also keep track of all your popups in a dictionary popup_id.
Since you haven't provided us with a JSfiddle it is a bit difficult to find the perfect answer for your case, but I hope that the popup layer (also here in my fiddle) gives you a good direction.

jVectorMap - setFocus error - jQuery

I understand that the minified file from the zip is only the base code, and does not include libraries. Therefore I ran the build.sh file and it produced another minified file that I have included in my scripts.
Expectation:
I am attempting to zoom in on a marker when clicked. I have a function that runs on the event, onMarkerClick.
The problem:
I have looked at (2) different posts:
Jvector map, how to focus on a marker?
https://github.com/bjornd/jvectormap/issues/157
Both posts produce the same exact error.
The error:
Error: <g> attribute transform: Expected number, "scale(NaN) translate(N…". jquery.jvectormap.min.js:733
line 733 - this.rootElement.node.setAttribute('transform', 'scale('+scale+') translate('+transX+', '+transY+')');
Apparently +scale+ is not a number (NaN)
I had a bunch of errors, but finally narrowed it down. First I thought that c had markers[c].latitude and markers[c].longitude, but it did not. Next mistake was not passing the configuration to the setFocus function
onMarkerClick: function (e, c) {
setFocusLatLng(5, markers[c].latLng[0], markers[c].latLng[1]);
}
// sets focus on marker clicked
function setFocusLatLng(scale, lat, lng) {
var mapObj = $('#map').vectorMap('get', 'mapObject');
var config = {
animate: true,
lat: lat,
lng: lng,
scale: scale
}
mapObj.setFocus(config)
}
Update:
In case you ever need to pan back out to full map and set the focus on the center of the map:
// sets focus on center of map and zooms back out to full view
function setFocusMapCenter() {
var mapObj = $('#map').vectorMap('get', 'mapObject'),
center = mapObj.pointToLatLng(mapObj.width / 2, mapObj.height / 2);
var config = {
animate: true,
lat: center.lat,
lng: center.lng,
scale: 1
}
mapObj.setFocus(config)
}
I have fought with this issue today and I'll leave my fix here in case I might help someone.
I was having the same error while trying to focus passing the id like this:
$('#world-map').vectorMap('get', 'mapObject');
map.setFocus(regionId)
But you have to pass an object as written below and it works perfectly. The documentation says it, but it is not that clear, with an example it would work better
$('#world-map').vectorMap('get', 'mapObject');
map.setFocus({region: regionId})
Late to the party but I had this issue myself and these solutions are not the best. Im using jquery-3.5.1.js with jvectormap multimap and it seems to work just fine except this issue occurs if the size of the viewport is changed
The problem is in the file svg-canvas-element.js in the function
jvm.SVGCanvasElement.prototype.applyTransformParams
The solution is to wrapping the setAttribute line with a numeric check on the value for the scale variable.
e.g.)
if($.isNumeric( scale )){
this.rootElement.node.setAttribute('transform', 'scale('+scale+') translate('+transX+', '+transY+')');
}
Make the change, Save, then test by opening the drill-down.html example. The intial map is the United States. Select a region it loads the State map. Now open developer tools ( im doing this in chrome ) then click the back button on the map ( not in the browser ). Now click on a different state it should throw the error in console and the new state may not even appear in the #map1 div if it is still broken. If it is fixed everything will work as expected.

Bing Maps API v7 limit by bounds

So I'm trying to use the Search Module of the Bing Maps AJAX API (v7), and I've noticed that in the Interactive SDK for it you can pass in a property called bounds which you give a bounding box to search within. The example simply uses the map's current bounding box so theoretically, if you zoom in, a new search should simply show you results within your zoomed in area, right?
Well here's the issue: Add the following code at the end of the example code in the Interactive SDK:
Microsoft.Maps.Events.addHandler(map, 'viewchange', searchRequest);
What this should do is every time you move around the map or zoom in or out, it should fire a new search with the new bounding area of the map... I say this because of the line that looks like this: bounds: map.getBounds(),. What actually happens is that it bounces back to where it was initially before zooming.
Call me crazy, but is the bounds property just being completely ignored? Does anyone know how to limit the search results to the currently visible map area?
Lastly: Is it just me, or are the API docs for V7 rather incomplete? I've managed to find a few things by inspecting stuff in the Chrome console that doesn't appear in the API docs.
Update: This is what my call to the search function looks like:
searchManager.search({
bounds: map.getBounds(),
callback: searchSuccess,
count: 20,
entityType:"Business",
errorCallback: searchFail,
startIndex: 0,
userData: userData,
what: what,
where: search
});
I have not personally used on view changed as I'm not sure that was available when I migrated from 6.0.
I'll share an alternative route I went that gets the trick done.
My search functionality also puts a Microsoft.Maps.Pushpin exactly where the user searched ("You are here!").
I then I create a boundary from the pushpin:
var viewBoundaries = Microsoft.Maps.LocationRect.fromLocations(pushpin.getLocation());
Then set the Map.setView properties for bounds. (Aswell as zoom in my case)
map.setView({ bounds: viewBoundaries });
map.setView({ zoom: 10 });
If you are not using a pushpin, you can simply create the view boundary from the location class.
MSDN Location Class

Creating a map using only toggleable overlays?

I'm trying to create map (using the Google Maps JavaScript API V3) which consists of several partially-transparent layers. By default, these layers should all be overlaid on top of one another to form a complete map, but the user should be able to turn any combination of them on or off (while preserving order) to create whatever view they prefer.
So far, I've had a great deal of luck getting this working for a single layer using map.mapTypes, but when adding all the layers via map.overlayMapTypes, I've hit a couple of snags:
The map doesn't seem to get fully initialized if map.setMapTypeId() is not called (no controls appear and the map is not correctly centered) and it cannot be called with an overlay.
It isn't clear how to toggle the visibility of an overlay without directly modifying the map.overlayMapTypes array, which complicates keeping them correctly ordered. I'd much prefer something analogous to the Traffic/Transit/Photos/etc. control available within Google Maps itself.
Here's the initialize function I'm working with. I'd post a link, but the map imagery isn't publicly available:
function initialize() {
map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 0,
center: center
});
/* if these lines are uncommented, the single layer displays perfectly */
//map.mapTypes.set("Layer 3", layers[3]);
//map.setMapTypeId("Layer 3");
//return;
var dummy = new google.maps.ImageMapType({
name: "Dummy",
minZoom: 0,
maxZoom: 6,
tileSize: new google.maps.Size(256, 256),
getTileUrl: function() {return null; }
});
map.mapTypes.set("Dummy", dummy);
map.setMapTypeId("Dummy");
// layers is an array of ImageMapTypes
for (var i = 0; i < layers.length; i++) {
map.overlayMapTypes.push(layers[i]);
}
}
As you can see, I've tried creating a "dummy" maptype (which always returns null for tile URLs) to serve as the base map. While this does cause the controls to display, it still doesn't center correctly.
What's the best way to create a map which consists only of toggleable overlays?
Update: Turns out the dummy maptype works perfectly well if you also remember to set a projection. That's one problem solved, at least. :-)
I use ImageMapType, but I don't add it to mapTypes. I just add it to overlayMapTypes and when I need to remove it I use setAt to set the entry in overlayMapTypes to null.
You will need to add individual controls to the UI that toggle the individual layers.

Categories