I am trying to update the coordinates of a folium map rendered by a Dash App on a callback (which is running inside a Flask App). The callback selectively renders different layers on a map - the issue is that the zoom and center coordinates are not persisted when the map is updated. The map is rendered as html and injected as in iframe into the app.
Addendum: Not a professional programmer, have only been trying my hand at this for the past six months.
I have tried three approaches:
JS API call client-side to flask route. I ended up realizing this had too much overhead (plus couldn't identify user to update the proper coordinates).
Encoding the coordinates and zoom in the URL. The URL changes as expected from this js snippet:
map_layer.on("mouseup zoomend", function(){
var coordinates = map_layer.getCenter();
var lat = coordinates.lat
var lon = coordinates.lng
var zoom = map_layer.getZoom();
parent.history.replaceState({}, '', `/app/layer?&lat=${lat}&lon=${lon}&zoom=${zoom}`);
// const ON_CHANGE = '_dashprivate_historychange';
// window.dispatchEvent(new CustomEvent(ON_CHANGE));
// parent.window.dispatchEvent(new CustomEvent(ON_CHANGE));
console.log("success");
The commented-out code also tries to dispatch a CustomEvent to dash to try and update it's history - not really clear what's happening there - just tried to emulate this approach.
The right URL is not passed on to the callback however. So despite the url changing on the browser, it's not being sent back with the updated query variables. If I refresh the page and actively send the URL, than the right URL is passed on, but that's not the kind of behavior I'm looking for - I would like for it to change with no active reloading of the page:
#layer.callback(
Output("map", "srcDoc"),
-- other inputs --,
Input('url', 'href') #tried State as well
)
def update_output_src(href):
print(href)
-- other code --
return map.get_root().render()
Using a hidden DIV to store the coordinates. The div content changes as expected from this js snippet:
map_layer.on("mouseup zoomend", function(){
var coordinates = map_layer.getCenter();
var lat = coordinates.lat
var lon = coordinates.lng
var zoom = map_layer.getZoom();
var latstore = parent.document.getElementById('latstore');
latstore.textContent = lat; #trying different approaches in what's changed to see if dash callback captures it.
var lonstore = parent.document.getElementById('lonstore');
lonstore.innerText = lon;
var zoomstore = parent.document.getElementById('zoomstore');
zoomstore.innerHTML = zoom;
console.log("success");
But again I am not able to capture the stored coordinates when the input is triggered.
#layer.callback(
Output("map", "srcDoc"),
-- other inputs --,
State('latstore', 'children'),
State('lonstore', 'children'),
State('zoomstore', 'children'),
)
def update_output_src(latstore, lonstore, zoomstore):
print(latstore, lonstore, zoomstor)
-- other code --
return map.get_root().render()
Any help or pointer in the right direction for approaches 2 or 3 would be super useful. I have been struggling with this for 3-4 days now and I'm out of ideas.
Related
I am writing a module that lets you track your mouse coordinates on another person his screen. I am writing the X and Y coordinates to firebase. The `onMouseMove write event goes really smooth and Firebase updates perfectly fine without lag. However my read event has some trouble keeping up:
.child('activity')
.on('value', snapshot => {
if (snapshot.val()) {
friendDashboardActivityData[userKey] = snapshot.val();
if (document.getElementById('cursor' + userKey)) {
/** Place the element based on the coordinates */
let d = document.getElementById('cursor' + userKey);
d.style.left = snapshot.val()['cursor'].x + 'px';
d.style.top = snapshot.val()['cursor'].y + 'px';
The mouse that appears on screen is really laggy because it can't handle the many changes per second in firebase. Is there a socket like connector in firebase (or perhaps we made a mistake somewhere) that we can use, so i seems that the cursor is moving smoothly.
I am passing proper latitude and longitude to the map but it shows old map, having old lat lng. I know I am doing something wrong in this, but I can't figure out it. My project in ionic framework and for map I am using this plugin map plugin
This my html code
<div style="width:100%;height:200px" id="mapDisplay"></div>
This my angularJS code
var getLat = $scope.dealer.lat;
var getLng = $scope.dealer.lng;
var address = $scope.dealer.address;
var mapDiv = document.getElementById("mapDisplay");
const myGeoLocation = new plugin.google.maps.LatLng(getLat,getLng);
var map = plugin.google.maps.Map.getMap(mapDiv, {
'camera': {
'latLng': myGeoLocation,
'zoom': 15
}
});
map.addEventListener(plugin.google.maps.event.MAP_READY, function() {
map.addMarker({
'position': myGeoLocation,
'title': address
}, function(marker) {
marker.showInfoWindow();
});
});
From the docs of the map plugin you are using
This plugin generates only one map instance using the Map.getMap()
method. In general, you may want to create multiple maps, but this
plugin doesn't allow it.
So your plugin.google.maps.Map.getMap is only grabbing the existing instance, not creating a new one and the plugin.google.maps.event.MAP_READY is only fired once, the first time. However looking at the source code it looks like the instance exposes a remove method which you can use to destroy the existing instance and create a new one with the new sets of coordinates when the callback is called, e.g: map.remove(function() { /** reset map coordinates **/ }
** Extra **
If you just need to display a map with a set of coordinates you can do it using the native app by passing lat/lon to the $cordovaInAppBrowser plugin. As an example for iOS and Android you'd have the following:
iOS
$cordovaInAppBrowser.open('maps://?q=<latitude>,<longitude>', '_system');
Android
$cordovaInAppBrowser.open('geo:0,0?q=<latitude>,<longitude>', '_system');
And finally you need to enable the geo uri in your config.xml like so:
<allow-intent href="geo:*" />
The plugin doesn't allow to create multiple maps instance More detail.
But you can achieve this by using this code
var map = plugin.google.maps.Map.getMap(div);
map.clear();
map.off();
map.trigger("test");
map.addEventListener(plugin.google.maps.event.MAP_READY);
Anybody know how to show driving directions between two points using ArcGis javascript api with location co ordinates only (not with place name or address)
I need to implement driving directions to a particular point(point B) from current user location(point A, I have the user location from browser) where the destination Point B's latitude and longitude is stored in my database,,,
pls help '
You can use RouteTask to get the result.
var routeParams = new RouteParameters();
routeParams.stops = new FeatureSet();
routeParams.returnRoutes = false;
routeParams.returnDirections = true;
routeParams.directionsLengthUnits = Units.MILES;
routeParams.outSpatialReference = new SpatialReference({ wkid:102100 });
if the locations are in lat long, convert it to WebMercator using webMercatorUtils
routeParams.stops.features.add(new Graphic(pointA));
routeParams.stops.features.add(new Graphic(pointB));
You can use your own service url here if you have one.
var routeTask = new RouteTask("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World");
routeTask .solve(routeParams, function(result){
//handle route result.
}, function(err){
//handle error
});
If you are using Directions dijit, it already has methods to add Point geometry. https://developers.arcgis.com/javascript/3/jsapi/directions-amd.html#addstop
Hope this was helpful.
I am trying to implement google custom search in an angular js website.
When I click on the search button it does not display me anything, but the url is updated to the url.
I have followed the steps mentioned in the documentation by google.
I am not sure what I am doing wrong?
My search bar is located on the home page as -
<gcse:searchbox-only enableAutoComplete="true" resultsUrl="#/searchresult" lr="lang_en" queryParameterName="search"></gcse:searchbox-only>
my search result has -
<gcse:searchresults-only lr="lang_en"></gcse:searchresults-only>
Any input is much appreciated.
Thanks,
You may have more than one problem happening at the same time...
1. Query Parameter mismatch
Your searchresults-only does not match the queryParameterName specified on gcse:searchbox-only.
Index.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
Search.html
<gcse:searchresults-only queryParameterName="search"></gcse:searchresults-only>
2. Angular.js is blocking the flow of Google CSE
Under normal circumstances, Google Search Element will trigger an HTTP GET with the search parameter. However, since you are dealing with a one-page application, you may not see the query parameter. If that suspicion is true when you target resultsUrl="#/searchresult", then you have two options:
Force a HTTP GET on resultsUrl="http://YOURWEBSITE/searchresult". You may have to match routes, or something along those lines in order to catch the REST request (Ember.js is really easy to do so, but I haven't done in Angular.js yet.)
Use JQuery alongside Angular.js to get the input from the user on Index.html and manually trigger a search on search.html. How would you do it? For the index.html you would do something like below and for the results you would implement something like I answered in another post.
Index.html
<div>GSC SEARCH BUTTON HOOK: <strong><div id="search_button_hook">NOT ACTIVATED.</div></strong></div>
<div>GSC SEARCH TEXT: <strong><div id="search_text_hook"></div></strong></div>
<gcse:search ></gcse:search>
Index.js
//Hook a callback into the rendered Google Search. From my understanding, this is possible because the outermost rendered div has id of "___gcse_0".
window.__gcse = {
callback: googleCSELoaded
};
//When it renders, their initial customized function cseLoaded() is triggered which adds more hooks. I added comments to what each one does:
function googleCSELoaded() {
$(".gsc-search-button").click(function() {
$("#search_button_hook").text('HOOK ACTIVATED');
});
$("#gsc-i-id1").keydown(function(e) {
if (e.which == 13) {
$("#enter_keyboard_hook").text('HOOK ACTIVATED');
}
else{
$("#search_text_hook").text($("#gsc-i-id1").val());
}
});
}
(function() {
var cx = '001386805071419863133:cb1vfab8b4y';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
I have a live version of the index.html code, but I don't make promises that will be permanently live since it is hosted in my NDSU FTP.
I have a Spring MVC 3 app (that uses JSP) running on Google App Engine and saving information on the Datastore. I'm using the Google Maps API v3 to project some of the data on maps by drawing shapes, colouring etc. My database will potentionally hold millions of entries.
I was wondering what the best way is to keep pulling data from the datastore and project them on the map until there are no more database entries left to project. I need to do this to avoid hitting the 30 seconds limit (and getting a DeadlineExceededException) but also for good user experience.
Is it worth using GWT?
Any advice would be great.
Thanks!
You could use a cursor similar to the pagination technique described here:
Pagination in Google App Engine with Java
When your page with the map loads, have it make an AJAX request with a blank cursor parameter. The request handler would fetch a small number of entities, then return a response containing them and a cursor (if there are entities remaining).
From the client javascript, after displaying the items on the map, if there is a cursor in the response start a new request with the cursor as an argument. In the request handler if a cursor is provided, use it when making the query.
This will set up a continuous loop of AJAX requests until all items have been fetched and displayed on the map.
Update:
You could write a service which returns JSON something like this:
{
items:
[
{ lat: 1.23, lon: 3.45, abc = 'def' },
{ lat: 2.34, lon: 4.56, abc = 'ghi' }
],
cursor: '1234abcd'
}
So, it contains an array of items (with lat/lon and whatever other info you need per item), as well as a cursor (which would be null when the last entity has been fetched).
Then, on the client side I would recommend using jQuery's ajax function to make the ajax calls, something like this:
$(document).ready(function()
{
// first you may need to initialise the map - then start fetching items
fetchItems(null);
});
function fetchItems(cursor)
{
// build the url to request the items - include the cursor as an argument
// if one is specified
var url = "/path/getitems";
if (cursor != null)
url += "?cursor=" + cursor;
// start the ajax request
$.ajax({
url: url,
dataType: 'json',
success: function(response)
{
// now handle the response - first loop over the items
for (i in response.items)
{
var item = response.items[i];
// add something to the map using item.lat, item.lon, etc
}
// if there is a cursor in the response then there are more items,
// so start fetching them
if (response.cursor != null)
fetchItems(response.cursor);
}});
}