2

I'm trying to read this GeoJSON from this string:

{

            "type": "FeatureCollection",

            "features": [

            {

                "type": "Feature",

                "geometry": {

                    "type": "Polygon",

                    "coordinates": [[[-69.0062266588211,

                    9.27562217679211],

                    [-43.517945408821106,

                    -2.811371193331128],

                    [-37.014039158821106,

                    -9.795677582829732],

                    [-72.61157142049046,

                    4.701436497353403],

                    [-69.0062266588211,

                    9.27562217679211]]]

                },

                "properties": {

                    "name": "DOUBLE OAKS CENTER",

                    "address": "1326 WOODWARD AV"

                }

            },

            {

                "type": "Feature",

                "geometry": {

                    "type": "Point",

                    "coordinates": [

                        -80.837753,

                        35.249801

                    ]

                },

                "properties": {

                    "name": "DOUBLE OAKS CENTER",

                    "address": "1326 WOODWARD AV"

                }

            }

            ]

        }

Via this code:

@SuppressWarnings("unchecked")
    public static final FeatureCollection<SimpleFeatureType, SimpleFeature> jsonToFeatureCollection(String jsonContent) throws IOException{

        GeometryJSON geometryJson = new GeometryJSON(15);
        FeatureJSON featureJson = new FeatureJSON(geometryJson);
        Reader stringReader = null;
        @SuppressWarnings("rawtypes")
        FeatureCollection featureCollection = null;

        try{
               stringReader = new StringReader(jsonContent);
               featureCollection = featureJson.readFeatureCollection(stringReader);
        }finally{
                        stringReader.close();
        }
        return featureCollection;

    }

And then re-printing it to check if all is fine, but it seems I don't get the same data that was in the string. In the specific the second geometry that is a Point is reported as a Polygon (with correct coordinates, just repeated geometry=POLYGON ((-80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801))

I print data via this code:

featureCollection = MarshallingTool.jsonToFeatureCollection(this.jsonContent);


                 featureIterator = featureCollection.features();

                 System.out.println("#### FEATURE COLLECTION ####");
                 System.out.println("FeatureCollection: size:" + featureCollection.size());
                 System.out.println("FeatureCollection: ID:" + featureCollection.getID());
                 System.out.println("FeatureCollection: Name:" + featureCollection.getSchema().getName().toString());
                 //System.out.println("FeatureCollection: ID:" + featureCollection.getSchema().getCoordinateReferenceSystem().toWKT());


                 while(featureIterator.hasNext()){
                             aFeature = (SimpleFeature) featureIterator.next();
                             System.out.println("######## FEATURE #######");
                             //System.out.println("Name:" + aFeature.getName());
                             System.out.println("2" + aFeature.getType().getGeometryDescriptor().getName());
                             System.out.println("3" + aFeature.getDefaultGeometryProperty().getType().getName().getLocalPart());
                             System.out.println("4" + aFeature.getIdentifier().getID());
                             System.out.println("5" + aFeature.getType());
                             System.out.println("6" + aFeature.getDefaultGeometryProperty());
                             System.out.println("7" + aFeature.getDefaultGeometry());

                             Map<Object, Object> userData = aFeature.getUserData();
                             Collection<Property> properties = aFeature.getProperties();

                             for(Property property:properties){
                                    System.out.println(property.getName() + "=" + property.getValue());
                             }

                             Set<Object> keys = userData.keySet();

                             for(Object aKey:keys){
                                    System.out.println(aKey + "=" + userData.get(aKey));
                             }

                             System.out.println("##############");         
               }

And when I re-print it I get instead:

#### FEATURE COLLECTION ####
FeatureCollection: size:2
FeatureCollection: ID:featureCollection
FeatureCollection: Name:http://geotools.org:feature
######## FEATURE #######
2geometry
3geometry
4feature-0
5SimpleFeatureTypeImpl http://geotools.org:feature identified extends Feature(name:name,address:address,geometry:geometry)
6GeometryAttributeImpl:geometry=POLYGON ((-69.0062266588211 9.27562217679211, -43.517945408821106 -2.811371193331128, -37.014039158821106 -9.795677582829732, -72.61157142049046 4.701436497353403, -69.0062266588211 9.27562217679211))
7POLYGON ((-69.0062266588211 9.27562217679211, -43.517945408821106 -2.811371193331128, -37.014039158821106 -9.795677582829732, -72.61157142049046 4.701436497353403, -69.0062266588211 9.27562217679211))
name=POLIGON DOUBLE OAKS CENTER
address=POLIGON 1326 WOODWARD AV
geometry=POLYGON ((-69.0062266588211 9.27562217679211, -43.517945408821106 -2.811371193331128, -37.014039158821106 -9.795677582829732, -72.61157142049046 4.701436497353403, -69.0062266588211 9.27562217679211))
##############
######## FEATURE #######
2geometry
3geometry
4feature-1
5SimpleFeatureTypeImpl http://geotools.org:feature identified extends Feature(name:name,address:address,geometry:geometry)
6GeometryAttributeImpl:geometry=POLYGON ((-80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801))
7POLYGON ((-80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801))
name=POINT DOUBLE OAKS CENTER
address=POINT 1326 WOODWARD AV
geometry=POLYGON ((-80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801, -80.837753 35.249801))
##############

Is it possible that the JSON part of the GeoTools API has an issue? Do you see anything wrong in the way I read the string or in how I print the content ?

2
  • Additional conformation on the issue: reading the class name Commented Mar 20, 2019 at 16:46
  • Additional conformation on the issue: reading the class name of the feature geaometry: System.out.println("Class: " + aFeature.getDefaultGeometry().getClass().getName()); I get both for the first and for the second feature geometry: org.locationtech.jts.geom.Polygon org.locationtech.jts.geom.Polygon Commented Mar 20, 2019 at 16:54

2 Answers 2

3

Currently, the gt-geojson module is unsupported and doesn't handle features when the first feature is not representative of the whole collection. Therefore your point is interpreted as a polygon because that is what the first feature has.

There is currently a volunteer effort to remove and replace this module with a smarter gt-geojsondatastore but it is currently stalled with a Windows file handling issue.

To quote the unsupported modules page:

If you really need one of these modules to work?

Several of the unsupported modules (for example process) are really popular. That does not however mean that there is anyone in the GeoTools community taking care, fixing bugs and providing documentation. In some cases these modules are the work of students, in others a commercial company that donated the code (but in both cases more resources are needed to make the module usable and trustworthy).

In the past we threw out such work - we are trying a change of policy here by letting you see what can be done (and has been done).

If you really do need one of these modules cleaned up, perhaps for a deadline, please check out our support page - a range of commercial support options are available. Any one of these organisations can be hired to bring these modules up to speed.

9
  • That makes sense, in fact the first seen determine the geometry implementation. This is quite sad as now I will have to look for a diffrent library and write integration code from whatever DTO into a Geo Tools API domain object. Commented Mar 20, 2019 at 17:07
  • 1
    or you could help upgrade the GeoTools code
    – Ian Turton
    Commented Mar 20, 2019 at 17:19
  • I' m not sure I'm good enough but if my stakeholders give me time... yez ! Commented Mar 20, 2019 at 17:27
  • Hi, is this bug this one: osgeo-org.atlassian.net/browse/GEOT-6243 ? Commented Mar 21, 2019 at 10:43
  • 1
    I think that is a different but related one.
    – Ian Turton
    Commented Mar 21, 2019 at 10:56
1

I have tried a fix (by now on the old unsupported module gt-geojson), by slightly changing FeatureHandler class from 20.x git branch. Still to do all the tests but in case someone may find it useful this seems to fix the second geometry issue to me. I'm posting the changed code here, as I cannot attach files.

Thanks :) Stefano.

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2002-2010, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.geojson.feature;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.DelegatingHandler;
import org.geotools.geojson.IContentHandler;
import org.geotools.geojson.geom.GeometryCollectionHandler;
import org.geotools.geojson.geom.GeometryHandler;
import org.json.simple.parser.ParseException;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/** @source $URL$ */
public class FeatureHandler extends DelegatingHandler<SimpleFeature> {

    private int fid = 0;

    private String separator = "-";

    String id;

    Geometry geometry;

    List<Object> values;

    List<String> properties;

    CoordinateReferenceSystem crs;

    SimpleFeatureBuilder builder;

    AttributeIO attio;

    SimpleFeature feature;

    private String baseId = "feature";

    /** should we attempt to automatically build fids */
    private boolean autoFID = false;

    public FeatureHandler() {
        this(null, new DefaultAttributeIO());
    }

    public FeatureHandler(SimpleFeatureBuilder builder, AttributeIO attio) {
        this.builder = builder;
        this.attio = attio;
    }

    @Override
    public boolean startObject() throws ParseException, IOException {
        if (properties == NULL_LIST) {
            properties = new ArrayList();
        } else if (properties != null) {
            // start of a new object in properties means a geometry
            delegate = new GeometryHandler(new GeometryFactory());
        }

        return super.startObject();
    }

    public boolean startObjectEntry(String key) throws ParseException, IOException {
        if ("id".equals(key) && properties == null) {
            id = "";
            return true;
        } else if ("crs".equals(key) && properties == null /* it's top level, not a property */) {
            delegate = new CRSHandler();
            return true;
        } else if ("geometry".equals(key)
                && properties == null /* it's top level, not a property */) {
            delegate = new GeometryHandler(new GeometryFactory());
            return true;
        } else if ("properties".equals(key) && delegate == NULL) {
            properties = NULL_LIST;
            values = new ArrayList();
        } else if (properties != null && delegate == NULL) {
            properties.add(key);
            return true;
        }

        return super.startObjectEntry(key);
    }

    @Override
    public boolean startArray() throws ParseException, IOException {
        if (properties != null && delegate == NULL) {
            // array inside of properties
            delegate = new ArrayHandler();
        }

        return super.startArray();
    }

    @Override
    public boolean endArray() throws ParseException, IOException {
        if (delegate instanceof ArrayHandler) {
            super.endArray();
            values.add(((ArrayHandler) delegate).getValue());
            delegate = NULL;
        }
        return super.endArray();
    }

    @Override
    public boolean endObject() throws ParseException, IOException {
        if (delegate instanceof IContentHandler) {
            ((IContentHandler) delegate).endObject();

            if (delegate instanceof GeometryHandler) {
                Geometry g = ((IContentHandler<Geometry>) delegate).getValue();
                if (g == null
                        && ((GeometryHandler) delegate).getDelegate()
                                instanceof GeometryCollectionHandler) {
                    // this means that the collecetion handler is still parsing objects, continue
                    // to delegate to it
                } else {
                    if (properties != null) {
                        // this is a regular property
                        values.add(g);
                    } else {
                        // its the default geometry
                        geometry = g;
                    }
                    delegate = NULL;
                }
            } else if (delegate instanceof CRSHandler) {
                crs = ((CRSHandler) delegate).getValue();
                delegate = UNINITIALIZED;
            }

            return true;
        } else if (delegate == UNINITIALIZED) {
            delegate = NULL;
            return true;
        } else if (properties != null) {
            if (builder == null) {
                // no builder specified, build on the fly
                builder = createBuilder(geometry);
            }
            for (int i = 0; i < properties.size(); i++) {
                String att = properties.get(i);
                Object val = values.get(i);

                if (val instanceof String) {
                    val = attio.parse(att, (String) val);
                }

                builder.set(att, val);
            }

            properties = null;
            values = null;
            return true;
        } else {
            feature = buildFeature();
            id = null;
            geometry = null;
            properties = null;
            values = null;

            return true;
        }
    }

    @Override
    public boolean primitive(Object value) throws ParseException, IOException {
        if (delegate instanceof GeometryHandler && value == null) {
            delegate = NULL;
            return true;
        } else if ("".equals(id)) {
            id = value.toString();
            setFID(id);
            return true;
        } else if (values != null && delegate == NULL) {
            // use the attribute parser
            values.add(value);
            return true;
        }

        return super.primitive(value);
    }

    @Override
    public SimpleFeature getValue() {
        return feature;
    }

    public CoordinateReferenceSystem getCRS() {
        return crs;
    }

    public void setCRS(CoordinateReferenceSystem crs) {
        this.crs = crs;
    }

    public void init() {
        feature = null;
    }

    SimpleFeatureBuilder createBuilder(Geometry geometry) {
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("feature");
        typeBuilder.setNamespaceURI("http://geotools.org");
        typeBuilder.setCRS(crs);

        if (properties != null) {
            for (int i = 0; i < properties.size(); i++) {
                String prop = properties.get(i);
                Object valu = values.get(i);
                typeBuilder.add(prop, valu != null ? valu.getClass() : Object.class);
            }
        }

        addGeometryType(typeBuilder, geometry);

        return new SimpleFeatureBuilder(typeBuilder.buildFeatureType());
    }

    void addGeometryType(SimpleFeatureTypeBuilder typeBuilder, Geometry geometry) {
        typeBuilder.add("geometry", geometry != null ? geometry.getClass() : Geometry.class);
        typeBuilder.setDefaultGeometry("geometry");
    }

    SimpleFeature buildFeature() {

        SimpleFeatureBuilder builder =
                this.builder != null ? this.builder : createBuilder(geometry);
        SimpleFeatureType featureType = builder.getFeatureType();
        SimpleFeature f = builder.buildFeature(getFID());
        if (geometry != null) {
            if (featureType.getGeometryDescriptor() == null) {
                // GEOT-4293, case of geometry coming after properties, we have to retype
                // the builder
                // JD: this is ugly, we should really come up with a better way to store internal
                // state of properties, and avoid creating the builder after the properties object
                // is completed
                SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
                typeBuilder.init(featureType);
                addGeometryType(typeBuilder, geometry);

                featureType = typeBuilder.buildFeatureType();
                SimpleFeatureBuilder newBuilder = new SimpleFeatureBuilder(featureType);
                newBuilder.init(f);
                f = newBuilder.buildFeature(getFID());
            }
            f.setAttribute(featureType.getGeometryDescriptor().getLocalName(), geometry);
        }
        this.builder = null;
        incrementFID();
        return f;
    }
    // "{" +
    // " 'type': 'Feature'," +
    // " 'geometry': {" +
    // " 'type': 'Point'," +
    // " 'coordinates': [" + val + "," + val + "]" +
    // " }, " +
    // "' properties': {" +
    // " 'int': 1," +
    // " 'double': " + (double)val + "," +
    // " 'string': '" + toString(val) + "'" +
    // " }," +
    // " 'id':'widgets." + val + "'" +
    // "}";

    /** set the ID to 0 */
    private void resetFID() {
        fid = 0;
    }

    /** Add one to the current ID */
    private void incrementFID() {
        fid = fid + 1;
    }

    private void setFID(String f) {
        int index = f.lastIndexOf('.');
        if (index < 0) {
            index = f.indexOf('-');
            if (index >= 0) {
                separator = "-";
            } else {
                autoFID = false;
                id = f;
                return;
            }
        } else {
            separator = ".";
        }
        baseId = f.substring(0, index);
        try {
            fid = Integer.parseInt(f.substring(index + 1));
        } catch (NumberFormatException e) {
            autoFID = false;
            id = f;
        }
    }

    private String getFID() {
        if (id == null || autoFID) {
            return baseId + separator + fid;
        } else {
            return id;
        }
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.