]> git.argeo.org Git - gpl/argeo-suite.git/blob - GeoUtils.java
3cec484c2f14d358f86b2cb66bbcab8bb8ee51c4
[gpl/argeo-suite.git] / GeoUtils.java
1 package org.argeo.app.geo;
2
3 import java.io.IOException;
4 import java.io.Serializable;
5 import java.io.Writer;
6 import java.nio.file.Path;
7 import java.util.ArrayList;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11
12 import javax.measure.Quantity;
13 import javax.measure.quantity.Area;
14 import javax.xml.transform.TransformerException;
15
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;
60
61 import si.uom.SI;
62 import tech.units.indriya.quantity.Quantities;
63
64 /** Utilities around geographical format, mostly wrapping GeoTools patterns. */
65 public class GeoUtils {
66
67 /** In square meters. */
68 public static Quantity<Area> calcArea(SimpleFeature feature) {
69 try {
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);
74
75 MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
76
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");
81 }
82 }
83
84 public static void exportToSvg(SimpleFeatureCollection features, Writer out, int width, int height) {
85 try {
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=\"");
95
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);
100
101 MathTransform transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, auto);
102
103 Polygon projed = (Polygon) JTS.transform(p, transform);
104
105 for (Coordinate coord : projed.getCoordinates()) {
106 double x = coord.x;
107 if (x < minX)
108 minX = x;
109 if (x > maxX)
110 maxX = x;
111 double y = -coord.y;
112 if (y < minY)
113 minY = y;
114 if (y > maxY)
115 maxY = y;
116 sb.append(x + "," + y + " ");
117 }
118 sb.append("\">");
119 sb.append("</polyline>\n");
120 shapes.add(sb.toString());
121
122 }
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");
130 out.write(">\n");
131 for (String shape : shapes) {
132 out.write(shape);
133 out.write("\n");
134 }
135 out.write("</svg>");
136 } catch (IOException | FactoryException | MismatchedDimensionException | TransformException e) {
137 throw new RuntimeException("Cannot export to SVG", e);
138 }
139 }
140
141 /** Write a list of simple features to a shapefile. */
142 public static void saveFeaturesAsShapefile(SimpleFeatureType featureType, List<SimpleFeature> features,
143 Path shpFile) {
144 try {
145 ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
146
147 Map<String, Serializable> params = new HashMap<>();
148 params.put("url", shpFile.toUri().toURL());
149
150 params.put("create spatial index", Boolean.TRUE);
151
152 ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
153 newDataStore.createSchema(featureType);
154
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);
160
161 try (Transaction transaction = new DefaultTransaction("create")) {
162 try {
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);
169 }
170 }
171 } else {
172 throw new IllegalArgumentException(typeName + " does not support read/write access");
173 }
174 } catch (IOException e) {
175 throw new RuntimeException("Cannot write shapefile " + shpFile, e);
176 }
177 }
178
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);
183
184 // try {
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();
191 // }
192
193 return style;
194 }
195
196 public static String createSldFromCss(String name, String title, String css) {
197
198 StyleFactory sf = CommonFactoryFinder.getStyleFactory();
199
200 StyledLayerDescriptor sld = sf.createStyledLayerDescriptor();
201 sld.setName(name);
202 sld.setTitle(title);
203
204 UserLayer layer = sf.createUserLayer();
205 layer.setName("default");
206
207 org.opengis.style.Style style = createStyleFromCss(css);
208 layer.userStyles().add((Style) style);
209
210 sld.layers().add(layer);
211 try {
212 SLDTransformer styleTransform = new SLDTransformer();
213 String xml = styleTransform.transform(sld);
214 // System.out.println(xml);
215 return xml;
216 } catch (TransformerException e) {
217 throw new IllegalStateException(e);
218 }
219 }
220
221 public static void main(String... args) {
222 String css = """
223 * {
224 mark: symbol(circle);
225 mark-size: 6px;
226 }
227
228 :mark {
229 fill: red;
230 }
231
232 """;
233 createSldFromCss("test", "Test", css);
234 }
235
236 public static String createTestSLD() {
237
238 StyleFactory sf = CommonFactoryFinder.getStyleFactory();
239 FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
240
241 StyledLayerDescriptor sld = sf.createStyledLayerDescriptor();
242 sld.setName("sld");
243 sld.setTitle("Example");
244 sld.setAbstract("Example Style Layer Descriptor");
245
246 UserLayer layer = sf.createUserLayer();
247 layer.setName("layer");
248
249 //
250 // define constraint limited what features the sld applies to
251 FeatureTypeConstraint constraint = sf.createFeatureTypeConstraint("Feature", Filter.INCLUDE);
252
253 layer.layerFeatureConstraints().add(constraint);
254
255 //
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");
261
262 //
263 // define feature type styles used to actually define how features are rendered
264 FeatureTypeStyle featureTypeStyle = sf.createFeatureTypeStyle();
265
266 // RULE 1
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)));
273
274 //
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));
278
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
282 //
283 // OnLineResourceImpl png = new OnLineResourceImpl(new URI("file:city.png"));
284 // png.freeze(); // freeze to prevent modification at runtime
285
286 //
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
293
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
299
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);
303
304 rule1.symbolizers().add(pointSymbolizer);
305
306 featureTypeStyle.rules().add(rule1);
307
308 //
309 // RULE 2 Default
310
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);
320
321 style.featureTypeStyles().add(featureTypeStyle);
322
323 layer.userStyles().add(style);
324
325 sld.layers().add(layer);
326
327 try {
328 SLDTransformer styleTransform = new SLDTransformer();
329 String xml = styleTransform.transform(sld);
330 System.out.println(xml);
331 return xml;
332 } catch (TransformerException e) {
333 throw new IllegalStateException(e);
334 }
335
336 }
337
338 /** Singleton. */
339 private GeoUtils() {
340 }
341 }