1 package org
.argeo
.app
.geo
;
3 import java
.io
.IOException
;
4 import java
.io
.Serializable
;
6 import java
.nio
.file
.Path
;
7 import java
.util
.ArrayList
;
8 import java
.util
.HashMap
;
12 import javax
.measure
.Quantity
;
13 import javax
.measure
.quantity
.Area
;
14 import javax
.xml
.transform
.TransformerException
;
16 import org
.geotools
.data
.DefaultTransaction
;
17 import org
.geotools
.data
.Transaction
;
18 import org
.geotools
.data
.collection
.ListFeatureCollection
;
19 import org
.geotools
.data
.shapefile
.ShapefileDataStore
;
20 import org
.geotools
.data
.shapefile
.ShapefileDataStoreFactory
;
21 import org
.geotools
.data
.simple
.SimpleFeatureCollection
;
22 import org
.geotools
.data
.simple
.SimpleFeatureIterator
;
23 import org
.geotools
.data
.simple
.SimpleFeatureSource
;
24 import org
.geotools
.data
.simple
.SimpleFeatureStore
;
25 import org
.geotools
.factory
.CommonFactoryFinder
;
26 import org
.geotools
.geometry
.jts
.JTS
;
27 import org
.geotools
.referencing
.CRS
;
28 import org
.geotools
.referencing
.crs
.DefaultGeographicCRS
;
29 import org
.geotools
.styling
.AnchorPoint
;
30 import org
.geotools
.styling
.Displacement
;
31 import org
.geotools
.styling
.FeatureTypeConstraint
;
32 import org
.geotools
.styling
.FeatureTypeStyle
;
33 import org
.geotools
.styling
.Fill
;
34 import org
.geotools
.styling
.Graphic
;
35 import org
.geotools
.styling
.PointSymbolizer
;
36 import org
.geotools
.styling
.Rule
;
37 import org
.geotools
.styling
.Stroke
;
38 import org
.geotools
.styling
.Style
;
39 import org
.geotools
.styling
.StyleFactory
;
40 import org
.geotools
.styling
.StyledLayerDescriptor
;
41 import org
.geotools
.styling
.UserLayer
;
42 import org
.geotools
.styling
.css
.CssParser
;
43 import org
.geotools
.styling
.css
.CssTranslator
;
44 import org
.geotools
.styling
.css
.Stylesheet
;
45 import org
.geotools
.xml
.styling
.SLDTransformer
;
46 import org
.locationtech
.jts
.geom
.Coordinate
;
47 import org
.locationtech
.jts
.geom
.Point
;
48 import org
.locationtech
.jts
.geom
.Polygon
;
49 import org
.opengis
.feature
.simple
.SimpleFeature
;
50 import org
.opengis
.feature
.simple
.SimpleFeatureType
;
51 import org
.opengis
.filter
.Filter
;
52 import org
.opengis
.filter
.FilterFactory2
;
53 import org
.opengis
.filter
.expression
.Expression
;
54 import org
.opengis
.geometry
.MismatchedDimensionException
;
55 import org
.opengis
.referencing
.FactoryException
;
56 import org
.opengis
.referencing
.crs
.CoordinateReferenceSystem
;
57 import org
.opengis
.referencing
.operation
.MathTransform
;
58 import org
.opengis
.referencing
.operation
.TransformException
;
59 import org
.opengis
.style
.GraphicalSymbol
;
62 import tech
.units
.indriya
.quantity
.Quantities
;
64 /** Utilities around geographical format, mostly wrapping GeoTools patterns. */
65 public class GeoUtils
{
67 /** In square meters. */
68 public static Quantity
<Area
> calcArea(SimpleFeature feature
) {
70 Polygon p
= (Polygon
) feature
.getDefaultGeometry();
71 Point centroid
= p
.getCentroid();
72 String code
= "AUTO:42001," + centroid
.getX() + "," + centroid
.getY();
73 CoordinateReferenceSystem auto
= CRS
.decode(code
);
75 MathTransform transform
= CRS
.findMathTransform(DefaultGeographicCRS
.WGS84
, auto
);
77 Polygon projed
= (Polygon
) JTS
.transform(p
, transform
);
78 return Quantities
.getQuantity(projed
.getArea(), SI
.SQUARE_METRE
);
79 } catch (MismatchedDimensionException
| FactoryException
| TransformException e
) {
80 throw new IllegalStateException("Cannot claculate area of feature");
84 public static void exportToSvg(SimpleFeatureCollection features
, Writer out
, int width
, int height
) {
86 double minY
= Double
.POSITIVE_INFINITY
;
87 double maxY
= Double
.NEGATIVE_INFINITY
;
88 double minX
= Double
.POSITIVE_INFINITY
;
89 double maxX
= Double
.NEGATIVE_INFINITY
;
90 List
<String
> shapes
= new ArrayList
<>();
91 for (SimpleFeatureIterator it
= features
.features(); it
.hasNext();) {
92 SimpleFeature feature
= it
.next();
93 StringBuffer sb
= new StringBuffer();
94 sb
.append("<polyline style=\"stroke-width:1;stroke:#000000;fill:none;\" points=\"");
96 Polygon p
= (Polygon
) feature
.getDefaultGeometry();
97 Point centroid
= p
.getCentroid();
98 String code
= "AUTO:42001," + centroid
.getX() + "," + centroid
.getY();
99 CoordinateReferenceSystem auto
= CRS
.decode(code
);
101 MathTransform transform
= CRS
.findMathTransform(DefaultGeographicCRS
.WGS84
, auto
);
103 Polygon projed
= (Polygon
) JTS
.transform(p
, transform
);
105 for (Coordinate coord
: projed
.getCoordinates()) {
116 sb
.append(x
+ "," + y
+ " ");
119 sb
.append("</polyline>\n");
120 shapes
.add(sb
.toString());
123 double viewportHeight
= maxY
- minY
;
124 double viewportWidth
= maxX
- minX
;
125 out
.write("<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
126 out
.write(" width=\"" + width
+ "\"\n");
127 out
.write(" height=\"" + height
+ "\"\n");
128 out
.write(" viewBox=\"" + minX
+ " " + minY
+ " " + viewportWidth
+ " " + viewportHeight
+ "\"\n");
129 out
.write(" preserveAspectRatio=\"xMidYMid meet\"\n");
131 for (String shape
: shapes
) {
136 } catch (IOException
| FactoryException
| MismatchedDimensionException
| TransformException e
) {
137 throw new RuntimeException("Cannot export to SVG", e
);
141 /** Write a list of simple features to a shapefile. */
142 public static void saveFeaturesAsShapefile(SimpleFeatureType featureType
, List
<SimpleFeature
> features
,
145 ShapefileDataStoreFactory dataStoreFactory
= new ShapefileDataStoreFactory();
147 Map
<String
, Serializable
> params
= new HashMap
<>();
148 params
.put("url", shpFile
.toUri().toURL());
150 params
.put("create spatial index", Boolean
.TRUE
);
152 ShapefileDataStore newDataStore
= (ShapefileDataStore
) dataStoreFactory
.createNewDataStore(params
);
153 newDataStore
.createSchema(featureType
);
155 String typeName
= newDataStore
.getTypeNames()[0];
156 SimpleFeatureSource featureSource
= newDataStore
.getFeatureSource(typeName
);
157 if (featureSource
instanceof SimpleFeatureStore
) {
158 SimpleFeatureStore featureStore
= (SimpleFeatureStore
) featureSource
;
159 SimpleFeatureCollection collection
= new ListFeatureCollection(featureType
, features
);
161 try (Transaction transaction
= new DefaultTransaction("create")) {
163 featureStore
.setTransaction(transaction
);
164 featureStore
.addFeatures(collection
);
165 transaction
.commit();
166 } catch (Exception problem
) {
167 transaction
.rollback();
168 throw new RuntimeException("Cannot write shapefile " + shpFile
, problem
);
172 throw new IllegalArgumentException(typeName
+ " does not support read/write access");
174 } catch (IOException e
) {
175 throw new RuntimeException("Cannot write shapefile " + shpFile
, e
);
179 public static org
.opengis
.style
.Style
createStyleFromCss(String css
) {
180 Stylesheet ss
= CssParser
.parse(css
);
181 CssTranslator translator
= new CssTranslator();
182 org
.opengis
.style
.Style style
= translator
.translate(ss
);
185 // SLDTransformer styleTransform = new SLDTransformer();
186 // String xml = styleTransform.transform(style);
187 // System.out.println(xml);
188 // } catch (TransformerException e) {
189 // // TODO Auto-generated catch block
190 // e.printStackTrace();
196 public static String
createSldFromCss(String name
, String title
, String css
) {
198 StyleFactory sf
= CommonFactoryFinder
.getStyleFactory();
200 StyledLayerDescriptor sld
= sf
.createStyledLayerDescriptor();
204 UserLayer layer
= sf
.createUserLayer();
205 layer
.setName("default");
207 org
.opengis
.style
.Style style
= createStyleFromCss(css
);
208 layer
.userStyles().add((Style
) style
);
210 sld
.layers().add(layer
);
212 SLDTransformer styleTransform
= new SLDTransformer();
213 String xml
= styleTransform
.transform(sld
);
214 // System.out.println(xml);
216 } catch (TransformerException e
) {
217 throw new IllegalStateException(e
);
221 public static void main(String
... args
) {
224 mark: symbol(circle);
233 createSldFromCss("test", "Test", css
);
236 public static String
createTestSLD() {
238 StyleFactory sf
= CommonFactoryFinder
.getStyleFactory();
239 FilterFactory2 ff
= CommonFactoryFinder
.getFilterFactory2();
241 StyledLayerDescriptor sld
= sf
.createStyledLayerDescriptor();
243 sld
.setTitle("Example");
244 sld
.setAbstract("Example Style Layer Descriptor");
246 UserLayer layer
= sf
.createUserLayer();
247 layer
.setName("layer");
250 // define constraint limited what features the sld applies to
251 FeatureTypeConstraint constraint
= sf
.createFeatureTypeConstraint("Feature", Filter
.INCLUDE
);
253 layer
.layerFeatureConstraints().add(constraint
);
256 // create a "user defined" style
257 Style style
= sf
.createStyle();
258 style
.setName("style");
259 style
.getDescription().setTitle("User Style");
260 style
.getDescription().setAbstract("Definition of Style");
263 // define feature type styles used to actually define how features are rendered
264 FeatureTypeStyle featureTypeStyle
= sf
.createFeatureTypeStyle();
267 // first rule to draw cities
268 Rule rule1
= sf
.createRule();
269 rule1
.setName("rule1");
270 rule1
.getDescription().setTitle("City");
271 rule1
.getDescription().setAbstract("Rule for drawing cities");
272 // rule1.setFilter(ff.less(ff.property("POPULATION"), ff.literal(50000)));
275 // create the graphical mark used to represent a city
276 Stroke stroke
= sf
.stroke(ff
.literal("#000000"), null, null, null, null, null, null);
277 Fill fill
= sf
.fill(null, ff
.literal(java
.awt
.Color
.BLUE
), ff
.literal(1.0));
279 // // OnLineResource implemented by gt-metadata - so no factory!
280 // OnLineResourceImpl svg = new OnLineResourceImpl(new URI("file:city.svg"));
281 // svg.freeze(); // freeze to prevent modification at runtime
283 // OnLineResourceImpl png = new OnLineResourceImpl(new URI("file:city.png"));
284 // png.freeze(); // freeze to prevent modification at runtime
287 // List of symbols is considered in order with the rendering engine choosing
288 // the first one it can handle. Allowing for svg, png, mark order
289 List
<GraphicalSymbol
> symbols
= new ArrayList
<>();
290 // symbols.add(sf.externalGraphic(svg, "svg", null)); // svg preferred
291 // symbols.add(sf.externalGraphic(png, "png", null)); // png preferred
292 symbols
.add(sf
.mark(ff
.literal("circle"), fill
, stroke
)); // simple circle backup plan
294 Expression opacity
= null; // use default
295 Expression size
= ff
.literal(10);
296 Expression rotation
= null; // use default
297 AnchorPoint anchor
= null; // use default
298 Displacement displacement
= null; // use default
300 // define a point symbolizer of a small circle
301 Graphic city
= sf
.graphic(symbols
, opacity
, size
, rotation
, anchor
, displacement
);
302 PointSymbolizer pointSymbolizer
= sf
.pointSymbolizer("point", ff
.property("the_geom"), null, null, city
);
304 rule1
.symbolizers().add(pointSymbolizer
);
306 featureTypeStyle
.rules().add(rule1
);
311 // List<GraphicalSymbol> dotSymbols = new ArrayList<>();
312 // dotSymbols.add(sf.mark(ff.literal("circle"), null, null));
313 // Graphic dotGraphic = sf.graphic(dotSymbols, null, ff.literal(3), null, null, null);
314 // PointSymbolizer dotSymbolizer = sf.pointSymbolizer("dot", ff.property("the_geom"), null, null, dotGraphic);
315 // List<org.opengis.style.Symbolizer> symbolizers = new ArrayList<>();
316 // symbolizers.add(dotSymbolizer);
317 // Filter other = null; // null will mark this rule as "other" accepting all remaining features
318 // Rule rule2 = sf.rule("default", null, null, Double.MIN_VALUE, Double.MAX_VALUE, symbolizers, other);
319 // featureTypeStyle.rules().add(rule2);
321 style
.featureTypeStyles().add(featureTypeStyle
);
323 layer
.userStyles().add(style
);
325 sld
.layers().add(layer
);
328 SLDTransformer styleTransform
= new SLDTransformer();
329 String xml
= styleTransform
.transform(sld
);
330 System
.out
.println(xml
);
332 } catch (TransformerException e
) {
333 throw new IllegalStateException(e
);