org.geoserver.wms.topojson.TopologyBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.wms.topojson.TopologyBuilder.java

Source

/* (c) 2015 Open Source Geospatial Foundation - all rights reserved
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.wms.topojson;

import static org.geoserver.wms.topojson.TopoJSONBuilderFactory.MIME_TYPE;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

import org.apache.commons.io.output.DeferredFileOutputStream;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.map.RawMap;
import org.geoserver.wms.topojson.TopoGeom.GeometryColleciton;
import org.geoserver.wms.vector.DeferredFileOutputStreamWebMap;
import org.geoserver.wms.vector.VectorTileBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.renderer.lite.RendererUtilities;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.operation.TransformException;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;

public class TopologyBuilder implements VectorTileBuilder {

    private AffineTransform worldToScreen;

    private AffineTransform screenToWorld;

    private List<LineString> arcs = new ArrayList<>();

    private Multimap<String, TopoGeom> layers = ArrayListMultimap.create();

    private GeometryFactory fixedGeometryFactory;

    public TopologyBuilder(Rectangle mapSize, ReferencedEnvelope mapArea) {
        this.worldToScreen = RendererUtilities.worldToScreenTransform(mapArea, mapSize);
        this.screenToWorld = new AffineTransform(this.worldToScreen);
        try {
            this.screenToWorld.invert();
        } catch (NoninvertibleTransformException e) {
            throw Throwables.propagate(e);
        }

        PrecisionModel precisionModel = new PrecisionModel(10.0);
        fixedGeometryFactory = new GeometryFactory(precisionModel);
    }

    @Override
    public void addFeature(String layerName, String featureId, String geometryName, Geometry geometry,
            Map<String, Object> properties) {
        TopoGeom topoObj;
        try {
            topoObj = createObject(featureId, geometry, properties);
        } catch (MismatchedDimensionException | TransformException e) {
            e.printStackTrace();
            throw Throwables.propagate(e);
        }

        if (topoObj != null) {
            layers.put(layerName, topoObj);
        }
    }

    @Override
    public RawMap build(WMSMapContent mapContent) throws IOException {

        Map<String, TopoGeom.GeometryColleciton> layers = new HashMap<>();
        for (String layer : this.layers.keySet()) {
            Collection<TopoGeom> collection = this.layers.get(layer);
            GeometryColleciton layerCollection = new TopoGeom.GeometryColleciton(collection);
            layers.put(layer, layerCollection);
        }

        List<LineString> arcs = this.arcs;
        this.arcs = null;
        this.layers = null;
        Topology topology = new Topology(screenToWorld, arcs, layers);

        final int threshold = 8096;
        DeferredFileOutputStream out = new DeferredFileOutputStream(threshold, "topology", ".topojson", null);
        TopoJSONEncoder encoder = new TopoJSONEncoder();

        Writer writer = new OutputStreamWriter(out, Charsets.UTF_8);
        encoder.encode(topology, writer);
        writer.flush();
        writer.close();
        out.close();

        long length;
        RawMap map;
        if (out.isInMemory()) {
            byte[] data = out.getData();
            length = data.length;
            map = new RawMap(mapContent, data, MIME_TYPE);
        } else {
            File f = out.getFile();
            length = f.length();
            map = new DeferredFileOutputStreamWebMap(mapContent, out, MIME_TYPE);
        }
        map.setResponseHeader("Content-Length", String.valueOf(length));

        return map;
    }

    @Nullable
    private TopoGeom createObject(String featureId, Geometry geom, Map<String, Object> properties)
            throws MismatchedDimensionException, TransformException {

        // // snap to pixel
        geom = fixedGeometryFactory.createGeometry(geom);

        if (geom.isEmpty()) {
            return null;
        }

        if (geom instanceof GeometryCollection && geom.getNumGeometries() == 1) {
            geom = geom.getGeometryN(0);
        }

        TopoGeom geometry = createGeometry(geom);

        geometry.setProperties(properties);

        geometry.setId(featureId);
        return geometry;
    }

    private TopoGeom createGeometry(Geometry geom) {
        Preconditions.checkNotNull(geom);

        TopoGeom topoGeom;

        if (geom instanceof Point) {
            topoGeom = createPoint((Point) geom);
        } else if (geom instanceof MultiPoint) {
            topoGeom = createMultiPoint((MultiPoint) geom);
        } else if (geom instanceof LineString) {
            topoGeom = createLineString((LineString) geom);
        } else if (geom instanceof MultiLineString) {
            topoGeom = createMultiLineString((MultiLineString) geom);
        } else if (geom instanceof Polygon) {
            topoGeom = createPolygon((Polygon) geom);
        } else if (geom instanceof MultiPolygon) {
            topoGeom = createMultiPolygon((MultiPolygon) geom);
        } else if (geom instanceof GeometryCollection) {
            topoGeom = createGeometryCollection((GeometryCollection) geom);
        } else {
            throw new IllegalArgumentException("Unknown geometry type: " + geom.getGeometryType());
        }

        return topoGeom;
    }

    private TopoGeom.LineString createLineString(LineString geom) {
        int arcIndex = this.arcs.size();
        this.arcs.add(geom);
        return new TopoGeom.LineString(ImmutableList.of(Integer.valueOf(arcIndex)));
    }

    private TopoGeom.Polygon createPolygon(Polygon geom) {
        List<TopoGeom.LineString> arcs = new ArrayList<>(1 + geom.getNumInteriorRing());

        arcs.add(createLineString(geom.getExteriorRing()));

        for (int n = 0; n < geom.getNumInteriorRing(); n++) {
            arcs.add(createLineString(geom.getInteriorRingN(n)));
        }
        return new TopoGeom.Polygon(arcs);
    }

    private TopoGeom.GeometryColleciton createGeometryCollection(GeometryCollection geom) {
        Collection<TopoGeom> members = new ArrayList<>(geom.getNumGeometries());
        for (int n = 0; n < geom.getNumGeometries(); n++) {
            TopoGeom o = createGeometry(geom.getGeometryN(n));
            members.add(o);
        }
        TopoGeom.GeometryColleciton collection = new TopoGeom.GeometryColleciton(members);
        return collection;
    }

    private TopoGeom.MultiPolygon createMultiPolygon(MultiPolygon geom) {
        List<TopoGeom.Polygon> polygons = new ArrayList<>(geom.getNumGeometries());
        for (int n = 0; n < geom.getNumGeometries(); n++) {
            polygons.add(createPolygon((Polygon) geom.getGeometryN(n)));
        }
        return new TopoGeom.MultiPolygon(polygons);
    }

    private TopoGeom.MultiLineString createMultiLineString(MultiLineString geom) {

        List<TopoGeom.LineString> arcs = new ArrayList<>(geom.getNumGeometries());
        for (int n = 0; n < geom.getNumGeometries(); n++) {
            arcs.add(createLineString((LineString) geom.getGeometryN(n)));
        }
        return new TopoGeom.MultiLineString(arcs);
    }

    private TopoGeom.MultiPoint createMultiPoint(MultiPoint geom) {
        List<TopoGeom.Point> points = new ArrayList<>(geom.getNumGeometries());
        for (int n = 0; n < geom.getNumGeometries(); n++) {
            points.add(createPoint((Point) geom.getGeometryN(n)));
        }
        return new TopoGeom.MultiPoint(points);
    }

    private TopoGeom.Point createPoint(Point geom) {
        return new TopoGeom.Point(geom.getX(), geom.getY());
    }

}