1 package net.sumaris.core.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import com.google.common.base.Preconditions;
26 import com.vividsolutions.jts.geom.*;
27 import com.vividsolutions.jts.io.ParseException;
28 import com.vividsolutions.jts.io.WKTReader;
29 import com.vividsolutions.jts.io.WKTWriter;
30 import net.sumaris.core.dao.referential.location.Locations;
31 import org.apache.commons.lang3.StringUtils;
32
33 import java.text.NumberFormat;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 public class Geometries {
38
39 protected static final double EARTH_RADIUS = 6378288.0;
40 protected static Pattern COORDINATES_PATTERN = Pattern.compile("([0-9]{4})([NS])[\\s\\xA0]+([0-9]{5})([EW])[\\s\\xA0]*");
41 protected final static String WKT_POINT = "POINT(%s %s)";
42 protected final static String WKT_MULTIPOINT_BASE = "MULTIPOINT(%s)";
43 protected final static String WKT_MULTIPOINT_SUBPART = "(%s %s)";
44 protected final static String WKT_POLYGON = "POLYGON((%s))";
45 protected final static String WKT_POLYGON_SUBPART = "%s %s";
46 protected final static String WKT_MULTIPOLYGON = "MULTIPOLYGON((%s))";
47 protected final static String WKT_MULTIPOLYGON_SUBPART = "(%s)";
48
49 private static final GeometryFactory geometryFactory = new GeometryFactory();
50
51
52
53
54
55
56
57
58 public static Point createPoint(Double x, Double y) {
59 return geometryFactory.createPoint(new Coordinate(x, y));
60 }
61
62
63 public static Point createPoint(Number longitudeX, Number latitudeY) {
64 String wktString = String.format(WKT_POINT, longitudeX, latitudeY);
65 return (Point) getGeometry(wktString);
66 }
67
68
69
70
71
72
73
74
75
76
77 public static LineString createLine(Double minX, Double minY, Double maxX, Double maxY) {
78 return geometryFactory.createLineString(new Coordinate[]{
79 new Coordinate(minX, minY),
80 new Coordinate(maxX, maxY)});
81 }
82
83
84
85
86
87
88
89
90
91
92 public static Polygon createPolygon(Double minX, Double minY, Double maxX, Double maxY) {
93 return geometryFactory.createPolygon(new Coordinate[]{
94 new Coordinate(minX, minY),
95 new Coordinate(minX, maxY),
96 new Coordinate(maxX, maxY),
97 new Coordinate(maxX, minY),
98 new Coordinate(minX, minY)});
99 }
100
101
102
103
104
105
106
107 public static Geometry getGeometry(String wktString) {
108 try {
109 return new WKTReader(geometryFactory).read(wktString);
110 } catch (ParseException e) {
111 throw new RuntimeException(e);
112 }
113 }
114
115
116
117
118
119
120
121 public static String getWKTString(Geometry geometry) {
122 return new WKTWriter().write(geometry);
123 }
124
125
126
127
128
129
130
131 @Deprecated
132 public static Geometry wktToGeometry(String wktGeometry) {
133 return getGeometry(wktGeometry);
134 }
135
136
137 public static MultiPoint createMultiPoint(Number... coords) {
138 StringBuilder sb = new StringBuilder();
139 for (int i = 0; i < coords.length; i = i + 2) {
140 Number longitudeX = coords[i];
141 Number latitudeY = coords[i];
142 sb.append(", ");
143 sb.append(String.format(WKT_MULTIPOINT_SUBPART, longitudeX, latitudeY));
144 }
145
146 String wktString = String.format(WKT_MULTIPOINT_BASE, sb.substring(2));
147 Geometry geom = getGeometry(wktString);
148 return (MultiPoint) geom;
149 }
150
151 public static Number getLatitudeFromCoordinates(String coordinates) throws java.text.ParseException {
152 Preconditions.checkNotNull(coordinates);
153 Matcher matcher = COORDINATES_PATTERN.matcher(StringUtils.trim(coordinates));
154 if (!matcher.matches()) {
155 throw new RuntimeException("Not a coordinate string: " + coordinates);
156 }
157 String latitudeString = matcher.group(1);
158 String latitudeLetter = matcher.group(2);
159 try {
160 Number latitudeDegrees = NumberFormat.getInstance().parse(latitudeString.substring(0, 2));
161 Number latitudeMinutes = NumberFormat.getInstance().parse(latitudeString.substring(2));
162 double latitude = ("S".equals(latitudeLetter) ? -1 : 1) * ((double) (latitudeMinutes.doubleValue() / 60) + latitudeDegrees.doubleValue());
163 return latitude;
164 } catch (java.text.ParseException e) {
165 throw new java.text.ParseException("Unable to convert coordinates in lat/long.", 0);
166 }
167 }
168
169 public static Number getLongitudeFromCoordinates(String coordinates) throws java.text.ParseException {
170 Preconditions.checkNotNull(coordinates);
171 Matcher matcher = COORDINATES_PATTERN.matcher(coordinates.trim());
172 if (!matcher.matches()) {
173 throw new RuntimeException("Not a coordinate string:" + coordinates);
174 }
175 String longitudeString = matcher.group(3);
176 String longitudeLetter = matcher.group(4);
177 try {
178 Number longitudeDegrees = NumberFormat.getInstance().parse(longitudeString.substring(0, 3));
179 Number longitudeMinutes = NumberFormat.getInstance().parse(longitudeString.substring(3));
180 double longitude = ("W".equals(longitudeLetter) ? -1 : 1)
181 * ((double) (longitudeMinutes.doubleValue() / 60) + longitudeDegrees.doubleValue());
182 return longitude;
183 } catch (java.text.ParseException e) {
184 throw new java.text.ParseException("Unable to convert coordinates in lat/long.", 0);
185 }
186 }
187
188
189
190
191
192
193
194
195
196
197 public static Geometry createRectangleGeometry(Number bottomLeftX, Number bottomLeftY, Number topRightX, Number topRightY,
198 boolean returnHasMultiPolygon) {
199 String wtkPoint1 = String.format(WKT_POLYGON_SUBPART, bottomLeftX, bottomLeftY);
200 String wtkPoint2 = String.format(WKT_POLYGON_SUBPART, topRightX, bottomLeftY);
201 String wtkPoint3 = String.format(WKT_POLYGON_SUBPART, topRightX, topRightY);
202 String wtkPoint4 = String.format(WKT_POLYGON_SUBPART, bottomLeftX, topRightY);
203 String wtkPolygon = wtkPoint1 + "," + wtkPoint2 + "," + wtkPoint3 + "," + wtkPoint4 + "," + wtkPoint1;
204 String wktString;
205 if (!returnHasMultiPolygon) {
206 wktString = String.format(WKT_POLYGON, wtkPolygon);
207 } else {
208 wktString = String.format(WKT_MULTIPOLYGON, String.format(WKT_MULTIPOLYGON_SUBPART, wtkPolygon));
209 }
210
211 return getGeometry(wktString);
212 }
213
214
215
216
217
218
219
220
221
222
223 public static int getDistanceInMeters(Number startLatitude,
224 Number startLongitude,
225 Number endLatitude,
226 Number endLongitude) {
227 Preconditions.checkNotNull(startLatitude);
228 Preconditions.checkNotNull(startLongitude);
229 Preconditions.checkNotNull(endLatitude);
230 Preconditions.checkNotNull(endLongitude);
231
232 double sLat = startLatitude.doubleValue() * Math.PI / 180.0;
233 double sLong = startLongitude.doubleValue() * Math.PI / 180.0;
234 double eLat = endLatitude.doubleValue() * Math.PI / 180.0;
235 double eLong = endLongitude.doubleValue() * Math.PI / 180.0;
236
237 Double d = EARTH_RADIUS *
238 (Math.PI / 2 - Math.asin(Math.sin(eLat) * Math.sin(sLat)
239 + Math.cos(eLong - sLong) * Math.cos(eLat) * Math.cos(sLat)));
240 return d.intValue();
241 }
242
243
244
245
246
247
248
249 public static String getDistanceInMilles(Number distance) {
250 String distanceText;
251 if (distance != null) {
252 double distanceInMilles = distance.doubleValue() / 1852;
253 distanceText = String.format("%.3d", distanceInMilles);
254
255 } else {
256 distanceText = "";
257 }
258 return distanceText;
259 }
260 }