Changeset 15442 in josm


Ignore:
Timestamp:
2019-10-07T23:48:07+02:00 (2 months ago)
Author:
Don-vip
Message:

fix #17495 - limited support of named CRS in GeoJSON import

Location:
trunk
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/GeoJSONReader.java

    r15427 r15442  
    2020import javax.json.stream.JsonParser.Event;
    2121
     22import org.openstreetmap.josm.data.coor.EastNorth;
    2223import org.openstreetmap.josm.data.coor.LatLon;
    2324import org.openstreetmap.josm.data.osm.DataSet;
     
    2728import org.openstreetmap.josm.data.osm.RelationMember;
    2829import org.openstreetmap.josm.data.osm.Way;
     30import org.openstreetmap.josm.data.projection.Projection;
     31import org.openstreetmap.josm.data.projection.Projections;
    2932import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    3033import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     
    3740public class GeoJSONReader extends AbstractReader {
    3841
     42    private static final String CRS = "crs";
     43    private static final String NAME = "name";
     44    private static final String LINK = "link";
    3945    private static final String COORDINATES = "coordinates";
    4046    private static final String FEATURES = "features";
     
    4349    private static final String TYPE = "type";
    4450    private JsonParser parser;
     51    private Projection projection = Projections.getProjectionByCode("EPSG:4326"); // WGS 84
    4552
    4653    GeoJSONReader() {
     
    5259    }
    5360
    54     private void parse() {
     61    private void parse() throws IllegalDataException {
    5562        while (parser.hasNext()) {
    5663            Event event = parser.next();
     
    6269    }
    6370
    64     private void parseRoot(final JsonObject object) {
     71    private void parseRoot(final JsonObject object) throws IllegalDataException {
     72        parseCrs(object.getJsonObject(CRS));
    6573        switch (object.getString(TYPE)) {
    6674            case "FeatureCollection":
     
    7886    }
    7987
     88    /**
     89     * Parse CRS as per https://geojson.org/geojson-spec.html#coordinate-reference-system-objects.
     90     * CRS are obsolete in RFC7946 but still allowed for interoperability with older applications.
     91     * Only named CRS are supported.
     92     *
     93     * @param crs CRS JSON object
     94     * @throws IllegalDataException in case of error
     95     */
     96    private void parseCrs(final JsonObject crs) throws IllegalDataException {
     97        if (crs != null) {
     98            // Inspired by https://github.com/JOSM/geojson/commit/f13ceed4645244612a63581c96e20da802779c56
     99            JsonObject properties = crs.getJsonObject("properties");
     100            if (properties != null) {
     101                switch (crs.getString(TYPE)) {
     102                    case NAME:
     103                        String crsName = properties.getString(NAME);
     104                        if ("urn:ogc:def:crs:OGC:1.3:CRS84".equals(crsName)) {
     105                            // https://osgeo-org.atlassian.net/browse/GEOT-1710
     106                            crsName = "EPSG:4326";
     107                        } else if (crsName.startsWith("urn:ogc:def:crs:EPSG:")) {
     108                            crsName = crsName.replace("urn:ogc:def:crs:", "");
     109                        }
     110                        projection = Optional.ofNullable(Projections.getProjectionByCode(crsName))
     111                                .orElse(Projections.getProjectionByCode("EPSG:4326")); // WGS84
     112                        break;
     113                    case LINK: // Not supported (security risk)
     114                    default:
     115                        throw new IllegalDataException(crs.toString());
     116                }
     117            }
     118        }
     119    }
     120
    80121    private void parseFeatureCollection(final JsonArray features) {
    81122        for (JsonValue feature : features) {
    82123            if (feature instanceof JsonObject) {
    83                 JsonObject item = (JsonObject) feature;
    84                 parseFeature(item);
     124                parseFeature((JsonObject) feature);
    85125            }
    86126        }
     
    117157
    118158    private void parseGeometryCollection(final JsonObject feature, final JsonObject geometry) {
    119         JsonArray geometries = geometry.getJsonArray("geometries");
    120         for (JsonValue jsonValue : geometries) {
     159        for (JsonValue jsonValue : geometry.getJsonArray("geometries")) {
    121160            parseGeometry(feature, jsonValue.asJsonObject());
    122161        }
     
    156195    }
    157196
     197    private LatLon getLatLon(final JsonArray coordinates) {
     198        return projection.eastNorth2latlon(new EastNorth(
     199                coordinates.getJsonNumber(0).doubleValue(),
     200                coordinates.getJsonNumber(1).doubleValue()));
     201    }
     202
    158203    private void parsePoint(final JsonObject feature, final JsonArray coordinates) {
    159         double lat = coordinates.getJsonNumber(1).doubleValue();
    160         double lon = coordinates.getJsonNumber(0).doubleValue();
    161         Node node = createNode(lat, lon);
    162         fillTagsFromFeature(feature, node);
     204        fillTagsFromFeature(feature, createNode(getLatLon(coordinates)));
    163205    }
    164206
    165207    private void parseMultiPoint(final JsonObject feature, final JsonObject geometry) {
    166         JsonArray coordinates = geometry.getJsonArray(COORDINATES);
    167         for (JsonValue coordinate : coordinates) {
     208        for (JsonValue coordinate : geometry.getJsonArray(COORDINATES)) {
    168209            parsePoint(feature, coordinate.asJsonArray());
    169210        }
     
    171212
    172213    private void parseLineString(final JsonObject feature, final JsonArray coordinates) {
    173         if (coordinates.isEmpty()) {
    174             return;
    175         }
    176         createWay(coordinates, false)
    177             .ifPresent(way -> fillTagsFromFeature(feature, way));
     214        if (!coordinates.isEmpty()) {
     215            createWay(coordinates, false)
     216                .ifPresent(way -> fillTagsFromFeature(feature, way));
     217        }
    178218    }
    179219
    180220    private void parseMultiLineString(final JsonObject feature, final JsonObject geometry) {
    181         JsonArray coordinates = geometry.getJsonArray(COORDINATES);
    182         for (JsonValue coordinate : coordinates) {
     221        for (JsonValue coordinate : geometry.getJsonArray(COORDINATES)) {
    183222            parseLineString(feature, coordinate.asJsonArray());
    184223        }
     
    186225
    187226    private void parsePolygon(final JsonObject feature, final JsonArray coordinates) {
    188         if (coordinates.size() == 1) {
     227        final int size = coordinates.size();
     228        if (size == 1) {
    189229            createWay(coordinates.getJsonArray(0), true)
    190230                .ifPresent(way -> fillTagsFromFeature(feature, way));
    191         } else if (coordinates.size() > 1) {
     231        } else if (size > 1) {
    192232            // create multipolygon
    193233            final Relation multipolygon = new Relation();
     
    196236                .ifPresent(way -> multipolygon.addMember(new RelationMember("outer", way)));
    197237
    198             for (JsonValue interiorRing : coordinates.subList(1, coordinates.size())) {
     238            for (JsonValue interiorRing : coordinates.subList(1, size)) {
    199239                createWay(interiorRing.asJsonArray(), true)
    200240                    .ifPresent(way -> multipolygon.addMember(new RelationMember("inner", way)));
     
    207247
    208248    private void parseMultiPolygon(final JsonObject feature, final JsonObject geometry) {
    209         JsonArray coordinates = geometry.getJsonArray(COORDINATES);
    210         for (JsonValue coordinate : coordinates) {
     249        for (JsonValue coordinate : geometry.getJsonArray(COORDINATES)) {
    211250            parsePolygon(feature, coordinate.asJsonArray());
    212251        }
    213252    }
    214253
    215     private Node createNode(final double lat, final double lon) {
    216         final Node node = new Node(new LatLon(lat, lon));
     254    private Node createNode(final LatLon latlon) {
     255        final Node node = new Node(latlon);
    217256        getDataSet().addPrimitive(node);
    218257        return node;
     
    224263        }
    225264
    226         final List<LatLon> latlons = coordinates.stream().map(coordinate -> {
    227             final JsonArray jsonValues = coordinate.asJsonArray();
    228             return new LatLon(
    229                 jsonValues.getJsonNumber(1).doubleValue(),
    230                 jsonValues.getJsonNumber(0).doubleValue()
    231             );
    232         }).collect(Collectors.toList());
     265        final List<LatLon> latlons = coordinates.stream().map(
     266                coordinate -> getLatLon(coordinate.asJsonArray())).collect(Collectors.toList());
    233267
    234268        final int size = latlons.size();
  • trunk/test/unit/org/openstreetmap/josm/io/GeoJSONReaderTest.java

    r15427 r15442  
    117117    }
    118118
     119    /**
     120     * Test reading a GeoJSON file with a named CRS.
     121     * @throws Exception in case of error
     122     */
     123    @Test
     124    public void testReadGeoJsonNamedCrs() throws Exception {
     125        try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "geocrs.json"))) {
     126            final List<OsmPrimitive> primitives = new ArrayList<>(new GeoJSONReader()
     127                    .doParseDataSet(in, null)
     128                    .getPrimitives(it -> true));
     129                assertEquals(24, primitives.size());
     130                assertTrue(primitives.stream()
     131                        .filter(it -> areEqualNodes(it, new Node(new LatLon(52.5840213, 13.1724145))))
     132                        .findAny().isPresent());
     133        }
     134    }
     135
    119136    private static boolean areEqualNodes(final OsmPrimitive p1, final OsmPrimitive p2) {
    120137        return (p1 instanceof Node)
    121138            && (p2 instanceof Node)
    122             && ((Node) p1).getCoor().equals(((Node) p2).getCoor());
     139            && ((Node) p1).getCoor().equalsEpsilon(((Node) p2).getCoor());
    123140    }
    124141
Note: See TracChangeset for help on using the changeset viewer.