Skip to content

Commit

Permalink
Merge branch 'master' of github.com:basonjui/geometryapi
Browse files Browse the repository at this point in the history
  • Loading branch information
basonjui committed Aug 22, 2022
2 parents 1c4c5c7 + a55b46e commit eed29fb
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 41 deletions.
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>-Xmx1024m</argLine>
</configuration>
</plugin>

<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.lang.Math;

import com.masterisehomes.geometryapi.hexagon.Coordinates;

public class Harversine {
/*
* Earth's radius at the equator (WGS-84)
Expand All @@ -15,20 +17,41 @@ public class Harversine {
*/
public static double distance(double lat1, double lng1, double lat2, double lng2) {
// Convert latitudes to Radians
double phi_1 = Math.toRadians(lat1);
double phi_2 = Math.toRadians(lat2);
final double phi_1 = Math.toRadians(lat1);
final double phi_2 = Math.toRadians(lat2);

// Distance between latitudes and longitudes
double delta_phi = Math.toRadians(lat2 - lat1);
double delta_lambda = Math.toRadians(lng2 - lng1);
final double delta_phi = Math.toRadians(lat2 - lat1);
final double delta_lambda = Math.toRadians(lng2 - lng1);

// Apply Harversine formula
double a = Math.pow(Math.sin(delta_phi / 2), 2)
final double a = Math.pow(Math.sin(delta_phi / 2), 2)
+ Math.pow(Math.sin(delta_lambda / 2), 2) * Math.cos(phi_1) * Math.cos(phi_2);
final double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
final double distance = R * c; // meters

return distance;
}

public static double distance(Coordinates coordinates1, Coordinates coordinates2) {
final double lat1 = coordinates1.getLatitude();
final double lng1 = coordinates1.getLongitude();
final double lat2 = coordinates2.getLatitude();
final double lng2 = coordinates2.getLongitude();

double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
// Convert latitudes to Radians
final double phi_1 = Math.toRadians(lat1);
final double phi_2 = Math.toRadians(lat2);

// Distance between latitudes and longitudes
final double delta_phi = Math.toRadians(lat2 - lat1);
final double delta_lambda = Math.toRadians(lng2 - lng1);

double distance = R * c; // meters
// Apply Harversine formula
final double a = Math.pow(Math.sin(delta_phi / 2), 2)
+ Math.pow(Math.sin(delta_lambda / 2), 2) * Math.cos(phi_1) * Math.cos(phi_2);
final double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
final double distance = R * c; // meters

return distance;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.masterisehomes.geometryapi.tessellation;

import java.lang.Math;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.List;
import java.util.ArrayList;

Expand Down Expand Up @@ -87,6 +89,8 @@ public class AxialClockwiseTessellation {
/* Basic stats here */
@Getter
private int totalHexagons = 0;
@Getter
private double tessellationDistance = 0;

/*
* Corner Hexagons - used to find Edge Hexagons (based on nthRing)
Expand Down Expand Up @@ -491,7 +495,7 @@ private final List<Hexagon> generateGisEdgeHexagons(Hexagon gisCornerHexagon, Ne
/* Calculate RequiredRings */
private final int calculateRequiredRings(Boundary boundary) {
/*
* ARBITRARY RING ADJUSTMENT CONSTANT
* ERROR MARGINS - TODO: needs to update descriptions
*
* Geometrically, at the outer most ring, 1/6 the area of each hexagon can be
* missed.
Expand All @@ -504,17 +508,29 @@ private final int calculateRequiredRings(Boundary boundary) {
final int CENTROID_PLACEMENT_ERROR_MARGIN = 1;
final int RING_ERROR_MARGIN = GRID_GEOMETRIC_ERROR_MARGIN + CENTROID_PLACEMENT_ERROR_MARGIN;

/* Coordinates */
final double minLat = boundary.getMinLat();
final double minLng = boundary.getMinLng();
final double maxLat = boundary.getMaxLat();
final double maxLng = boundary.getMaxLng();

/*
* Calculate the Great-circle Distance between the START and END boundary
* coordinates
* Calculate the requiredTessellationDistance from boundary by Harversine formula
*
* is the minimum distance required to generate Grid Map that can cover boundary
* fully - this distance can grow up to maxBoundaryDistance, depending where the
* rootHexagon is placed within Boundary.
*/
final double maxBoundaryDistance = Harversine.distance(minLat, minLng, maxLat, maxLng);
final Coordinates minCoordinates = boundary.getMinCoordinates();
final Coordinates maxCoordinates = boundary.getMaxCoordinates();
final Coordinates centroidCoordinates = rootHexagon.getCentroid();

// Calculate distance between Boundary's minCoordinates/maxCoordinates to rootCentroid
final double minCoordinatesDistance = Harversine.distance(minCoordinates, centroidCoordinates);
final double maxCoordinatesDistance = Harversine.distance(maxCoordinates, centroidCoordinates);

// Determine requiredTessellationDistance: the larger distance == the requiredTessellationDistance
final double requiredTessellationDistance;
if (minCoordinatesDistance >= maxCoordinatesDistance) {
requiredTessellationDistance = minCoordinatesDistance;
} else {
requiredTessellationDistance = maxCoordinatesDistance;
}
this.tessellationDistance = requiredTessellationDistance;

/*
* Neighbor's distance - distance between each hexagon's neighbor centroid:
Expand All @@ -531,10 +547,10 @@ private final int calculateRequiredRings(Boundary boundary) {
/*
* In Hexagons grids, we can look at it with 3 primary axes (the 6 neighbor
* directions):
* - minAxialHexagons is the minimum amount of hexagons that required to stack
* up (from edges) in those 3 axes to cover the grid map largest diameter.
* - requiredAxialHexagons is the minimum amount of hexagons that required to stack
* up (from edges) in those 3 axes to cover the boundary largest diameter.
*/
final int requiredAxialHexagons = (int) Math.ceil(maxBoundaryDistance / neighborDistance); // round up
final int requiredAxialHexagons = (int) Math.ceil(requiredTessellationDistance / neighborDistance); // round up

/*
* Calculate the Minimum Required Rings
Expand Down Expand Up @@ -598,33 +614,57 @@ private final void clearHexagons() {
public static void main(String[] args) {
Gson gson = new GsonBuilder().create();

Coordinates origin = new Coordinates(106.7064, 10.7744);
Coordinates origin = new Coordinates(105.8121224, 21.0358791);
// Coordinates origin = new Coordinates(109.466667, 23.383333);

Hexagon hexagon = new Hexagon(origin, 200);
Hexagon hexagon = new Hexagon(origin, 500);
Neighbors neighbors = new Neighbors(hexagon);

AxialClockwiseTessellation tessellation = new AxialClockwiseTessellation(hexagon);

Boundary boundary = new Boundary(
new Coordinates(106.6959, 10.7826),
new Coordinates(106.7064, 10.7743));
new Coordinates(105, 21),
new Coordinates(109.466667, 23.383333));

// Test harversine
double greatCircleDistance = Harversine.distance(boundary.getMinLat(), boundary.getMinLng(),
boundary.getMaxLat(), boundary.getMaxLng());

tessellation.tessellate(boundary);
tessellation.tessellate(boundary);

System.out.println("Great-circle distance: " + greatCircleDistance);
System.out.println("Total rings: " + tessellation.totalRings);
System.out.println("Minimum required rings: " + tessellation.requiredRings);
System.out.println("Current nthRing: " + tessellation.nthRing);
System.out.println("Total hexagons: " + tessellation.totalHexagons);
System.out.println("Boundary: " + tessellation.boundary + "\n");
// Set rounding format
DecimalFormat df = new DecimalFormat("#.##");
df.setRoundingMode(RoundingMode.CEILING);

final double oldCoverageDistance = 531312.330953638;
final int oldRequiredRings = 616;
final int oldTotalHexagons = 1136521;

final String newCoverageDistanceDiffPct = df.format(
(tessellation.tessellationDistance / oldCoverageDistance - 1) * 100);
final String newRingsDiffPct = df.format(
(tessellation.requiredRings / (double) oldRequiredRings - 1) * 100);
final String newTotalHexagonsDiffPct = df.format(
(tessellation.totalHexagons / (double) oldTotalHexagons - 1) * 100);

System.out.println("\n------------ Tessellation optimization stats ------------");
System.out.println("Boundary: " + tessellation.boundary.gisBoundary());
System.out.println("Hexagon's circumradius: " + hexagon.getCircumradius());

System.out.println("\nOld coverage Distance: " + oldCoverageDistance);
System.out.println("New Coverage Distance: " + tessellation.tessellationDistance);
System.out.println("*Difference percentage: " + newCoverageDistanceDiffPct + "%");

System.out.println("\nOld minimum required Rings: " + oldRequiredRings);
System.out.println("New minimum required Rings: " + tessellation.requiredRings);
System.out.println("*Difference percentage: " + newRingsDiffPct + "%");

System.out.println("\nOld total Hexagons: " + oldTotalHexagons);
System.out.println("New total hexagons: " + tessellation.totalHexagons);
System.out.println("*Difference percentage: " + newTotalHexagonsDiffPct + "%");

GeoJsonManager tessellationManager = new GeoJsonManager(tessellation);
System.out.println(
gson.toJson(tessellationManager.getFeatureCollection()));
// GeoJsonManager tessellationManager = new GeoJsonManager(tessellation);
// System.out.println(
// gson.toJson(tessellationManager.getFeatureCollection()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import lombok.Getter;
import com.google.gson.JsonObject;

import com.google.gson.Gson;
import com.masterisehomes.geometryapi.neighbors.Neighbors;
import com.masterisehomes.geometryapi.geodesy.Harversine;
import com.masterisehomes.geometryapi.geojson.GeoJsonManager;

import com.masterisehomes.geometryapi.hexagon.Coordinates;
import com.masterisehomes.geometryapi.hexagon.Hexagon;

Expand Down Expand Up @@ -93,4 +98,35 @@ public AxialClockwiseTessellationDto(JsonObject payload) {
this.totalRings = this.tessellation.getTotalRings();
this.totalHexagons = this.tessellation.getTotalHexagons();
}


public static void main(String[] args) {
Gson gson = new Gson();

Coordinates origin = new Coordinates(107, 23);

Hexagon hexagon = new Hexagon(origin, 250);
Neighbors neighbors = new Neighbors(hexagon);

AxialClockwiseTessellation tessellation = new AxialClockwiseTessellation(hexagon);

Boundary boundary = new Boundary(
new Coordinates(106, 20),
new Coordinates(109.466667, 23.383333));

// Test harversine
double greatCircleDistance = Harversine.distance(boundary.getMinLat(), boundary.getMinLng(),
boundary.getMaxLat(), boundary.getMaxLng());

tessellation.tessellate(boundary);

AxialClockwiseTessellationDto dto = new AxialClockwiseTessellationDto(tessellation);

System.out.println("Great-circle distance: " + greatCircleDistance);
System.out.println("Total rings: " + dto.getTessellation().getTotalRings());
System.out.println("Total hexagons: " + dto.getTessellation().getTotalHexagons());
System.out.println("Boundary: " + dto.getTessellation().getBoundary() + "\n");

GeoJsonManager tessellationManager = new GeoJsonManager(tessellation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.Getter;
import lombok.ToString;

import com.masterisehomes.geometryapi.geodesy.Harversine;
import com.masterisehomes.geometryapi.hexagon.Coordinates;

/* Similar to setup() in Processing
Expand All @@ -15,33 +16,53 @@
*/

@ToString
@Getter
public class Boundary {
@Getter
private final Coordinates minCoordinates, maxCoordinates;

// Processing attributes
private int width, height;
private Coordinates start, end;

// WGS84 Coordinates attributes
private double minLat, minLng;
private double maxLat, maxLng;
@Getter
private final double minLat, minLng;
@Getter
private final double maxLat, maxLng;

// Builder pattern to take in dimension ,
public Boundary(float x, float y, int width, int height) {
this.start = new Coordinates(x, y);
this.minCoordinates = new Coordinates(x, y);
this.width = width;
this.height = height;
this.end = new Coordinates(x + width, y + height);
this.maxCoordinates = new Coordinates(x + width, y + height);

this.minLat = minCoordinates.getLatitude();
this.minLng = minCoordinates.getLongitude();
this.maxLat = maxCoordinates.getLatitude();
this.maxLng = maxCoordinates.getLongitude();
}

// WGS84 Coordinates Boundary
public Boundary(Coordinates minCoordinates, Coordinates maxCoordinates) {
this.minCoordinates = minCoordinates;
this.maxCoordinates = maxCoordinates;

this.minLat = minCoordinates.getLatitude();
this.minLng = minCoordinates.getLongitude();
this.maxLat = maxCoordinates.getLatitude();
this.maxLng = maxCoordinates.getLongitude();
}

// Comparison methods
/* Calculate Great-circle distance */
public double greatCircleDistance() {
double greatCircleDistance = Harversine.distance(
minLat, minLng,
maxLat, maxLng);

return greatCircleDistance;
}

/* Comparison methods */
public boolean contains(Coordinates centroid) {
double centroidLat = centroid.getLatitude();
double centroidLng = centroid.getLongitude();
Expand Down Expand Up @@ -69,4 +90,9 @@ private boolean containsLng(double lng) {
return false;
}
}

/* Getters */
public String gisBoundary() {
return String.format("GisBoundary(minLat=%s, minLng=%s, maxLat=%s, maxLng=%s)", minLat, minLng, maxLat, maxLng);
}
}

0 comments on commit eed29fb

Please sign in to comment.