如何格式化 ArcGIS FeatureSet/FeatureCollection 以与 Folium 一起使用
How to format an ArcGIS FeatureSet/FeatureCollection to be used with Folium
目标:使用 ArcGIS 的行驶时间计算数据并使用 Folium 而非 ArcGIS/ESRI 的制图显示结果。
说明:使用 arcgis 包,我能够获取给定点周围的行驶时间数据,为我提供要在地图上显示的边界区域。来自 ArcGIS 的数据,使用 ArcGIS 地图很好,但是,显示速度非常慢,并且通常在 ArcGIS 地图上显示其他元素(例如半径、单个点和任何带有自定义图标的元素)要耗费更多时间。但是,Folium 可以很好地显示所有这些额外数据,并且加载速度比 ArcGIS 快得多。
障碍:我正在寻找一种方法来使用从 ArcGIS 返回的数据,然后使用 Folium 显示它。我遇到的问题是来自 ArcGIS 的数据,虽然它说它包含坐标数据,但其值太大而不是 latitude/longitude。似乎有某种方法可以 'decode' 这些值与 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()
Folium 看起来很接近这个——尽管正如我所说,GeoJson 数据现在没有引入:
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()
这张地图将会加载,但其中没有任何图层。我知道这个 geojson 会起作用:
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 'decode' 这些值或将其转换为正确数据类型的问题。非常感谢任何帮助、建议或想法。
您必须处理两个问题。第一个是将 ArcGIS x、y 转换为纬度、经度,这可以通过这样的函数完成:
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()
您会注意到,当您将其转换为字典时,键 'coordinates' 更改为 'rings' 这对于下一步。接下来,根据坐标值迭代字典和 运行 上述函数,以将它们更新为 GPS 坐标。然后你需要调整字典的格式,使其成为 folium 可以识别的格式,并从本质上拉入现在包含你的坐标值的 'rings' 数据,并且将是这样的:
# 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)
剩下的就像你从那里做的那样,在保存地图和不保存地图以及 运行 网络视图方面,你会看到地图和你的驾驶时间边界。
目标:使用 ArcGIS 的行驶时间计算数据并使用 Folium 而非 ArcGIS/ESRI 的制图显示结果。
说明:使用 arcgis 包,我能够获取给定点周围的行驶时间数据,为我提供要在地图上显示的边界区域。来自 ArcGIS 的数据,使用 ArcGIS 地图很好,但是,显示速度非常慢,并且通常在 ArcGIS 地图上显示其他元素(例如半径、单个点和任何带有自定义图标的元素)要耗费更多时间。但是,Folium 可以很好地显示所有这些额外数据,并且加载速度比 ArcGIS 快得多。
障碍:我正在寻找一种方法来使用从 ArcGIS 返回的数据,然后使用 Folium 显示它。我遇到的问题是来自 ArcGIS 的数据,虽然它说它包含坐标数据,但其值太大而不是 latitude/longitude。似乎有某种方法可以 'decode' 这些值与 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()
Folium 看起来很接近这个——尽管正如我所说,GeoJson 数据现在没有引入:
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()
这张地图将会加载,但其中没有任何图层。我知道这个 geojson 会起作用:
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 'decode' 这些值或将其转换为正确数据类型的问题。非常感谢任何帮助、建议或想法。
您必须处理两个问题。第一个是将 ArcGIS x、y 转换为纬度、经度,这可以通过这样的函数完成:
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()
您会注意到,当您将其转换为字典时,键 'coordinates' 更改为 'rings' 这对于下一步。接下来,根据坐标值迭代字典和 运行 上述函数,以将它们更新为 GPS 坐标。然后你需要调整字典的格式,使其成为 folium 可以识别的格式,并从本质上拉入现在包含你的坐标值的 'rings' 数据,并且将是这样的:
# 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)
剩下的就像你从那里做的那样,在保存地图和不保存地图以及 运行 网络视图方面,你会看到地图和你的驾驶时间边界。