Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Use shapefile crs attribute when possible #2307

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions lib/cartopy/feature/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,10 @@ def geometries(self):
path = shapereader.natural_earth(resolution=self.scale,
category=self.category,
name=self.name)
geometries = tuple(shapereader.Reader(path).geometries())
reader = shapereader.Reader(path)
if reader.crs is not None:
self._crs = reader.crs
geometries = tuple(reader.geometries())
_NATURAL_EARTH_GEOM_CACHE[key] = geometries
else:
geometries = _NATURAL_EARTH_GEOM_CACHE[key]
Expand Down Expand Up @@ -422,7 +425,10 @@ def intersecting_geometries(self, extent):
# Load GSHHS geometries from appropriate shape file.
# TODO selective load based on bbox of each geom in file.
path = shapereader.gshhs(scale, level)
geoms = tuple(shapereader.Reader(path).geometries())
reader = shapereader.Reader(path)
if reader.crs is not None:
self._crs = reader.crs
geoms = tuple(reader.geometries())
GSHHSFeature._geometries_cache[(scale, level)] = geoms
for geom in geoms:
if extent is None or extent_geom.intersects(geom):
Expand Down
17 changes: 16 additions & 1 deletion lib/cartopy/io/shapereader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
from pathlib import Path
from urllib.error import HTTPError

from pyproj import CRS
import shapefile
import shapely.geometry as sgeom

from cartopy import config
import cartopy.crs as ccrs
from cartopy.io import Downloader


Expand Down Expand Up @@ -136,6 +138,13 @@ class BasicReader:
def __init__(self, filename, bbox=None, **kwargs):
# Validate the filename/shapefile
self._reader = reader = shapefile.Reader(filename, **kwargs)
# Try to set the CRS from the .prj file if it exists
self.crs = None
try:
with open(Path(filename).with_suffix('.prj'), 'rt') as fobj:
self.crs = ccrs.CRS(CRS.from_wkt(fobj.read()))
except FileNotFoundError:
pass
self._bbox = bbox
if reader.shp is None or reader.shx is None or reader.dbf is None:
raise ValueError("Incomplete shapefile definition "
Expand Down Expand Up @@ -195,7 +204,13 @@ class FionaReader:

def __init__(self, filename, bbox=None, **kwargs):
self._data = []

# Try to set the CRS from the .prj file if it exists
self.crs = None
try:
with open(Path(filename).with_suffix('.prj'), 'rt') as fobj:
self.crs = ccrs.CRS(CRS.from_wkt(fobj.read()))
except FileNotFoundError:
pass
with fiona.open(filename, **kwargs) as f:
if bbox is not None:
assert len(bbox) == 4
Expand Down
16 changes: 16 additions & 0 deletions lib/cartopy/tests/test_shapereader.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def test_record(self):
assert actual == expected
assert lake_record.geometry == self.test_lake_geometry

def test_no_included_projection_file(self):
# No .prj file included with the lakes shapefile
assert self.reader.crs is None

def test_bounds(self):
if isinstance(self.reader, shp.BasicReader):
# tests that a file which has a record with a bbox can
Expand Down Expand Up @@ -120,3 +124,15 @@ def test_record(self):
if key in expected_attributes:
assert value == expected_attributes[key]
assert river_record.geometry == self.test_river_geometry

def test_included_projection_file(self):
# This shapefile includes a .prj definition
wkt = ('GEOGCRS["WGS 84",DATUM["World Geodetic System 1984",'
'ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],'
'ID["EPSG",6326]],PRIMEM["Greenwich",0,'
'ANGLEUNIT["Degree",0.0174532925199433]],CS[ellipsoidal,2],'
'AXIS["longitude",east,ORDER[1],'
'ANGLEUNIT["Degree",0.0174532925199433]],'
'AXIS["latitude",north,ORDER[2],'
'ANGLEUNIT["Degree",0.0174532925199433]]]')
assert self.reader.crs.to_wkt() == wkt