]> git.argeo.org Git - gpl/argeo-suite.git/blob - GpxUtils.java
15f6b89675e5a03cf16bc03882271baa36ce6f5b
[gpl/argeo-suite.git] / GpxUtils.java
1 package org.argeo.app.geo;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.io.OutputStreamWriter;
7 import java.io.Writer;
8 import java.nio.charset.StandardCharsets;
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.Objects;
12 import java.util.StringTokenizer;
13
14 import javax.xml.parsers.ParserConfigurationException;
15 import javax.xml.parsers.SAXParser;
16 import javax.xml.parsers.SAXParserFactory;
17
18 import org.geotools.data.DataUtilities;
19 import org.geotools.feature.SchemaException;
20 import org.geotools.feature.simple.SimpleFeatureBuilder;
21 import org.locationtech.jts.geom.Coordinate;
22 import org.locationtech.jts.geom.Geometry;
23 import org.locationtech.jts.geom.GeometryFactory;
24 import org.locationtech.jts.geom.LineString;
25 import org.locationtech.jts.geom.Polygon;
26 import org.opengis.feature.simple.SimpleFeature;
27 import org.opengis.feature.simple.SimpleFeatureType;
28 import org.xml.sax.Attributes;
29 import org.xml.sax.SAXException;
30 import org.xml.sax.helpers.DefaultHandler;
31
32 /** Utilities around the GPX format. */
33 public class GpxUtils {
34 /** GPX as a LineString in WGS84 (feature type with only the_geom). */
35 public static final SimpleFeatureType LINESTRING_FEATURE_TYPE;
36 /** GPX as a Polygon in WGS84 (feature type with only the_geom). */
37 public static final SimpleFeatureType POLYGON_FEATURE_TYPE;
38
39 static {
40 try {
41 LINESTRING_FEATURE_TYPE = DataUtilities.createType("Area", "the_geom:LineString:srid=4326");
42 POLYGON_FEATURE_TYPE = DataUtilities.createType("Area", "the_geom:Polygon:srid=4326");
43 } catch (SchemaException e) {
44 throw new RuntimeException("Cannot create GPX Feature type", e);
45 }
46 }
47
48 /**
49 * Converts a GPX track to either a {@link Geometry} with WGS84 coordinates
50 * ({@link LineString} or {@link Polygon}) or a {@link SimpleFeature} (with
51 * {@link #LINESTRING_FEATURE_TYPE}).
52 */
53 @SuppressWarnings("unchecked")
54 public static <T> T parseGpxTrackTo(InputStream in, Class<T> clss) throws IOException {
55 GeometryFactory geometryFactory = GeoTools.GEOMETRY_FACTORY;
56 List<Coordinate> coordinates = new ArrayList<>();
57 try {
58 SAXParserFactory factory = SAXParserFactory.newInstance();
59 SAXParser saxParser = factory.newSAXParser();
60
61 saxParser.parse(in, new DefaultHandler() {
62
63 @Override
64 public void startElement(String uri, String localName, String qName, Attributes attributes)
65 throws SAXException {
66 if ("trkpt".equals(qName)) {
67 Double latitude = Double.parseDouble(attributes.getValue("lat"));
68 Double longitude = Double.parseDouble(attributes.getValue("lon"));
69 // TODO elevation in 3D context
70 Coordinate coordinate = new Coordinate(longitude, latitude);
71 coordinates.add(coordinate);
72 }
73 }
74
75 });
76 } catch (ParserConfigurationException | SAXException e) {
77 throw new RuntimeException("Cannot convert GPX", e);
78 }
79
80 if (LineString.class.isAssignableFrom(clss)) {
81 LineString lineString = geometryFactory
82 .createLineString(coordinates.toArray(new Coordinate[coordinates.size()]));
83 return (T) lineString;
84 } else if (Polygon.class.isAssignableFrom(clss)) {
85 // close the line string
86 coordinates.add(coordinates.get(0));
87 Polygon polygon = geometryFactory.createPolygon(coordinates.toArray(new Coordinate[coordinates.size()]));
88 return (T) polygon;
89 }
90 // TODO MultiPoint? MultiLine? etc.
91 else if (SimpleFeature.class.isAssignableFrom(clss)) {
92 SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(LINESTRING_FEATURE_TYPE);
93 LineString lineString = geometryFactory
94 .createLineString(coordinates.toArray(new Coordinate[coordinates.size()]));
95 featureBuilder.add(lineString);
96 SimpleFeature area = featureBuilder.buildFeature(null);
97 return (T) area;
98 } else {
99 throw new IllegalArgumentException("Unsupported format " + clss);
100 }
101 }
102
103 /** @deprecated Use {@link #parseGpxTrackTo(InputStream, Class)} instead. */
104 @Deprecated
105 public static SimpleFeature parseGpxToPolygon(InputStream in) throws IOException {
106 SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(POLYGON_FEATURE_TYPE);
107 Polygon polygon = parseGpxTrackTo(in, Polygon.class);
108 featureBuilder.add(polygon);
109 SimpleFeature area = featureBuilder.buildFeature(null);
110 return area;
111 }
112
113 /** Write ODK GepShape as a GPX file. */
114 public static void writeGeoShapeAsGpx(String geoShape, OutputStream out) throws IOException {
115 Objects.requireNonNull(geoShape);
116 Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
117 writer.append("<gpx><trk><trkseg>");
118 StringTokenizer stSeg = new StringTokenizer(geoShape.trim(), ";");
119 while (stSeg.hasMoreTokens()) {
120 StringTokenizer stPt = new StringTokenizer(stSeg.nextToken().trim(), " ");
121 String lat = stPt.nextToken();
122 String lng = stPt.nextToken();
123 String alt = stPt.nextToken();
124 // String precision = stPt.nextToken();
125 writer.append("<trkpt");
126 writer.append(" lat=\"").append(lat).append('\"');
127 writer.append(" lon=\"").append(lng).append('\"');
128 if (!alt.equals("0.0")) {
129 writer.append('>');
130 writer.append("<ele>").append(alt).append("</ele>");
131 writer.append("</trkpt>");
132 } else {
133 writer.append("/>");
134 }
135 }
136 writer.append("</trkseg></trk></gpx>");
137 writer.flush();
138 }
139
140 /** Singleton. */
141 private GpxUtils() {
142 }
143 }