Fallback to lenient shapefile parsing (#570)

pull/571/head
Michael Barry 2023-04-27 08:30:55 -04:00 zatwierdzone przez GitHub
rodzic 8d0e06c667
commit bf86516fab
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
2 zmienionych plików z 76 dodań i 5 usunięć

Wyświetl plik

@ -21,7 +21,10 @@ import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.OperationNotFoundException;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility that reads {@link SourceFeature SourceFeatures} from the geometries contained in an ESRI shapefile.
@ -33,6 +36,7 @@ import org.opengis.referencing.operation.TransformException;
* Shapefile Specification</a>
*/
public class ShapefileReader extends SimpleReader<SimpleFeature> {
private static final Logger LOGGER = LoggerFactory.getLogger(ShapefileReader.class);
private final FeatureCollection<SimpleFeatureType, org.opengis.feature.simple.SimpleFeature> inputSource;
private final String[] attributeNames;
@ -53,7 +57,7 @@ public class ShapefileReader extends SimpleReader<SimpleFeature> {
CoordinateReferenceSystem src =
sourceProjection == null ? source.getSchema().getCoordinateReferenceSystem() : CRS.decode(sourceProjection);
CoordinateReferenceSystem dest = CRS.decode("EPSG:4326", true);
transformToLatLon = CRS.findMathTransform(src, dest);
transformToLatLon = findMathTransform(input, src, dest);
if (transformToLatLon.isIdentity()) {
transformToLatLon = null;
}
@ -68,6 +72,19 @@ public class ShapefileReader extends SimpleReader<SimpleFeature> {
}
}
private static MathTransform findMathTransform(Path input, CoordinateReferenceSystem src,
CoordinateReferenceSystem dest) throws FactoryException {
try {
return CRS.findMathTransform(src, dest);
} catch (OperationNotFoundException e) {
var result = CRS.findMathTransform(src, dest, true);
LOGGER.warn(
"Failed to parse projection from {} (\"{}\") using lenient mode instead which may result in data inconsistencies",
input.getFileName(), e.getMessage());
return result;
}
}
/**
* Renders map features for all elements from an ESRI Shapefile based on the mapping logic defined in {@code profile}.
* Overrides the coordinate reference system defined in the shapefile.

Wyświetl plik

@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.onthegomap.planetiler.TestUtils;
import com.onthegomap.planetiler.collection.IterableOnce;
import com.onthegomap.planetiler.geo.GeoUtils;
import com.onthegomap.planetiler.stats.Stats;
import com.onthegomap.planetiler.util.FileUtils;
@ -14,13 +13,24 @@ import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.Map;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.referencing.CRS;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.io.TempDir;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
class ShapefileReaderTest {
@TempDir
@ -45,6 +55,51 @@ class ShapefileReaderTest {
testReadShapefile(dest.resolve("shapefile").resolve("stations.shp"));
}
@Test
void testReadShapefileLeniently(@TempDir Path dir) throws IOException, TransformException, FactoryException {
var shpPath = dir.resolve("test.shp");
var dataStoreFactory = new ShapefileDataStoreFactory();
var newDataStore =
(ShapefileDataStore) dataStoreFactory.createNewDataStore(Map.of("url", shpPath.toUri().toURL()));
var builder = new SimpleFeatureTypeBuilder();
builder.setName("the_geom");
builder.setCRS(CRS.parseWKT(
"""
PROJCS["SWEREF99_TM",GEOGCS["GCS_SWEREF99",DATUM["D_SWEREF99",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
"""));
builder.add("the_geom", Point.class);
builder.add("value", Integer.class);
builder.setDefaultGeometry("the_geom");
var type = builder.buildFeatureType();
newDataStore.createSchema(type);
try (var transaction = new DefaultTransaction("create")) {
var typeName = newDataStore.getTypeNames()[0];
var featureSource = newDataStore.getFeatureSource(typeName);
var featureStore = (SimpleFeatureStore) featureSource;
featureStore.setTransaction(transaction);
var collection = new DefaultFeatureCollection();
var featureBuilder = new SimpleFeatureBuilder(type);
featureBuilder.add(TestUtils.newPoint(1, 2));
featureBuilder.add(3);
var feature = featureBuilder.buildFeature(null);
collection.add(feature);
featureStore.addFeatures(collection);
transaction.commit();
}
try (var reader = new ShapefileReader(null, "test", shpPath)) {
assertEquals(1, reader.getFeatureCount());
List<SimpleFeature> features = new ArrayList<>();
reader.readFeatures(features::add);
assertEquals(10.5113, features.get(0).latLonGeometry().getCentroid().getX(), 1e-4);
assertEquals(0, features.get(0).latLonGeometry().getCentroid().getY(), 1e-4);
assertEquals(3, features.get(0).getTag("value"));
}
}
private static void testReadShapefile(Path path) {
try (var reader = new ShapefileReader(null, "test", path)) {
@ -53,8 +108,7 @@ class ShapefileReaderTest {
List<Geometry> points = new ArrayList<>();
List<String> names = new ArrayList<>();
WorkerPipeline.start("test", Stats.inMemory())
.readFromTiny("files", List.of(path))
.addWorker("reader", 1, (IterableOnce<Path> p, Consumer<SimpleFeature> next) -> reader.readFeatures(next))
.fromGenerator("source", reader::readFeatures)
.addBuffer("reader_queue", 100, 1)
.sinkToConsumer("counter", 1, elem -> {
assertTrue(elem.getTag("name") instanceof String);