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);
}
}