1 package org
.argeo
.app
.geo
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
6 import java
.io
.OutputStreamWriter
;
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
;
14 import javax
.xml
.parsers
.ParserConfigurationException
;
15 import javax
.xml
.parsers
.SAXParser
;
16 import javax
.xml
.parsers
.SAXParserFactory
;
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
;
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
;
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
);
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}).
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
<>();
58 SAXParserFactory factory
= SAXParserFactory
.newInstance();
59 SAXParser saxParser
= factory
.newSAXParser();
61 saxParser
.parse(in
, new DefaultHandler() {
64 public void startElement(String uri
, String localName
, String qName
, Attributes attributes
)
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
);
76 } catch (ParserConfigurationException
| SAXException e
) {
77 throw new RuntimeException("Cannot convert GPX", e
);
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()]));
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);
99 throw new IllegalArgumentException("Unsupported format " + clss
);
103 /** @deprecated Use {@link #parseGpxTrackTo(InputStream, Class)} instead. */
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);
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")) {
130 writer
.append("<ele>").append(alt
).append("</ele>");
131 writer
.append("</trkpt>");
136 writer
.append("</trkseg></trk></gpx>");