Skip to content

Commit

Permalink
Merge pull request #182 from pmia712/closest-shoreline-distance
Browse files Browse the repository at this point in the history
Closest shoreline distance
  • Loading branch information
xspanger3770 committed Dec 19, 2023
2 parents 4482675 + deeb803 commit b05a9d5
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 18 deletions.
211 changes: 196 additions & 15 deletions GlobalQuakeCore/src/main/java/globalquake/core/regions/Regions.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import globalquake.utils.GeoUtils;
import globalquake.utils.LookupTableIO;
import org.geojson.*;
import org.json.JSONObject;
import org.tinylog.Logger;
Expand All @@ -14,7 +15,11 @@
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class Regions {
Expand Down Expand Up @@ -51,6 +56,8 @@ public class Regions {


private static final ArrayList<Region> regionSearchHD = new ArrayList<>();
private static HashMap<String, Double> shorelineLookup;


public static void init() throws IOException {
parseGeoJson("polygons/countriesMD.json", raw_polygonsMD, regionsMD, NONE);
Expand All @@ -64,11 +71,34 @@ public static void init() throws IOException {
parseGeoJson("polygons_converted/new-zealand-districts.geojson", raw_polygonsNZ, regionsNZ, NONE);
parseGeoJson("polygons_converted/hawaii-countries.geojson", raw_polygonsHW, regionsHW, NONE);
parseGeoJson("polygons_converted/italy_provinces.geojson", raw_polygonsIT, regionsIT, NONE);
parseGeoJson("polygons_converted/italy_provinces.geojson", raw_polygonsIT, regionsIT, NONE);
parseGeoJson("polygons_converted/region_dataset.geojson", null, regionSearchHD, NONE);

for(ArrayList<Region> list : List.of(regionsUS, regionsAK, regionsJP, regionsNZ, regionsHW, regionsIT)){
regionSearchHD.addAll(list);
}

shorelineLookup = LookupTableIO.importLookupTableFromFile();

if(shorelineLookup == null){
System.err.println("No lookup table found! Generating...");
double start = System.currentTimeMillis();
boolean exportResult = LookupTableIO.exportLookupTableToFile();
System.out.println("Generating took: " + (System.currentTimeMillis() - start)/1000 + "s");

if (exportResult) {
System.out.println("Lookup table successfully generated! Loading " + shorelineLookup.size() + " items.");
shorelineLookup = LookupTableIO.importLookupTableFromFile();
} else {
System.err.println("Failed to export lookup table!");
}
}

}


@SuppressWarnings("EmptyMethod")
public static synchronized void awaitDownload() {
}

@Deprecated
public static synchronized String downloadRegion(double lat, double lon) {
if (!enabled) {
Expand Down Expand Up @@ -159,12 +189,17 @@ public static String getName(double lat, double lon, List<Region> regions){

public static String getExtendedName(double lat, double lon){
String localName = getName(lat, lon, regionSearchHD);
String globalName = getName(lat, lon, regionsUHD);

if(localName != null && globalName != null) {
return "%s, %s".formatted(localName, globalName);
}

if(localName != null){
return localName;
}

return getName(lat, lon, regionsUHD);
return globalName;
}

public static String getRegion(double lat, double lon) {
Expand Down Expand Up @@ -210,16 +245,166 @@ public static String getRegion(double lat, double lon) {
return name;
}

public static double getShorelineDistance(double lat, double lon) {
String extendedName = getExtendedName(lat, lon);
if(extendedName != null){
return 0;
}

double closestDistance = Double.MAX_VALUE;
for (Region reg : regionsMD) {
for (Polygon polygon : reg.raws()) {
for (LngLatAlt pos : polygon.getCoordinates().get(0)) {
double dist = GeoUtils.greatCircleDistance(pos.getLatitude(), pos.getLongitude(), lat, lon);
if (dist < closestDistance) {
closestDistance = dist;
}
}
}
}


return closestDistance;
}

public static HashMap<String, Double> generateLookupTable(double minLat, double maxLat, double minLon, double maxLon) {
final double STEP_LAT = 0.5;
final double STEP_LON = 0.5;
HashMap<String, Double> lookupTable = new HashMap<>();

for (double lat = minLat; lat < maxLat; lat += STEP_LAT) {
for (double lon = minLon; lon < maxLon; lon += STEP_LON) {
double distance = getShorelineDistance(lat, lon);

if (distance != 0) {
lookupTable.put(String.format("%f,%f", lat, lon), distance);
}
}
}

return lookupTable;
}

public static List<HashMap<String, Double>> generateLookupTablesInParallel() {
final double MIN_LAT = -90;
final double MAX_LAT = 90;
final double MIN_LON = -180;
final double MAX_LON = 180;

ExecutorService executorService = Executors.newFixedThreadPool(4);
List<HashMap<String, Double>> allLookupTables = new ArrayList<>();

for (double latStart = MIN_LAT; latStart < MAX_LAT; latStart += (MAX_LAT - MIN_LAT) / 2) {
for (double lonStart = MIN_LON; lonStart < MAX_LON; lonStart += (MAX_LON - MIN_LON) / 2) {
double latEnd = latStart + (MAX_LAT - MIN_LAT) / 2;
double lonEnd = lonStart + (MAX_LON - MIN_LON) / 2;

double finalLatStart = latStart;
double finalLonStart = lonStart;
executorService.submit(() -> {
HashMap<String, Double> lookupTable = generateLookupTable(finalLatStart, latEnd, finalLonStart, lonEnd);
synchronized (allLookupTables) {
allLookupTables.add(lookupTable);
}
});
}
}

executorService.shutdown();
try {
boolean ignored = executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}

return allLookupTables;
}

public static boolean isValidPoint(double x, double y) {
return x >= -90 && x <= 90 && y >= -180 && y <= 180;
}

public static double interpolate(
double lat, double lon,
HashMap<String, Double> lookupTable
) {
if(lookupTable.containsKey(String.format("%.6f,%.6f", lat, lon))){
return lookupTable.get(String.format("%.6f,%.6f", lat, lon));
}

double tmp = lat - Math.floor(lat);
double x0, x1, y0, y1;

if(tmp < 0.5) {
x0 = (int) Math.floor(lat);
x1 = x0 + 0.5;
} else {
x0 = (int) Math.floor(lat) + 0.5;
x1 = x0 + 0.5;
}

tmp = lon - Math.floor(lon);

if(tmp < 0.5) {
y0 = (int) Math.floor(lon);
y1 = y0 + 0.5;
} else {
y0 = (int) Math.floor(lon) + 0.5;
y1 = y0 + 0.5;
}

if (!isValidPoint(x0, y0) ||
!isValidPoint(x1, y1)) {
return -1;
}

String first = String.format("%.6f,%.6f", x0, y0);
String second = String.format("%.6f,%.6f", x0, y1);
String third = String.format("%.6f,%.6f", x1, y0);
String fourth = String.format("%.6f,%.6f", x1, y1);

double f00 = lookupTable.getOrDefault(first, (double) 0);
double f01 = lookupTable.getOrDefault(second, (double) 0);
double f10 = lookupTable.getOrDefault(third, (double) 0);
double f11 = lookupTable.getOrDefault(fourth, (double) 0);

double r1 = ((x1 - lat)/(x1 - x0) * f00) + ((lat - x0)/(x1 - x0) * f10);
double r2 = ((x1 - lat)/(x1 - x0) * f01) + ((lat - x0)/(x1 - x0) * f11);

double result = ((y1 - lon)/(y1 - y0) * r1) + ((lon - y0)/(y1 - y0) * r2);

return Double.isNaN(result) ? 0 : result;
}


public static void main(String[] args) throws Exception{
System.out.println("INIT");
init();

System.out.println("FIND");
long a = System.currentTimeMillis();
for(int i = 0; i < 500; i++){
getRegion(58.79,-150.80);

double lat = 39.59763558387561,
lon = -9.14040362258988;

assert shorelineLookup != null;
double interpolation = interpolate(lat, lon, shorelineLookup);

if (Double.isNaN(interpolation) || interpolation == -1){
System.err.println("Values couldn't be interpolated, using legacy method...");
double shorelineDistance = getShorelineDistance(lat, lon);
shorelineLookup.putIfAbsent(String.format("%.6f,%.6f", lat, lon), shorelineDistance);
} else {
System.out.println("Interpolated distance to the closest shoreline is: " + interpolation);
shorelineLookup.putIfAbsent(String.format("%.6f,%.6f", lat, lon), interpolation);
}

boolean exportResult = LookupTableIO.exportLookupTableToFile(shorelineLookup);
if(exportResult){
System.out.println("Lookup Table was successfully exported.");
} else {
System.err.println("Lookup Table export failed");
}
System.out.println(getRegion(33.78,135.74));
System.err.println(System.currentTimeMillis()-a);
}

public static void parseGeoJson(String path, ArrayList<Polygon> raw, ArrayList<Region> regions, List<String> remove) throws IOException {
Expand Down Expand Up @@ -249,25 +434,21 @@ public static void parseGeoJson(String path, ArrayList<Polygon> raw, ArrayList<R
raws.add(pol);
paths.add(toPath(pol));

if(raw != null) {
raw.add(pol);
}
raw.add(pol);
regions.add(new Region(name, paths, paths.stream().map(Path2D.Double::getBounds2D).collect(Collectors.toList()), raws));
} else if (o instanceof MultiPolygon mp) {
createRegion(regions, mp, name);

List<List<List<LngLatAlt>>> polygons = mp.getCoordinates();
for (List<List<LngLatAlt>> polygon : polygons) {
org.geojson.Polygon pol = new org.geojson.Polygon(polygon.get(0));
if(raw != null) {
raw.add(pol);
}
raw.add(pol);
}
}
}
}

private static final String[] NAME_NAMES = {"name_long", "name", "NAME_2", "NAME_1", "NAME", "name_l"};
private static final String[] NAME_NAMES = {"name_long", "name", "NAME_2", "NAME_1", "NAME"};

private static String fetchName(Feature f) {
String name;
Expand Down
60 changes: 60 additions & 0 deletions GlobalQuakeCore/src/main/java/globalquake/utils/LookupTableIO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package globalquake.utils;

import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.List;

import globalquake.core.regions.Regions;

public class LookupTableIO {
public static boolean exportLookupTableToFile() {
List<HashMap<String, Double>> lookupTables = Regions.generateLookupTablesInParallel();
HashMap<String, Double> lookupTable = new HashMap<>();
for(HashMap<String, Double> singleLT : lookupTables) {
lookupTable.putAll(singleLT);
}

return performExport(lookupTable);
}

public static boolean exportLookupTableToFile(HashMap<String, Double> lookupTable) throws IOException {
return performExport(lookupTable);
}

private static boolean performExport(HashMap<String, Double> lookupTable) {
String path = "lookup/lookupTable.dat";
URL resource = ClassLoader.getSystemClassLoader().getResource(path);

try{
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("lookupTable.dat"));
output.writeObject(lookupTable);
output.close();
} catch (Exception e){
System.err.println("Unable to save a lookup table! " + e);
return false;
}

return true;
}

public static HashMap<String, Double> importLookupTableFromFile() throws IOException {
String path = "lookup/lookupTable.dat";
URL resource = ClassLoader.getSystemClassLoader().getResource(path);

if (resource == null) {
System.err.printf("Unable to load a lookup table: %s", path);
return null;
}

HashMap<String, Double> lookupTable;
try{
ObjectInput input = new ObjectInputStream(resource.openStream());
lookupTable = (HashMap<String, Double>) input.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new IOException("Unable to load stream of a lookup table! ", e);
}

return lookupTable;
}
}
Binary file not shown.
Loading

0 comments on commit b05a9d5

Please sign in to comment.