Programmatic Panning in map with Python API (map.center)

577
5
02-14-2022 01:04 PM
Arne_Gelfert
Occasional Contributor III

After using the Python API mostly for admin purpose in the past, I'm getting a little more into weeds of analytical and data science applications. So I put together a routine that works great for a single location (LL) and leverages the a map in Jupyter Notebooks, Basically something like...

 

from arcgis import GIS
portalUrl = r' ... my Portal url '
mygis = GIS(portalUrl)
location_coords = [lat, lon]
zoomlevel = some number
myMap = mygis.map(location=location_coords, zoomlevel=zoomlevel)
myMap

 

This works great and puts out a Web Mercator (EPSG 3857) map that I can add layers to - whatever!

What if I want to move/pan to a different location in the map? Say I have a list of locations to process (which I do). The documentation here suggest that I should be able to do the following to shift the map:

 

new_location_coords = [new_lat,new_lon]
myMap.center = new_location_coords

 

This in fact updates the map in the notebook, i.e. pans to the new location centered on the new coordinates.

But when I then call:

 

myMap.center

 

I get:

 

{'spatialReference': {'latestWkid': 3857, 'wkid': 102100}, 'x': original value, 'y': original value}

 

where 'original value' the original Web Mercator coordinates from my original Lat/Lon;s. So all the panning does not change the original definition of the map. Am I doing something wrong? Or is this be design or is it a bug? I'd like to be able to use the new center of the map to build some bounding boxes and things like that) but right now, I can't. - Thanks for any feedback.

0 Kudos
5 Replies
DanPatterson
MVP Esteemed Contributor

arcgis.gis module — center

it may be a noun verb thing, according to the docs, you need to provide info to get the map center(ed) at a location rather than the map center(which is providing the original values since you haven't changed anything)


... sort of retired...
0 Kudos
Arne_Gelfert
Occasional Contributor III

Thanks for stepping away from retirement for a moment, Dan. But I don't know what you mean. The line:

myMap.center = new_location_coords

appears to re-center the map around new coordinates, i.e. the map updates. But why do these new coordinates not stick for "center"? It's like the setter refreshes the map but does not set the property correctly.

0 Kudos
DanPatterson
MVP Esteemed Contributor

That is what I meant about a noun versus a verb, center moves it there as a verb, center is what it is as a noun.  I didn't see anything about center establishing new coordinates as the "center" after the move.  A subtle but important distinction, which you can explore since the help is a bit vague as to whether it is a property or a method in python and if it is a method, does it change the property


... sort of retired...
0 Kudos
Arne_Gelfert
Occasional Contributor III

Ok, I get what you're saying. But I think it doesn't help me along. Sadly, I think this is just another ESRI half baked feature that's either not intuitive at all or buggy.

If I do this as in previous example :

 

myMap = mygis.map(location=location_coords, zoomlevel=zoomlevel)
myMap

 

I get a map showing in my notebook. I actually don't need to see the map but I found that certain things don't work unless you actually call the map. So I show it.

Now, a simplified version of what I tried previously looked like this:

for loc in [[40,80],[45,85],[50,90]]:
   myMap.center = loc
   print(myMap.center)
   time.sleep(2)   # so I can watch in amazement

Here, the map advanced to the new extent, centered around 'loc'. But when calling the center property (and I believe this is a property not a method), I find that it never changes.

When, however, I try this:

loc = [42,-85]
myMap.center = loc
print(myMap.center)
>>>{'spatialReference': {'latestWkid': 3857, 'wkid': 102100}, 'x': -9684795.6990148, 'y': 5465442.183322752}
=== THEN IN NEW JUPYTER CELL ===
loc = [42,-85]
myMap.center = loc
print(myMap.center)
>>>{'spatialReference': {'latestWkid': 3857, 'wkid': 102100}, 'x': -9128198.245048434, 'y': 5012341.663847516}
=== THEN IN NEW JUPYTER CELL ===
loc = [44,-87]
myMap.center = loc
print(myMap.center)
>>>{'spatialReference': {'latestWkid': 3857, 'wkid': 102100}, 'x': -9462156.717428254, 'y': 5160979.444049782}

It works the way I would expect it. The map is updated and shows the new extent (based on center and zoom level) and the map.center property is updated.

So I suspect it must somehow have to do with how the map images that's first rendered with gis.map() is referenced from code in the same or other cells and then updated. This may be more of a Jupyter internal thing than the Python API. Who knows. Could be by design but is strikes me as more of a bug than a feature!

As always, Dan, thanks for taking the time to take a look. Haven't spent as much time in the ESRI Python world as I'd like to over the last 12 months, so feedback from the Jedi Masters is always appreciated!

Arne_Gelfert
Occasional Contributor III

I believe what I have been trying to do here can't be done. It's the way arcgis-map-ipywidget-view.js works and that interacts with my synchronous Python. Here is the JS code.

center_long_lat_changed: function(){
        ///When the center is passed in as a [long,lat] list, add it to the view
        ///Then watch the view until it changes center into the correct format,
        ///which we will then update correctly via the _center model attribute
        esriLoader.loadModules(["esri/core/watchUtils"],
        options).then(([watchUtils]) => {
            var _center_long_lat = this.model.get("_center_long_lat");
            if(_center_long_lat.length === 2){
                console.log("Converting [long, lat] center to standard center...");
                //Set up the callback when the view's center is ready for consuption
                watchUtils.once(this.activeView, "center", () => {
                    console.log("Center is converted");
                    var view_center = JSON.parse(JSON.stringify(this.activeView.center));
                    this.model.set("_center", view_center);
                    this.model.set("_center_long_lat", []);
                    this.model.save_changes();
                })
                //Actually update the center on the activeView to trigger the above
                this.activeView.center = _center_long_lat
            }
       }).catch((err) =>{
            this._displayErrorBox("Error while modifying center_long_lat.");
            console.warn("Error on center_long_lat"); console.warn(err);
        });

Throw in some break points and look at how lat/lat and X/Y change for 

this.activeView.center

 and appears to all be there. My Python code just doesn't wait for things to update. If anyone has a suggestion for a work-around, holler. In the meantime, I'm moving on.

0 Kudos