Как отформатировать ArcGIS FeatureSet / FeatureCollection для использования с Folium - PullRequest
0 голосов
/ 28 марта 2020

Цель: использовать данные расчета времени вождения ArcGIS и отображать результаты с помощью Folium вместо отображения ArcGIS / ESRI.

Объяснение: Используя пакет arcgis, я могу получить данные времени вождения, связанные с данной точкой , предоставляя мне границу, которая будет показана на карте. Данные из ArcGIS, карты просто отлично, используя ArcGIS, однако, это очень медленно отображать и гораздо больше времени для отображения других элементов на картах ArcGIS в целом, таких как радиусы, отдельные точки и все что угодно с пользовательским значком. Тем не менее, Folium работает достаточно хорошо для отображения всех этих дополнительных данных и загружается намного быстрее, чем ArcGIS.

Препятствие: я ищу способ использовать эти данные, которые возвращаются из ArcGIS, и затем отображать их с помощью Folium. У меня проблема в том, что данные из ArcGIS, хотя и содержат данные о координатах, имеют слишком большие значения, чтобы быть широтой / долготой. Казалось бы, есть какой-то способ «декодировать» эти значения для использования с Folium, или, возможно, я просто не использую данные из ArcGIS в качестве правильного типа данных, как только я пытаюсь использовать их с Folium.

import pandas
import arcgis
import webview

data = {'address': {0:'2015 Terminal Way'}, 'city': {0:'Reno'}, 'state': {0: 'NV'}, 'zip': {0:'89502'}}
df = pandas.DataFrame(data)
# Obviously I'm unable to include my username and password - which I understand probably limits who can help with this question since without logging in, you wouldn't be able to test my code, but there's nothing I can do about it
my_gis = arcgis.gis.GIS("https://www.arcgis.com", username, password)
address_layer = my_gis.content.import_data(df, address_fields={"Address":"address","City":"city","State":"state","Zip":"zip"})
target = arcgis.features.use_proximity.create_drive_time_areas(input_layer=address_layer, break_values=[5], break_units="Minutes", overlap_policy="Overlap")
# NOTE - ArcGIS documentation states that if an output name is given, the return object is a FeatureSet, but since I leave it blank, it is a FeatureCollection - not sure if that matters either
drivetime_data_geojson = target.query().to_geojson
# The above line returns as below, though trimmed down a bit to save space - I'm so sorry it's so long but pasting it in was the only way I could think of to allow others to test it in Folium

drivetime_data_geojson = '{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry":
{"type": "Polygon", "coordinates": [[[-13334722.942400001, 4801659.346199997], [-13334622.9429,
4801594.495999999], [-13334572.9431, 4801335.0988000035], [-13334572.9431, 4800232.736199997], 
[-13334197.9448, 4800232.736199997], [-13334122.9452, 4800297.577799998], [-13334022.945700001, 
4800167.895199999], [-13334047.945500001, 4800005.794399999], [-13334122.9452, 4799940.954700001], 
[-13334572.9431, 4799940.954700001], [-13334622.9429, 4799227.746699996], [-13334497.943500001, 
4799195.329400003], [-13334447.9437, 4799065.6611], [-13334222.944699999, 4799065.6611], 
[-13333947.9459, 4798968.4108000025], [-13333522.947900001, 4798676.666100003], [-13332722.9515, 
4798579.419799998], [-13332622.952, 4798449.759499997], [-13332722.9515, 4798287.686399996], 
[-13333247.9492, 4798320.100699998]]]}}]}'

# This is how it would be displayed using ArcGIS
# Creating the basemap image
map = my_gis.map(location='2015 Terminal Way Reno, NV 89502')
# Adding the layer to the map
map.add_layer(drivetime_data)
folder_path = os.getcwd()
path_to_map = folder_path + '\\arcgis_to_folium.html'
# webview is just a lightweight browser package that allows for easy viewing of these maps
webview.create_window('MAP', path_to_map)
webview.start() 

Фолиум выглядел бы как-то близко к этому - хотя, как я утверждаю, данные Geo Json прямо сейчас не загружаются:

import folium

folium_map = folium.Map(location=[39.505745, -119.77869])
drivetime_layer = folium.FeatureGroup(name='Drive Time')
folium.GeoJson(drivetime_data_geojson).add_to(drivetime_layer)
drivetime_layer.add_to(folium_map)
folium.LayerControl().add_to(folium_map)
folium_map.save('folium_drivetime_attempt.html')
path_to_map = folder_path + '\\folium_drivetime_attempt.html'
webview.create_window('MAP', path_to_map)
webview.start()

Эта карта загрузится, но выиграла Никакого слоя к нему. Я знаю, что этот geo json будет работать:

drivetime_data_geojson = '{"type": "FeatureCollection", "features": [{
"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [
[[-119.766227, 39.856011], [-120.260612, 39.251417], [-119.067222, 39.246099]]]}}]}'

, потому что он использует координаты GPS вместо этих странных значений из ArcGIS. На самом деле, нужно выяснить, как «декодировать» эти значения из ArcGIS или преобразовать их в правильный тип данных. Любая помощь, предложения или мысли очень ценятся.

1 Ответ

1 голос
/ 28 марта 2020

Есть две проблемы, с которыми вам придется иметь дело. Первый - это преобразование ArcGIS x, y в lat, lon, что можно сделать с помощью такой функции:

import math

def meters_to_coords(y, x):
    if y > 0:
        z = -20037508.3427892
    else:
        z = 20037508.3427892

    lon = (y / z) * 180
    lat = (x / z) * 180
    lat = 180 / math.pi * (2 * math.atan(math.exp(lat * math.pi / 180)) - math.pi / 2)
    return [lon, lat]

Вместо drivetime_data_geojson = target.query().to_geojson используйте drivetime_data_dict = target.query().to_dict() Вы заметите, что когда вы конвертируете ее в словаре ключ «координаты» меняется на «кольца», что важно для следующего шага. Затем выполните итерацию по dict и запустите указанную выше функцию для значений координат, чтобы обновить их до координат GPS. Затем вам нужно настроить формат словаря так, чтобы фолиант мог его распознавать, и, по сути, извлекать данные «колец», которые теперь содержат ваши значения координат и будут выглядеть примерно так:

# Grab the same list of drive time values you sent into the ArcGIS request and use it here, 
# since you'll get a separate set of x,y data for each driving time you request - 
# per above, break_values was just [5]

drivetime_ranges = break_values
first_pass = False

for n in range(len(drivetime_ranges)):
    for i, each in enumerate(drivetime_data_dict['features'][n]['geometry']['rings'][0]):
        drivetime_data_dict['features'][n]['geometry']['rings'][0][i] = meters_to_coords(each[0], each[1])
    if first_pass is not True:
        default_geojson_structure = {
                                     "type": "FeatureCollection", 
                                     "features": [{"type": "Feature",
                                     "geometry": {"type": "Polygon", 
                                     "coordinates":
                                     [drivetime_data_dict['features'][n]
                                     ['geometry']['rings'][0]]}}]
                                     }
        first_pass = True
    else:
        default_geojson_structure['features'].append({"type": "Feature",
        "geometry": {"type": "Polygon", "coordinates":
        [dt_map_overlay['features'][n]['geometry']['rings'][0]]}})

print(default_geojson_structure)

Теперь просто создайте группу Folium FeatureGroup:

dt_layer = folium.FeatureGroup(name='Drive Time')

И загрузите default_geojson_structure в следующую функцию и добавьте ее к своему слою.

folium.GeoJson(default_geojson_structure, style_function=lambda x: {'fillColor': 
'#0000ff'}).add_to(dt_layer)

Сделайте все остальное, как вы сделали в условия сохранения карты, а что нет и запуска веб-просмотра, и вы увидите карту с границей вашего времени в пути.

enter image description here

...