Lecture de données brutes dans des géopandas

14

Est-il possible de lire des données brutes en a geopandas GeoDataFrame, a la a pandas DataFrame?

Par exemple, les travaux suivants:

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))

Ce qui suit ne:

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))

En d'autres termes, est-il possible de lire des données géospatiales qui sont en mémoire sans d'abord enregistrer ces données sur le disque?

Aleksey Bilogur
la source

Réponses:

16

Vous pouvez passer le json directement au constructeur 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()

Les sorties:

                                            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

Pour les formats de fichier unique pris en charge ou les fichiers de formes zippés, vous pouvez utiliser fiona.BytesCollectionet 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())
et pour les fichiers de formes zippés (pris en charge à partir de 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())

Vous pouvez découvrir les formats pris en charge par Fiona en utilisant quelque chose comme:

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

Et une solution de contournement hacky pour lire les données compressées en mémoire dans fiona 1.7.1 ou version antérieure:

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
la source
Cela fonctionne pour GeoJSON, qui répond à la question. Mais cela ne fonctionnerait pas pour d'autres formats de fichiers géospatiaux, comme des fichiers de formes ou KML ou KMZ. Connaissez-vous une solution de contournement pour ces cas?
Aleksey Bilogur
Une petite clarification s'impose. GeoPandas et Fiona prennent en charge les fichiers de formes et KML, mais ils ne peuvent pas entièrement prendre en charge des API uniques comme la ville de New York. En outre, cela BytesCollectionfonctionne totalement, mais va probablement être supprimé dans une future version au profit de l'une des options de github.com/Toblerity/Fiona/issues/409 .
sgillies
Merci. @sgillies devrait-il être ouvert en tant que demande de fonctionnalité geopandas, ou serait-il préférable d'attendre les modifications que vous mentionnez ici ?
Aleksey Bilogur
@sgillies vous déclarez que Fiona prend en charge KML dans votre commentaire ci-dessus, mais DriverError: unsupported driver: 'KML'est soulevé lors de la tentative d'ouverture de KML car ce n'est pas dans le supported_driversdict (en utilisant Fiona 1.7.1) et j'ai remarqué quelques problèmes. manque de support KML (# 23 & # 97). Fiona prend-il en charge KML?
user2856
Merci d'avoir repéré la from_featuresméthode. J'ai sauvé ma journée!
jlandercy
3

Puisque fiona.BytesCollectionne semble pas fonctionner pour TopoJSONici une solution qui fonctionne pour tous sans avoir besoin de 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
la source
Avec geopandas==0.4.0, Fiona==1.8.4et Python 3, je reçois DriverError: unsupported driver: 'TopoJSON'.
edesz
Tu as raison. Il fonctionnait jusqu'à au moins la version 1.7.13deFiona
Mattijn
Il est regrettable que cela ne fonctionne pas. J'essayais de suivre votre exemple sur GitHub pour les tracés choroplèthes Altair, mais cela génère exactement la même erreur sur la ligne gdf = gpd.read_file(counties, driver='TopoJSON'). Je pensais que l'utilisation with fiona.Collection...pourrait fonctionner mais malheureusement ce n'est pas le cas.
edesz
@edesz c'était un bug et sera corrigé dans Fiona 1.8.5, voir: github.com/Toblerity/Fiona/issues/721
Mattijn
2

Lorsque vous utilisez Fiona 1.8, cela peut (doit?) Être fait en utilisant le projet MemoryFileouZipMemoryFile .

Par exemple:

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
la source
0

Le moyen le plus simple consiste à saisir l'URL GeoJSON directement dans gpd.read (). J'avais essayé d'extraire un fichier de formes d'un zip avant cela en utilisant BytesIO & zipfile et j'avais des problèmes avec gpd (spécifiquement Fiona) acceptant des objets de type fichier.

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
la source
0

Je préfère le résultat obtenu en utilisant le non documenté GeoDataFrame.from_features()plutôt que de passer directement le GeoJSON au constructeur 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())

Production

                       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

Le GeoDataFrame résultant a la colonne de géométrie correctement définie et toutes les colonnes comme je m'y attendrais, sans avoir besoin de désincorporer des FeatureCollections

dericke
la source