All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.integratedmodelling.engine.geospace.utils.SpatialDisplay Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *  Copyright (C) 2007, 2015:
 *  
 *    - Ferdinando Villa 
 *    - integratedmodelling.org
 *    - any other authors listed in @author annotations
 *
 *    All rights reserved. This file is part of the k.LAB software suite,
 *    meant to enable modular, collaborative, integrated 
 *    development of interoperable data and model components. For
 *    details, see http://integratedmodelling.org.
 *    
 *    This program is free software; you can redistribute it and/or
 *    modify it under the terms of the Affero General Public License 
 *    Version 3 or any later version.
 *
 *    This program 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
 *    Affero General Public License for more details.
 *  
 *     You should have received a copy of the Affero General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *     The license is also available at: https://www.gnu.org/licenses/agpl.html
 *******************************************************************************/
package org.integratedmodelling.engine.geospace.utils;

import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JFrame;

import org.geotools.data.DataUtilities;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.MapViewport;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.Graphic;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.swing.JMapFrame;
import org.integratedmodelling.api.modelling.IState;
import org.integratedmodelling.api.space.IGrid.Cell;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.space.IGeometricShape;
import org.integratedmodelling.common.visualization.Viewport;
import org.integratedmodelling.engine.NodeEngine;
import org.integratedmodelling.engine.geospace.extents.Grid.CellImpl;
import org.integratedmodelling.engine.geospace.extents.SpaceExtent;
import org.integratedmodelling.engine.geospace.literals.ShapeValue;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory;

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

/**
 * Generic spatial visualizer/debugger that is initialized with a spatial extent and you can just
 * throw shapes and states into. 
 * 
 * Should become a complete no-op on public engines, so cause no trouble if inadvertently left in
 * a headless engine.
 * 
 * @author Ferd
 *
 */
public class SpatialDisplay {

    int                  SLID          = 0;
    static StyleFactory  styleFactory  = CommonFactoryFinder.getStyleFactory();
    static FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();

    class SLDesc {
        SimpleFeatureType    fType;
        List  features = new ArrayList<>();
        int                  nShapes  = 0;
        SimpleFeatureBuilder featureBuilder;
        String               name;
        Class             geometryClass;

        SLDesc(String name) {
            this.name = name;
        }

        void addFeature(IGeometricShape shape) {

            if (featureBuilder == null) {
                try {
                    geometryClass = shape.getGeometry().getClass();
                    fType = DataUtilities.createType("Location", "the_geom:"
                            + shape.getGeometry().getGeometryType() + ":srid=" + shape.getSRID()
                            + ",number:Integer");
                    featureBuilder = new SimpleFeatureBuilder(fType);
                } catch (SchemaException e) {
                    throw new KlabRuntimeException(e);
                }
            }

            featureBuilder.add(shape.getGeometry());
            featureBuilder.add(++nShapes);
            features.add(featureBuilder.buildFeature(null));
        }

        Layer getLayer() {

            if (geometryClass == null) {
                return null;
            }

            DefaultFeatureCollection featureCollection = new DefaultFeatureCollection("fc_" + name, fType);
            for (SimpleFeature f : features) {
                featureCollection.add(f);
            }
            return new FeatureLayer(featureCollection, createStyle(geometryClass));
        }
    }

    Map sLayers = new HashMap<>();
    SpaceExtent         space;

    public SpatialDisplay(SpaceExtent space) {
        this.space = space;
    }

    /**
     * Add a shape to the standard layer "shapes_xxx" where xxx is the geometry type of the shape.
     * CRS of shape isn't checked and must be same as passed scale.
     * 
     * @param shape
     */
    public void add(IGeometricShape shape) {

        if (KLAB.ENGINE instanceof NodeEngine) {
            return;
        }

        SLDesc slDesc = getSLDesc("shapes_" + shape.getGeometryType().name());
        slDesc.addFeature(shape);
    }

    /**
     * Add shape to named layer, creating if necessary. All must be projected like
     * the scale.
     * 
     * @param shape
     * @param layer
     */
    public void add(IGeometricShape shape, String layer) {

        if (KLAB.ENGINE instanceof NodeEngine) {
            return;
        }

        SLDesc slDesc = getSLDesc(layer);
        slDesc.addFeature(shape);
    }

    /**
     * Add a raster state, complaining loudly if the spatial extent does not fit the space 
     * passed in the constructor. That will soon mean that an adapter cannot be found, not 
     * that it's not identical.
     * 
     * @param state
     */
    public void add(IState state) {

        if (KLAB.ENGINE instanceof NodeEngine) {
            return;
        }

    }

    /**
     * Show the map using a JMapFrame.
     */
    public void show() {

        if (KLAB.ENGINE instanceof NodeEngine) {
            return;
        }

        MapContent content = new MapContent();
        content.setViewport(new MapViewport(space.getEnvelope()));

        for (SLDesc sld : sLayers.values()) {
            content.addLayer(sld.getLayer());
        }

        Viewport viewport = new Viewport(800, 800);
        int[] xy = viewport.getSizeFor(space.getMaxX() - space.getMinX(), space.getMaxY() - space.getMinY());
        content.setTitle("Spatial display");
        JMapFrame display = new JMapFrame(content);
        display.setMinimumSize(new Dimension(xy[0] + 300, xy[1]));
        display.enableLayerTable(true);
        display.enableToolBar(true);
        display.enableStatusBar(true);
        display.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        display.setVisible(true);
    }

    private SLDesc getSLDesc(String layer) {
        if (!sLayers.containsKey(layer)) {
            SLDesc ret = new SLDesc(layer);
            sLayers.put(layer, ret);
        }
        return sLayers.get(layer);
    }

    private Style createStyle(Class gclass) {

        if (Polygon.class.isAssignableFrom(gclass)
                || MultiPolygon.class.isAssignableFrom(gclass)) {
            return createPolygonStyle();

        } else if (LineString.class.isAssignableFrom(gclass)
                || MultiLineString.class.isAssignableFrom(gclass)) {
            return createLineStyle();

        } else {
            return createPointStyle();
        }
    }

    /**
     * Create a Style to draw polygon features with a thin blue outline and
     * a cyan fill
     */
    private Style createPolygonStyle() {

        // create a partially opaque outline stroke
        Stroke stroke = styleFactory.createStroke(filterFactory.literal(Color.GRAY), filterFactory
                .literal(1), filterFactory.literal(0.5));

        // create a partial opaque fill
        Fill fill = styleFactory
                .createFill(filterFactory.literal(Color.LIGHT_GRAY), filterFactory.literal(0.5));

        /*
         * Setting the geometryPropertyName arg to null signals that we want to
         * draw the default geomettry of features
         */
        PolygonSymbolizer sym = styleFactory.createPolygonSymbolizer(stroke, fill, null);

        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(sym);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
        Style style = styleFactory.createStyle();
        style.featureTypeStyles().add(fts);

        return style;
    }

    /**
     * Create a Style to draw line features as thin blue lines
     */
    private Style createLineStyle() {
        Stroke stroke = styleFactory
                .createStroke(filterFactory.literal(Color.GREEN), filterFactory.literal(1));

        /*
         * Setting the geometryPropertyName arg to null signals that we want to
         * draw the default geometry of features
         */
        LineSymbolizer sym = styleFactory.createLineSymbolizer(stroke, null);

        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(sym);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
        Style style = styleFactory.createStyle();
        style.featureTypeStyles().add(fts);

        return style;
    }

    /**
     * Create a Style to draw point features as circles with blue outlines
     * and cyan fill
     */
    private Style createPointStyle() {

        Graphic gr = styleFactory.createDefaultGraphic();

        Mark mark = styleFactory.getCircleMark();

        mark.setStroke(styleFactory
                .createStroke(filterFactory.literal(Color.BLUE), filterFactory.literal(1)));

        mark.setFill(styleFactory.createFill(filterFactory.literal(Color.RED)));

        gr.graphicalSymbols().clear();
        gr.graphicalSymbols().add(mark);
        gr.setSize(filterFactory.literal(5));

        /*
         * Setting the geometryPropertyName arg to null signals that we want to
         * draw the default geomettry of features
         */
        PointSymbolizer sym = styleFactory.createPointSymbolizer(gr, null);

        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(sym);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] { rule });
        Style style = styleFactory.createStyle();
        style.featureTypeStyles().add(fts);

        return style;
    }

    public void add(Cell cell, String layer) {
        add(new ShapeValue(((CellImpl) cell).getEnvelope(), ((CellImpl) cell).getGrid().getCRS()), layer);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy