1 package net.sumaris.core.dao.referential.location;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 import com.google.common.base.Preconditions;
29 import com.google.common.base.Strings;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.Lists;
32 import com.google.common.collect.Sets;
33 import com.vividsolutions.jts.geom.Coordinate;
34 import com.vividsolutions.jts.geom.Geometry;
35 import net.sumaris.core.exception.SumarisTechnicalException;
36 import net.sumaris.core.util.Geometries;
37 import org.apache.commons.lang3.StringUtils;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import org.springframework.core.io.Resource;
41 import org.springframework.core.io.ResourceLoader;
42
43 import java.io.BufferedReader;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Set;
50 import java.util.stream.Collectors;
51
52
53
54
55
56
57
58 public class Locations {
59
60
61
62 private static final Logger log = LoggerFactory.getLogger(Locations.class);
63
64
65
66
67 protected Locations() {
68
69 }
70
71
72
73
74
75
76
77
78 public static String getRectangleLabelByLatLong(Number latitude, Number longitude) {
79 if (longitude == null || latitude == null) {
80 return null;
81 }
82 String locationLabel = null;
83 double lat = latitude.doubleValue();
84 double lon = longitude.doubleValue();
85
86
87 if (((lon >= 0 && lon < 42) && (lat >= 30 && lat < 47.5))
88 || ((lon >= -6 && lon < 0) && (lat >= 35 && lat < 40))) {
89
90
91 double nbdemidegreeLat = Math.floor((lat - 30)) * 2;
92
93
94 double nbdemidegreeLong = Math.floor((lon + 6)) * 2;
95
96
97 char letter = (char) ((int) (Math.floor(nbdemidegreeLong / 10) + 65));
98 int rest = (int) (nbdemidegreeLong % 10);
99 locationLabel = "M" + ((int) nbdemidegreeLat) + letter + rest;
100 }
101
102
103 else if ((lon >= -50 && lon <= 70) && (lat >= 36 && lat <= 89)) {
104 int halfDegreesNb = (int) Math.floor((lat - 36) * 2) + 1;
105 double degreesNb = Math.floor(lon + 50);
106 char letter = (char) ((int) (Math.floor(degreesNb / 10) + 65));
107 int rest = (int) (degreesNb % 10);
108 locationLabel = String.valueOf(halfDegreesNb) + letter + rest;
109 }
110 return locationLabel;
111 }
112
113
114 public static Geometry getGeometryFromRectangleLabel(String rectangleLabel) {
115 return getGeometryFromRectangleLabel(rectangleLabel, false);
116 }
117
118 public static Geometry getGeometryFromRectangleLabel(String rectangleLabel, boolean useMultiPolygon) {
119 Preconditions.checkNotNull(rectangleLabel);
120 Preconditions.checkArgument(StringUtils.isNotBlank(rectangleLabel), "Argument 'rectangleLabel' must not be empty string.");
121
122 Geometry geometry;
123
124
125 if (rectangleLabel.startsWith("M")) {
126 String rectangleLabelNoLetter = rectangleLabel.substring(1);
127 String nbdemidegreeLat = rectangleLabelNoLetter.substring(0, 2);
128 String letter = rectangleLabelNoLetter.substring(2, 3);
129 String rest = rectangleLabelNoLetter.substring(3);
130
131 double latitude = Double.parseDouble(nbdemidegreeLat) * 0.5f + 30;
132
133 double longitude = Double.parseDouble(rest) * 0.5f + (letter.charAt(0) - 65) * 5 - 6f;
134 geometry = Geometries.createRectangleGeometry(longitude, latitude, longitude + 0.5f, latitude + 0.5f, useMultiPolygon );
135 }
136
137
138 else {
139 String nbdemidegreeLat = rectangleLabel.substring(0, 2);
140 String letter = rectangleLabel.substring(2, 3);
141 String rest = rectangleLabel.substring(3);
142
143
144 if (rectangleLabel.length() == 5) {
145 nbdemidegreeLat = rectangleLabel.substring(0, 3);
146 letter = rectangleLabel.substring(3, 4);
147 rest = rectangleLabel.substring(4);
148 }
149
150 double latitude = Double.parseDouble(nbdemidegreeLat) * 0.5f + 35.5f;
151 double longitude = Double.parseDouble(rest) + (letter.charAt(0) - 65) * 10 - 50;
152
153 geometry = Geometries.createRectangleGeometry(longitude, latitude, longitude + 1, latitude + 0.5f, useMultiPolygon );
154 }
155
156 return geometry;
157 }
158
159
160
161
162
163
164
165 @Deprecated
166 public static Geometry getGeometryFromSquare10Label(String square1010) {
167 return getGeometryFromMinuteSquareLabel(square1010, 10, true);
168 }
169
170 public static Geometry getGeometryFromMinuteSquareLabel(String label, int minute, boolean useMultiPolygon) {
171 Preconditions.checkNotNull(label);
172 Preconditions.checkArgument(StringUtils.isNotBlank(label));
173 if (minute < 10) {
174 Preconditions.checkArgument(label.length() == 10, String.format("Invalid square format. Expected 10 characters for %s'x%s' square", minute, minute));
175 }
176 else {
177 Preconditions.checkArgument(label.length() == 8, String.format("Invalid square format. Expected 8 characters for %s'x%s' square", minute, minute));
178 }
179
180 int cadran = Integer.parseInt(label.substring(0, 1));
181
182 double signLongitude;
183 double signLatitude;
184 switch (cadran) {
185 case 1:
186 signLatitude = 1.0;
187 signLongitude = -1.0;
188 break;
189 case 2:
190 signLatitude = 1.0;
191 signLongitude = 1.0;
192 break;
193 case 3:
194 signLatitude = -1.0;
195 signLongitude = 1.0;
196 break;
197 case 4:
198 signLatitude = -1.0;
199 signLongitude = -1.0;
200 break;
201 default:
202 throw new IllegalArgumentException("Unable to parse quadrant");
203 }
204
205 int offset = 1;
206 int nbMinuteChar = minute < 10 ? 2 : 1;
207
208
209 double intLatitude = Double.parseDouble(label.substring(offset, offset+2));
210 offset += 2;
211 double decLatitude = Double.parseDouble(label.substring(offset, offset + nbMinuteChar)) * minute / 60.0;
212 offset += nbMinuteChar;
213 double latitude = signLatitude * (intLatitude + decLatitude);
214
215
216 double intLongitude = Double.parseDouble(label.substring(offset, offset+3));
217 offset += 3;
218 double decLongitude = Double.parseDouble(label.substring(offset, offset+nbMinuteChar)) * minute / 60.0;
219 double longitude = signLongitude * (intLongitude + decLongitude);
220
221 return Geometries.createRectangleGeometry(
222 longitude,
223 latitude,
224 longitude + signLongitude * minute / 60.0,
225 latitude + signLatitude * minute / 60.0,
226 useMultiPolygon );
227 }
228
229
230
231
232
233
234 public static String convertMinuteSquareToRectangle(final String squareLabel, final int minute) {
235 String calculRectangle = "";
236
237 if (squareLabel == null || squareLabel.length() != 8) {
238 return calculRectangle;
239 }
240
241 int cadran = Integer.parseInt(squareLabel.substring(0, 1));
242
243 double signLongitude;
244 double signLatitude;
245 switch (cadran) {
246 case 1:
247 signLatitude = 1.0;
248 signLongitude = -1.0;
249 break;
250 case 2:
251 signLatitude = 1.0;
252 signLongitude = 1.0;
253 break;
254 case 3:
255 signLatitude = -1.0;
256 signLongitude = 1.0;
257 break;
258 case 4:
259 signLatitude = -1.0;
260 signLongitude = -1.0;
261 break;
262 default:
263 throw new IllegalArgumentException("Unable to parse quadrant");
264 }
265
266 int offset = 1;
267 int nbMinuteChar = minute < 10 ? 2 : 1;
268
269 double intLatitude = Double.parseDouble(squareLabel.substring(offset, offset+2));
270 offset += 2;
271 double decLatitude = Double.parseDouble(squareLabel.substring(offset, offset + nbMinuteChar)) * minute / 60.0;
272 offset += nbMinuteChar;
273 double latitude = signLatitude * (intLatitude + decLatitude);
274
275
276 double intLongitude = Double.parseDouble(squareLabel.substring(offset, offset+3));
277 offset += 3;
278 double decLongitude = Double.parseDouble(squareLabel.substring(offset, offset + nbMinuteChar)) * minute / 60.0;
279 double longitude = signLongitude * (intLongitude + decLongitude);
280
281 return getRectangleLabelByLatLong(latitude, longitude);
282 }
283
284
285
286
287
288
289 public static String convertSquare10ToRectangle(final String square1010) {
290 return convertMinuteSquareToRectangle(square1010, 10);
291 }
292
293
294
295
296
297
298 public static Set<String> convertRectangleToSquares10(final String rectangleLabel) {
299 return convertRectangleToMinuteSquares(rectangleLabel, 10);
300 }
301
302
303 public static Set<String> getAllIcesRectangleLabels(ResourceLoader resourceLoader, boolean failSafe) {
304
305 try {
306 return readLines(resourceLoader.getResource("classpath:referential/ices_rectangles.txt"));
307 } catch (SumarisTechnicalException e) {
308 if (failSafe) {
309
310 } else {
311 throw e;
312 }
313 }
314
315 Set<String> result = Sets.newHashSet();
316 StringBuilder sb = new StringBuilder();
317 for (int i = 1; i <= 98; i++) {
318 sb.setLength(0);
319 sb.append(i < 10 ? ("0" + i) : i);
320 for (char l = 'A'; l <= 'M'; l++) {
321 sb.setLength(2);
322 sb.append(l);
323 for (int j = 0; j <= 8; j++) {
324 sb.setLength(3);
325 sb.append(j);
326 result.add(sb.toString());
327 }
328 }
329 }
330
331 return result;
332 }
333
334 public static Set<String> getAllCgpmGfcmRectangleLabels(ResourceLoader resourceLoader, boolean failSafe) {
335 try {
336 return readLines(resourceLoader.getResource("classpath:referential/cgpm_rectangles.txt"));
337 } catch (SumarisTechnicalException e) {
338 if (failSafe) {
339
340 } else {
341 throw e;
342 }
343 }
344
345
346 Set<String> result = Sets.newHashSet();
347 StringBuilder sb = new StringBuilder();
348 sb.append("M");
349 for (int i = 0; i <= 34; i++) {
350 sb.setLength(1);
351 sb.append(i < 10 ? ("0" + i) : i);
352 for (char l = 'A'; l <= 'J'; l++) {
353 sb.setLength(3);
354 sb.append(l);
355 for (int j = 0; j <= 8; j++) {
356 sb.setLength(4);
357 sb.append(j);
358 result.add(sb.toString());
359 }
360 }
361 }
362
363 return result;
364 }
365
366
367
368
369
370
371
372
373 public static String getSquare10LabelByLatLong(Number latitude, Number longitude) {
374 return getMinuteSquareLabelByLatLong(latitude, longitude, 10);
375 }
376
377
378
379
380
381
382
383
384
385 public static String getMinuteSquareLabelByLatLong(Number latitude, Number longitude, int squareSize) {
386 if (longitude == null || latitude == null) {
387 return null;
388 }
389 double lat = latitude.doubleValue();
390 double lon = longitude.doubleValue();
391 StringBuilder result = new StringBuilder();
392
393
394 int quadrant;
395 if (lon <= 0 && lat >= 0) {
396 quadrant = 1;
397 }
398 else if (lon > 0 && lat > 0) {
399 quadrant = 2;
400 } else if (lon > 0 && lat < 0) {
401 quadrant = 3;
402 } else {
403 quadrant = 4;
404 }
405 result.append(quadrant);
406
407
408 lat = Math.abs(lat);
409 int intLatitude = (int)Math.floor(lat);
410 int decLatitude = (int)Math.floor((lat - intLatitude) * 60 / squareSize);
411 result.append(Strings.padStart(String.valueOf(intLatitude), 2, '0'));
412 if (squareSize >= 10) {
413
414 result.append(decLatitude);
415 }
416 else {
417
418 result.append(Strings.padStart(String.valueOf(decLatitude), 2, '0'));
419 }
420
421
422 lon = Math.abs(lon);
423 int intLongitude = (int)Math.floor(lon);
424 int decLongitude = (int)Math.floor((lon - intLongitude) * 60 / squareSize);
425 result.append(Strings.padStart(String.valueOf(intLongitude), 3, '0'));
426 if (squareSize >= 10) {
427
428 result.append(decLongitude);
429 }
430 else {
431
432 result.append(Strings.padStart(String.valueOf(decLongitude), 2, '0'));
433 }
434
435 return result.toString();
436 }
437
438 public static Set<String> getAllSquare10Labels(ResourceLoader resourceLoader, boolean failSafe) {
439 return ImmutableSet.<String>builder()
440 .addAll(getAllIcesRectangleLabels(resourceLoader, failSafe))
441 .addAll(getAllCgpmGfcmRectangleLabels(resourceLoader, failSafe))
442 .build().stream()
443 .flatMap(label -> Locations.convertRectangleToSquares10(label).stream())
444 .collect(Collectors.toSet());
445 }
446
447
448
449 public static Set<String> convertRectangleToMinuteSquares(final String rectangleLabel, final int minute) {
450 Preconditions.checkNotNull(rectangleLabel);
451 Preconditions.checkArgument(StringUtils.isNotBlank(rectangleLabel), "Argument 'rectangleLabel' must not be empty string.");
452
453 Geometry geom = getGeometryFromRectangleLabel(rectangleLabel, false);
454
455 if (geom == null) return null;
456
457 Coordinate startPoint = geom.getCoordinates()[0];
458 Coordinate endPoint = geom.getCoordinates()[2];
459
460 Set<String> result = Sets.newHashSet();
461
462 for (double longitude = startPoint.x; longitude < endPoint.x; longitude += minute/60.) {
463 for (double latitude = startPoint.y; latitude < endPoint.y; latitude += minute/60.) {
464 String label = getSquare10LabelByLatLong(latitude, longitude);
465 result.add(label);
466 }
467 }
468
469 return result;
470 }
471
472 private static Set<String> readLines(Resource resource) {
473 Preconditions.checkNotNull(resource);
474 Preconditions.checkArgument(resource.exists());
475 Set<String> result = Sets.newHashSet();
476 try {
477 InputStream is = resource.getInputStream();
478 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
479 String line = reader.readLine();
480 while (line != null) {
481 if (StringUtils.isNotBlank(line)) {
482 result.add(line.trim());
483 }
484 line = reader.readLine();
485 }
486 return result;
487 } catch (IOException e) {
488 throw new SumarisTechnicalException("Could not read resource: " + resource.getFilename(), e);
489 }
490 }
491
492
493 }