Revision: 4943 http://sourceforge.net/p/jump-pilot/code/4943 Author: edso Date: 2016-07-01 15:34:36 +0000 (Fri, 01 Jul 2016) Log Message: ----------- add GeoJSON read support
Modified Paths: -------------- core/trunk/etc/readme.txt core/trunk/src/com/vividsolutions/jump/io/datasource/StandardReaderWriterFileDataSource.java core/trunk/src/com/vividsolutions/jump/workbench/datasource/InstallStandardDataSourceQueryChoosersPlugIn.java Added Paths: ----------- core/trunk/lib/json-simple-1.1.1.jar core/trunk/lib/jts-io-1.14.0.jar core/trunk/src/com/vividsolutions/jump/io/GeoJSONConstants.java core/trunk/src/com/vividsolutions/jump/io/GeoJSONReader.java core/trunk/src/com/vividsolutions/jump/io/GeoJSONWriter.java Modified: core/trunk/etc/readme.txt =================================================================== --- core/trunk/etc/readme.txt 2016-07-01 15:28:42 UTC (rev 4942) +++ core/trunk/etc/readme.txt 2016-07-01 15:34:36 UTC (rev 4943) @@ -52,6 +52,7 @@ JMath and its successor projects JMathTools (IO,Plot,Array) - BSD license - jmath_license.txt Javascript library RHINO - GPL2 - gpl-2.txt + JSON-simple - Apache License Version 2.0 - apache_license-2.0.txt JTS Topology Suite - LGPL2 - lgpl-2.1.txt JUnit - Eclipse Public License v1.0 - epl-v10.txt Jython - Jython license - jython_license.txt Added: core/trunk/lib/json-simple-1.1.1.jar =================================================================== (Binary files differ) Index: core/trunk/lib/json-simple-1.1.1.jar =================================================================== --- core/trunk/lib/json-simple-1.1.1.jar 2016-07-01 15:28:42 UTC (rev 4942) +++ core/trunk/lib/json-simple-1.1.1.jar 2016-07-01 15:34:36 UTC (rev 4943) Property changes on: core/trunk/lib/json-simple-1.1.1.jar ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Added: core/trunk/lib/jts-io-1.14.0.jar =================================================================== (Binary files differ) Index: core/trunk/lib/jts-io-1.14.0.jar =================================================================== --- core/trunk/lib/jts-io-1.14.0.jar 2016-07-01 15:28:42 UTC (rev 4942) +++ core/trunk/lib/jts-io-1.14.0.jar 2016-07-01 15:34:36 UTC (rev 4943) Property changes on: core/trunk/lib/jts-io-1.14.0.jar ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Added: core/trunk/src/com/vividsolutions/jump/io/GeoJSONConstants.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/io/GeoJSONConstants.java (rev 0) +++ core/trunk/src/com/vividsolutions/jump/io/GeoJSONConstants.java 2016-07-01 15:34:36 UTC (rev 4943) @@ -0,0 +1,7 @@ +package com.vividsolutions.jump.io; + +public class GeoJSONConstants { + public static final String PROPERTIES = "properties"; + public static final String GEOMETRY = "geometry"; + public static final Object FEATURES = "features"; +} Property changes on: core/trunk/src/com/vividsolutions/jump/io/GeoJSONConstants.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: core/trunk/src/com/vividsolutions/jump/io/GeoJSONReader.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/io/GeoJSONReader.java (rev 0) +++ core/trunk/src/com/vividsolutions/jump/io/GeoJSONReader.java 2016-07-01 15:34:36 UTC (rev 4943) @@ -0,0 +1,331 @@ +package com.vividsolutions.jump.io; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.Vector; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.ContainerFactory; +import org.json.simple.parser.ContentHandler; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.io.geojson.GeoJsonConstants; +import com.vividsolutions.jts.io.geojson.GeoJsonReader; +import com.vividsolutions.jump.feature.AttributeType; +import com.vividsolutions.jump.feature.BasicFeature; +import com.vividsolutions.jump.feature.Feature; +import com.vividsolutions.jump.feature.FeatureCollection; +import com.vividsolutions.jump.feature.FeatureDataset; +import com.vividsolutions.jump.feature.FeatureSchema; +import com.vividsolutions.jump.util.FileUtil; + +public class GeoJSONReader extends AbstractJUMPReader { + + @Override + public FeatureCollection read(DriverProperties dp) throws Exception { + + String inputUri = dp.getProperty("Uri"); + if (inputUri == null) { + throw new IllegalParametersException( + "call to GeoJSONReader.read() has DataProperties w/o an InputUri specified"); + } + + InputStream in = null; + InputStreamReader inReader = null; + Reader bufReader = null; + GeoJSONFeatureCollectionWrapper fcwrap = new GeoJSONFeatureCollectionWrapper(); + try { + in = CompressedFile.openFile(new URI(inputUri)); + inReader = new InputStreamReader(in, "UTF-8"); + bufReader = new BufferedReader(inReader); + + // parse and create features via content handler + Transformer t = new Transformer(fcwrap); + new JSONParser().parse(bufReader, t); + + } finally { + FileUtil.close(bufReader); + FileUtil.close(inReader); + FileUtil.close(in); + } + + return fcwrap.getFeatureCollection(); + } +} + +/** + * utility class to allow creating geometries directly from simple-json maps + */ +class MapGeoJsonGeometryReader extends GeoJsonReader { + GeometryFactory gf = null; + Method m, m2; + + /** + * this is a hack and should be communicated to JTS. we need an exposed method + * to feed the simple-json map to + * + * @throws NoSuchMethodException + * @throws SecurityException + */ + public MapGeoJsonGeometryReader() throws NoSuchMethodException, + SecurityException { + m = GeoJsonReader.class.getDeclaredMethod("create", Map.class, + GeometryFactory.class); + m.setAccessible(true); + m2 = GeoJsonReader.class.getDeclaredMethod("getGeometryFactory", Map.class); + m2.setAccessible(true); + } + + /** + * create a JTS geometry from a simple-json map + * + * @param geometryMap + * @return + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + */ + public Geometry read(Map geometryMap) throws IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + GeometryFactory geometryFactory = null; + if (this.gf == null) { + geometryFactory = (GeometryFactory) m2.invoke(this, geometryMap); + } else { + geometryFactory = this.gf; + } + + return (Geometry) m.invoke(this, geometryMap, geometryFactory); + } +} + +/** + * a wrapper for a feature collection to do funky geojson stuff to/with + * + */ +class GeoJSONFeatureCollectionWrapper { + MapGeoJsonGeometryReader geomReader = null; + FeatureSchema featureSchema = null; + List<Feature> featureList = new LinkedList<Feature>(); + FeatureCollection featureCollection = null; + + public GeoJSONFeatureCollectionWrapper() { + featureSchema = new FeatureSchema(); + } + + public GeoJSONFeatureCollectionWrapper(FeatureCollection fc) { + this.featureSchema = fc.getFeatureSchema(); + this.featureCollection = fc; + } + + public void add(Map featureMap) throws Exception { + + // parse geometry + Geometry geom = null; + if (featureMap.containsKey(GeoJSONConstants.GEOMETRY) + && featureMap.get(GeoJSONConstants.GEOMETRY) instanceof Map) { + Map geometryMap = (Map) featureMap.get(GeoJSONConstants.GEOMETRY); + + if (geomReader == null) + geomReader = new MapGeoJsonGeometryReader(); + + if (featureSchema.getGeometryIndex() < 0) { + featureSchema.addAttribute("Geometry", AttributeType.GEOMETRY); + } + + geom = geomReader.read(geometryMap); + } + + // parse attributes + Map attribsMap = null; + if (featureMap.containsKey(GeoJSONConstants.PROPERTIES) + && featureMap.get(GeoJSONConstants.PROPERTIES) instanceof Map) { + attribsMap = (Map) featureMap.get(GeoJSONConstants.PROPERTIES); + for (Object key : attribsMap.keySet()) { + String attribName = key.toString(); + // extend schema if attrib is unknown + if (!featureSchema.hasAttribute(attribName)) { + featureSchema.addAttribute(attribName, AttributeType.STRING); + } + } + } + + // this type of feature "autoextends" by returning null for unknown attribs + Feature feature = new BasicFeature(featureSchema) { + public Object getAttribute(int i) { + Object[] attributes = getAttributes(); + if (i >= attributes.length) + return null; + + return attributes[i]; + } + }; + + // set geo, if any + if (geom != null) + feature.setGeometry(geom); + + // set attribs, if any + if (attribsMap != null) { + for (Object key : attribsMap.keySet()) { + String attribName = key.toString(); + feature.setAttribute(attribName, attribsMap.get(attribName)); + } + } + + featureList.add(feature); + } + + public FeatureCollection getFeatureCollection() { + return new FeatureDataset(featureList, featureSchema); + } +} + +class Transformer implements ContentHandler { + private Stack valueStack; + private Object featsId = null; + private GeoJSONFeatureCollectionWrapper fcwrap = null; + + public Transformer(GeoJSONFeatureCollectionWrapper fcwrap) { + super(); + this.fcwrap = fcwrap; + } + + public Stack getResult() { + if (valueStack == null || valueStack.size() == 0) + return null; + return valueStack; + } + + private void addFeatures(List<Map> featureMapList) throws IOException { + for (Map featureMap : featureMapList) { + try { + fcwrap.add(featureMap); + } catch (Exception e) { + throw new IOException(e); + } + } + } + + private void trackBack() { + if (valueStack.size() > 1) { + Object value = valueStack.pop(); + Object prev = valueStack.peek(); + if (prev instanceof String) { + valueStack.push(value); + } + } + } + + private void consumeValue(Object value) { + if (valueStack.size() == 0) + valueStack.push(value); + else { + Object prev = valueStack.peek(); + if (prev instanceof List) { + List array = (List) prev; + array.add(value); + } else { + valueStack.push(value); + } + } + } + + public boolean primitive(Object value) throws ParseException, IOException { + consumeValue(value); + return true; + } + + public boolean startArray() throws ParseException, IOException { + List array = new LinkedList(); + consumeValue(array); + valueStack.push(array); + return true; + } + + public boolean endArray() throws ParseException, IOException { + trackBack(); + return true; + } + + public void startJSON() throws ParseException, IOException { + valueStack = new Stack(); + } + + public void endJSON() throws ParseException, IOException { + // add rest of features + for (Object object : valueStack) { + List<Map> featureList = (List) ((Map) object).get("features"); + addFeatures(featureList); + featureList.clear(); + } + + } + + public boolean startObject() throws ParseException, IOException { + // memorize feature object for processing in endObject + if (featsId == null + && valueStack.size() > 2 + && valueStack.elementAt(valueStack.size() - 3).equals( + GeoJSONConstants.FEATURES)) { + featsId = valueStack.elementAt(valueStack.size() - 3); + } + + Map object = new LinkedHashMap(); + consumeValue(object); + valueStack.push(object); + + return true; + } + + public boolean endObject() throws ParseException, IOException { + trackBack(); + + // at this point we just finished parsing a whole feature object + if (featsId != null && valueStack.size() > 2 + && valueStack.elementAt(valueStack.size() - 3) == featsId) { + List<Map> featureList = (List) valueStack + .elementAt(valueStack.size() - 2); + + // process by batches of ten and the rest in endJSON() + if (featureList.size() >= 10) { + addFeatures(featureList); + featureList.clear(); + } + } + return true; + } + + public boolean startObjectEntry(String key) throws ParseException, + IOException { + valueStack.push(key); + return true; + } + + public boolean endObjectEntry() throws ParseException, IOException { + Object value = valueStack.pop(); + Object key = valueStack.pop(); + Map parent = (Map) valueStack.peek(); + parent.put(key, value); + return true; + } + +} Property changes on: core/trunk/src/com/vividsolutions/jump/io/GeoJSONReader.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: core/trunk/src/com/vividsolutions/jump/io/GeoJSONWriter.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/io/GeoJSONWriter.java (rev 0) +++ core/trunk/src/com/vividsolutions/jump/io/GeoJSONWriter.java 2016-07-01 15:34:36 UTC (rev 4943) @@ -0,0 +1,14 @@ +package com.vividsolutions.jump.io; + +import com.vividsolutions.jump.feature.FeatureCollection; + +public class GeoJSONWriter implements JUMPWriter { + + @Override + public void write(FeatureCollection featureCollection, DriverProperties dp) + throws Exception { + // TODO Auto-generated method stub + + } + +} Property changes on: core/trunk/src/com/vividsolutions/jump/io/GeoJSONWriter.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Modified: core/trunk/src/com/vividsolutions/jump/io/datasource/StandardReaderWriterFileDataSource.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/io/datasource/StandardReaderWriterFileDataSource.java 2016-07-01 15:28:42 UTC (rev 4942) +++ core/trunk/src/com/vividsolutions/jump/io/datasource/StandardReaderWriterFileDataSource.java 2016-07-01 15:34:36 UTC (rev 4943) @@ -132,6 +132,12 @@ } } + public static class GeoJSON extends ClassicReaderWriterFileDataSource { + public GeoJSON() { + super(new GeoJSONReader(), /*new GeoJSONWriter()*/ null, new String[] { "json" }); + } + } + public static class FMEGML extends ClassicReaderWriterFileDataSource { public FMEGML() { super(new FMEGMLReader(), new FMEGMLWriter(), new String[] { "gml", "xml", "fme" }); Modified: core/trunk/src/com/vividsolutions/jump/workbench/datasource/InstallStandardDataSourceQueryChoosersPlugIn.java =================================================================== --- core/trunk/src/com/vividsolutions/jump/workbench/datasource/InstallStandardDataSourceQueryChoosersPlugIn.java 2016-07-01 15:28:42 UTC (rev 4942) +++ core/trunk/src/com/vividsolutions/jump/workbench/datasource/InstallStandardDataSourceQueryChoosersPlugIn.java 2016-07-01 15:34:36 UTC (rev 4943) @@ -169,6 +169,10 @@ addFileDataSourceQueryChoosers(new ShapefileReader(), new ShapefileWriter(), "ESRI Shapefile", context.getWorkbenchContext(), StandardReaderWriterFileDataSource.Shapefile.class); + + addFileDataSourceQueryChoosers(new GeoJSONReader(), + /*new GeoJSONWriter()*/ null, "GeoJSON", context.getWorkbenchContext(), + StandardReaderWriterFileDataSource.GeoJSON.class); } public static void addCompressedFileFilter(final String description, ------------------------------------------------------------------------------ Attend Shape: An AT&T Tech Expo July 15-16. Meet us at AT&T Park in San Francisco, CA to explore cutting-edge tech and listen to tech luminaries present their vision of the future. This family event has something for everyone, including kids. Get more information and register today. http://sdm.link/attshape _______________________________________________ Jump-pilot-devel mailing list Jump-pilot-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel