Java tutorial
/* * polymap.org * Copyright (C) 2011-2014, Falko Brutigam. All rights reserved. * * This 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; either version 2.1 of * the License, or (at your option) any later version. * * This software 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.polymap.rhei.fulltext.servlet; import java.util.LinkedHashMap; import java.util.Set; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectOutput; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geojson.GeoJSONUtil; import org.geotools.geojson.feature.FeatureJSON; import org.geotools.referencing.NamedIdentifier; import org.json.JSONObject; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.FactoryException; import org.opengis.referencing.ReferenceIdentifier; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.apache.commons.io.output.CountingOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.document.Document; import com.vividsolutions.jts.geom.Geometry; import org.polymap.core.data.pipeline.PipelineProcessor; import org.polymap.core.data.util.Geometries; import static org.polymap.rhei.fulltext.FulltextIndex.*; /** * This stream encodes {@link SimpleFeature} and {@link Document} objects into * GeoJSON stream. Later this may also extend to be a {@link PipelineProcessor} * * @author <a href="http://www.polymap.de">Falko Brutigam</a> */ public class GeoJsonEncoder extends DataOutputStream implements ObjectOutput { private static final Log log = LogFactory.getLog(GeoJsonEncoder.class); private String typeName = "Orte"; private boolean streamStarted; private CoordinateReferenceSystem worldCRS; /** The accumulator for the encoded features until they are flushed. */ private DefaultFeatureCollection features; public GeoJsonEncoder(OutputStream out, CoordinateReferenceSystem worldCRS) throws Exception { super(out); this.worldCRS = worldCRS; } public void writeObject(Object obj) throws IOException { if (!streamStarted) { startStream(); } try { if (obj instanceof SimpleFeature) { encodeFeature((SimpleFeature) obj); } else if (obj instanceof JSONObject) { encodeFeature((JSONObject) obj); } else { throw new RuntimeException("Unsupported object type: " + obj); } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new IOException(e.getMessage(), e); } } protected void startStream() { assert !streamStarted; streamStarted = true; this.features = new DefaultFeatureCollection(null, null); } private boolean aboutToFlush = false; public synchronized void flush() throws IOException { if (aboutToFlush) { return; } aboutToFlush = true; try { FeatureJSON fjson = new FeatureJSON(); fjson.setEncodeFeatureBounds(false); fjson.setEncodeFeatureCRS(false); LinkedHashMap obj = new LinkedHashMap(); obj.put("type", "FeatureCollection"); obj.put("features", new CollectionEncoder(features, fjson, null)); obj.put("crs", new CRSEncoder(fjson, worldCRS)); GeoJSONUtil.encode(obj, new OutputStreamWriter(this, "UTF-8")); log.info(" encoded: " + size() + " bytes"); if (features != null) { features.clear(); } } finally { aboutToFlush = false; super.flush(); } } protected void encodeFeature(SimpleFeature feature) throws FactoryException { features.add(feature); log.debug("Feature added: " + feature); } protected void encodeFeature(JSONObject obj) throws Exception { // transform geometry String title = obj.getString(FIELD_TITLE); Geometry geom = (Geometry) obj.get(FIELD_GEOM); String srs = obj.getString(FIELD_SRS); geom = Geometries.transform(geom, Geometries.crs(srs), worldCRS); // type SimpleFeatureTypeBuilder ftb = new SimpleFeatureTypeBuilder(); ftb.setName(typeName); ftb.add("geometry", geom.getClass(), worldCRS); ftb.add("title", String.class); // // address // if (obj.getAddress() != null) { // ftb.add( "address", String.class ); // } // fields for (String field : (Set<String>) obj.keySet()) { String value = obj.getString(field); if (value != null && value.length() > 0) { ftb.add(field, String.class); } } SimpleFeatureType featuresType = ftb.buildFeatureType(); // feature SimpleFeatureBuilder fb = new SimpleFeatureBuilder(featuresType); fb.set("title", /*JSONObject.escape(*/ title); // // address // if (obj.getAddress() != null) { // //fb.set( "address", JSONObject.escape( obj.getAddress().toJSON() ) ); // fb.set( "address", obj.getAddress().toJSON() ); // } // fields for (String field : (Set<String>) obj.keySet()) { String value = obj.getString(field); if (value != null && value.length() > 0) { // umlauts //value = StringEscapeUtils.escapeHtml( value ); // does not seem to be done by gt-geojson //value = JSONObject.escape( value ); fb.set(field, value); } } encodeFeature(fb.buildFeature(null)); } /** * */ class CRSEncoder /*implements JSONStreamAware*/ { private FeatureJSON fjson; private CoordinateReferenceSystem crs; public CRSEncoder(FeatureJSON fjson, CoordinateReferenceSystem crs) { super(); this.fjson = fjson; this.crs = crs; } public void writeJSONString(@SuppressWarnings("hiding") Writer out) throws IOException { // this is code is from the 'old' JSONServer Set<ReferenceIdentifier> ids = worldCRS.getIdentifiers(); // WKT defined crs might not have identifiers at all if (ids != null && ids.size() > 0) { NamedIdentifier namedIdent = (NamedIdentifier) ids.iterator().next(); String csStr = namedIdent.getCodeSpace().toUpperCase(); if (csStr.equals("EPSG")) { JSONObject obj = new JSONObject(); obj.put("type", csStr); JSONObject props = new JSONObject(); obj.put("properties", props); props.put("code", namedIdent.getCode()); obj.write(out); } else { log.warn("Non-EPSG code not supported: " + csStr); } } else { log.warn("No CRS identifier for CRS: " + worldCRS); } // this is the original code which throws exception for lookupIdentifier() // JSONObject obj = new JSONObject(); // obj.put("type", "name"); // // Map<String,Object> props = new LinkedHashMap<String, Object>(); // try { // props.put("name", CRS.lookupIdentifier(crs, true)); // } // catch (FactoryException e) { // throw (IOException) new IOException("Error looking up crs identifier").initCause(e); // } // // obj.put("properties", props); // return obj; } } /** * */ static class CollectionEncoder /*implements JSONStreamAware*/ { private FeatureCollection features; private FeatureJSON fjson; private MathTransform transform; private SimpleFeatureType transformedSchema; private CountingOutputStream byteCounter; public CollectionEncoder(FeatureCollection features, FeatureJSON fjson, CountingOutputStream byteCounter) { this.features = features; this.fjson = fjson; this.byteCounter = byteCounter; } public void writeJSONString(Writer out) throws IOException { if (features == null) { out.write("[]"); return; } out.write("["); try (FeatureIterator<SimpleFeature> it = features.features();) { if (it.hasNext()) { fjson.writeFeature(it.next(), out); } while (it.hasNext()) { // // check byte limit // if (byteCounter.getCount() > maxBytes) { // log.warn( "Byte limit reached. Features encoded: " + featureCount ); // display.asyncExec( new Runnable() { // public void run() { // MessageDialog.openInformation( // PolymapWorkbench.getShellToParentOn(), // "Information", // "Es knnen nicht alle Objekte angezeigt werden.\nWenn mglich, dann schrnken Sie die Auswahl weiter ein." ); // } // } ); // break; // } // encode feature out.write(","); fjson.writeFeature(it.next(), out); } } catch (IOException e) { throw e; } catch (Exception e) { throw new IOException(e); } out.write("]"); } // /** // * Transform the given feature: reproject CRS; (XXX strip properties?) // * // * @throws FactoryException // * @throws TransformException // * @throws MismatchedDimensionException // */ // private SimpleFeature transform( SimpleFeature feature ) // throws FactoryException, MismatchedDimensionException, TransformException { // SimpleFeatureType schema = feature.getFeatureType(); // CoordinateReferenceSystem featureCRS = schema.getGeometryDescriptor().getCoordinateReferenceSystem(); // // if (transform == null && // featureCRS != null && worldCRS != null && // !worldCRS.equals( featureCRS )) { // // transform = CRS.findMathTransform( featureCRS, worldCRS, true ); // } // //// if (transformedSchema == null) { //// SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder(); //// typeBuilder.setCRS( mapCRS ); //// //// for (AttributeDescriptor ad : schema.getAttributeDescriptors()) { //// typeBuilder. //// } //// } //// SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(); // // Object[] attributes = feature.getAttributes().toArray(); // for (int i=0; i < attributes.length; i++) { // if (attributes[i] instanceof Geometry) { // attributes[i] = transform != null // ? JTS.transform( (Geometry) attributes[i], transform ) // : attributes[i]; // } // } // // return SimpleFeatureBuilder.build( schema, attributes, feature.getID() ); // } } }