Чтение необработанных данных в геопанды

14

Можно ли читать необработанные данные в a geopandas GeoDataFrame, a la a pandas DataFrame?

Например, следующие работы:

import pandas as pd
import requests
data = requests.get("https://data.cityofnewyork.us/api/geospatial/arq3-7z49?method=export&format=GeoJSON")
pd.read_json(io.BytesIO(r.content))

Следующее не:

import geopandas as gpd
import requests
data = requests.get("https://data.cityofnewyork.us/api/geospatial/arq3-7z49?method=export&format=GeoJSON")
gpd.read_file(io.BytesIO(r.content))

Другими словами, возможно ли считывать геопространственные данные, находящиеся в памяти, без предварительного сохранения этих данных на диск?

Алексей Билогур
источник

Ответы:

16

Вы можете передать json напрямую в конструктор GeoDataFrame:

import geopandas as gpd
import requests
data = requests.get("https://data.cityofnewyork.us/api/geospatial/arq3-7z49?method=export&format=GeoJSON")
gdf = gpd.GeoDataFrame(data.json())
gdf.head()

Выходы:

                                            features               type
0  {'type': 'Feature', 'geometry': {'type': 'Poin...  FeatureCollection
1  {'type': 'Feature', 'geometry': {'type': 'Poin...  FeatureCollection
2  {'type': 'Feature', 'geometry': {'type': 'Poin...  FeatureCollection
3  {'type': 'Feature', 'geometry': {'type': 'Poin...  FeatureCollection
4  {'type': 'Feature', 'geometry': {'type': 'Poin...  FeatureCollection

Для поддерживаемых однофайловых форматов или сжатых шейп-файлов вы можете использовать fiona.BytesCollectionи GeoDataFrame.from_features:

import requests
import fiona
import geopandas as gpd

url = 'http://www.geopackage.org/data/gdal_sample.gpkg'
request = requests.get(url)
b = bytes(request.content)
with fiona.BytesCollection(b) as f:
    crs = f.crs
    gdf = gpd.GeoDataFrame.from_features(f, crs=crs)
    print(gdf.head())
и для архивированных шейп-файлов (поддерживается с fiona 1.7.2 )
url = 'https://www2.census.gov/geo/tiger/TIGER2010/STATE/2010/tl_2010_31_state10.zip'
request = requests.get(url)
b = bytes(request.content)
with fiona.BytesCollection(b) as f:
    crs = f.crs
    gdf = gpd.GeoDataFrame.from_features(f, crs=crs)
    print(gdf.head())

Вы можете узнать, какие форматы поддерживает Fiona, используя что-то вроде:

import fiona
for name, access in fiona.supported_drivers.items():
    print('{}: {}'.format(name, access))

И хакерский обходной путь для чтения сжатых данных в памяти в Фионе 1.7.1 или более ранней версии:

import requests
import uuid
import fiona
import geopandas as gpd
from osgeo import gdal

request = requests.get('https://github.com/OSGeo/gdal/blob/trunk/autotest/ogr/data/poly.zip?raw=true')
vsiz = '/vsimem/{}.zip'.format(uuid.uuid4().hex) #gdal/ogr requires a .zip extension

gdal.FileFromMemBuffer(vsiz,bytes(request.content))
with fiona.Collection(vsiz, vsi='zip', layer ='poly') as f:
    gdf = gpd.GeoDataFrame.from_features(f, crs=f.crs)
    print(gdf.head())
user2856
источник
Это работает для GeoJSON, который отвечает на вопрос. Но это не будет работать для других геопространственных форматов файлов, таких как шейп-файлы, KML или KMZ. Знаете ли вы об обходном пути для этих случаев?
Алексей Билогур
Небольшое уточнение в порядке. GeoPandas и Fiona поддерживают шейп-файлы и KML, но они не могут полностью поддерживать одноразовые API, такие как City of New York. Кроме того, BytesCollectionполностью работает, но, вероятно, будет удален в будущей версии в пользу одного из вариантов в github.com/Toblerity/Fiona/issues/409 .
sgillies
Спасибо. @sgillies, следует ли это открывать как запрос функции geopandasили лучше подождать изменений, о которых вы здесь упомянули ?
Алексей Билогур
@sgillies Вы заявляете, что Fiona поддерживает KML в своем комментарии выше, но DriverError: unsupported driver: 'KML'возникает при попытке открыть KML, так как он не является обязательным supported_drivers(используется Fiona 1.7.1), и я заметил несколько проблем, связанных с. отсутствие поддержки KML (# 23 и # 97). Fiona поддерживает KML?
user2856
Спасибо, что обнаружили from_featuresметод. Спас мой день!
Jlandercy
3

Поскольку fiona.BytesCollection, кажется, TopoJSONздесь не работает решение, которое работает для всех без необходимости gdal:

import fiona
import geopandas as gpd
import requests

# parse the topojson file into memory
request = requests.get('https://vega.github.io/vega-datasets/data/us-10m.json')
visz = fiona.ogrext.buffer_to_virtual_file(bytes(request.content))

# read the features from a fiona collection into a GeoDataFrame
with fiona.Collection(visz, driver='TopoJSON') as f:
    gdf = gpd.GeoDataFrame.from_features(f, crs=f.crs)
Mattijn
источник
С geopandas==0.4.0, Fiona==1.8.4и Python 3, я получаю DriverError: unsupported driver: 'TopoJSON'.
Edesz
Вы правы. Он работал по крайней мере до версии 1.7.13изFiona
Mattijn
К сожалению, это не работает. Я пытался следовать вашему примеру на GitHub для сюжетов Альтаира, но это также приводит к точно такой же ошибке в строке gdf = gpd.read_file(counties, driver='TopoJSON'). Я думал, что использование with fiona.Collection...может работать, но, к сожалению, это не так.
edesz
@edesz - это ошибка, и она будет исправлена ​​в Fiona 1.8.5, см. github.com/Toblerity/Fiona/issues/721
Mattijn
2

При использовании Fiona 1.8 это можно (нужно?) Сделать с помощью этого проекта MemoryFileилиZipMemoryFile .

Например:

import fiona.io
import geopandas as gpd
import requests

response = requests.get('http://example.com/Some_shapefile.zip')
data_bytes = response.content

with fiona.io.ZipMemoryFile(data_bytes) as zip_memory_file:
    with zip_memory_file.open('Some_shapefile.shp') as collection:
      geodf = gpd.GeoDataFrame.from_features(collection, crs=collection.crs)
Esmail
источник
0

Самый простой способ - ввести URL-адрес GeoJSON непосредственно в gpd.read (). Я пытался извлечь шейп-файл из zip-файла до этого, используя BytesIO & zipfile, и у меня были проблемы с gpd (особенно Fiona), принимающим файловые объекты.

import geopandas as gpd
import David.SQL_pull_by_placename as sql
import os

os.environ['PROJ_LIB'] = r'C:\Users\littlexsparkee\Anaconda3\Library\share\proj'

geojson_url = f'https://github.com/loganpowell/census-geojson/blob/master/GeoJSON/500k/2018/{sql.state}/block-group.json?raw=true'
census_tracts_gdf = gpd.read_file(geojson_url)
littlexsparkee
источник
0

Я предпочитаю результат, полученный с помощью недокументированного, GeoDataFrame.from_features()а не передачи GeoJSON в конструктор GDF напрямую:

import geopandas as gpd
import requests
data = requests.get("https://data.cityofnewyork.us/api/geospatial/arq3-7z49?method=export&format=GeoJSON")
gpd.GeoDataFrame().from_features(data.json())

Вывод

                       geometry                         name                                url           line objectid                                              notes
0    POINT (-73.99107 40.73005)                     Astor Pl  http://web.mta.info/nyct/service/  4-6-6 Express        1  4 nights, 6-all times, 6 Express-weekdays AM s...
1    POINT (-74.00019 40.71880)                     Canal St  http://web.mta.info/nyct/service/  4-6-6 Express        2  4 nights, 6-all times, 6 Express-weekdays AM s...
2    POINT (-73.98385 40.76173)                      50th St  http://web.mta.info/nyct/service/            1-2        3                              1-all times, 2-nights
3    POINT (-73.97500 40.68086)                    Bergen St  http://web.mta.info/nyct/service/          2-3-4        4           4-nights, 3-all other times, 2-all times
4    POINT (-73.89489 40.66471)             Pennsylvania Ave  http://web.mta.info/nyct/service/            3-4        5                        4-nights, 3-all other times

Получившийся GeoDataFrame имеет правильно настроенный столбец геометрии и все столбцы, как и следовало ожидать, без необходимости удалять вложенные FeatureCollections

dericke
источник