Skip to content

Commit

Permalink
feat: add location based logical types FPF-10479 (#32)
Browse files Browse the repository at this point in the history
* Add geometry, feature and feature-collection logical types

* Add Coordinates Reference System classes

* fix: geojson random generators

* chore: freeze sdk

---------

Co-authored-by: Carlos del Prado <[email protected]>
Co-authored-by: Carlos del Prado Mota <[email protected]>
  • Loading branch information
3 people authored Aug 30, 2023
1 parent 46591c4 commit f68665f
Show file tree
Hide file tree
Showing 17 changed files with 518 additions and 1 deletion.
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ repositories {
mavenCentral()
maven { url 'https://4p-public-artifacts.s3.amazonaws.com/baikal/releases/' }
maven { url 'https://4p-public-artifacts.s3.amazonaws.com/baikal/snapshots/' }
maven { url 'https://repo.osgeo.org/repository/release/' }
}

dependencies {
Expand All @@ -31,7 +32,7 @@ dependencies {
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.8'
implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'

implementation group: 'com.telefonica.baikal', name: 'spark-commons_2.12', version: '2.1.0'
implementation group: 'com.telefonica.baikal', name: 'spark-commons_2.12', version: '2.2.0'

// XXX: custom lib with performance improvements: https://github.com/Telefonica/libphonenumber-1
implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.12.25-4p'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import com.google.i18n.phonenumbers.Phonenumber;
import com.googlecode.ipv6.IPv6Address;
import com.telefonica.baikal.utils.Validations;
import io.confluent.avro.random.generator.geojson.Feature;
import io.confluent.avro.random.generator.geojson.FeatureCollection;
import io.confluent.avro.random.generator.geojson.Geometry;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.apache.commons.text.RandomStringGenerator;
import scala.collection.JavaConverters;
Expand Down Expand Up @@ -283,6 +286,19 @@ public String random(String logicalType, Map propertiesProp) {
case "time-zone":
List<String> timezones = JavaConverters.seqAsJavaList(Validations.availableTimeZones());
return timezones.get(random.nextInt(timezones.size()));
case "geometry":
return new Geometry(null, null).toString();
case "feature":
return new Feature(new Geometry(null, null)).toString();
case "feature-collection":
int numFeatures = 10;
ArrayList<Feature> features = new ArrayList<>(numFeatures);

for (int i = 0; i < numFeatures; i++) {
features.add(new Feature(new Geometry(null, null)));
}

return new FeatureCollection(features).toString();
default:
throw new IllegalArgumentException("Unsupported logical type: " + logicalType);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.confluent.avro.random.generator.geojson;

import io.confluent.avro.random.generator.geojson.crs.CRS;

import java.util.*;

/**
* Use this class in order to create Feature objects according to GeoJson standard that can be stringified.
*/
public class Feature {
public final Geometry geometry;
public final Properties properties;

/**
* Constructor.
*
* @param geometry Geometry object.
*/
public Feature(
Geometry geometry
) {
this.geometry = geometry;
this.properties = new Properties(geometry.crs);
}

/**
* Stringifies this object.
*
* @return String version of this object.
*/
public String toString() {
return "{\"type\": \"Feature\", \"geometry\": " + geometry + ", \"properties\": " + properties + "}";
}

private static class Properties extends HashMap<String, String> {
public Properties(CRS crs) {
super();
this.put("\"crs\"", "\"" + crs.toString() + "\"");
this.put("\"date_time\"", "\"" + new java.util.Date() + "\"");
}

@Override
public String toString() {
return super.toString().replaceAll("\"=\"", "\": \"");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.confluent.avro.random.generator.geojson;

import java.util.ArrayList;

/**
* Use this class in order to create FeatureCollection objects according to GeoJson standard that can be
* stringified.
*/
public class FeatureCollection {
public ArrayList<Feature> features;

/**
* Constructor.
* @param features Features list.
*/
public FeatureCollection(
ArrayList<Feature> features
) {
this.features = features;
}

/**
* Stringifies this object.
* @return String version of this object.
*/
public String toString() {
return "{\"type\": \"FeatureCollection\", \"features\":" + features + "}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.confluent.avro.random.generator.geojson;

import io.confluent.avro.random.generator.geojson.crs.CRS;
import io.confluent.avro.random.generator.geojson.crs.EPSG4326;

import java.util.Arrays;
import java.util.List;

/**
* Use this class in order to create Geometry objects according to GeoJson standard that can be stringified.
*/
public class Geometry {
public final GeometryType geometryType;
public final CRS crs;
public final String coordinates;

/**
* Constructor.
* @param geometryType Geometry type as can be found in GeometryType.java . Random type by default.
* @param crs Coordinates Reference System. EPSG4326 by default.
*/
public Geometry(
GeometryType geometryType,
CRS crs
) {
this.geometryType = geometryType == null ? GeometryType.randomGeometryType() : geometryType;
this.crs = crs == null ? new EPSG4326() : crs;
coordinates = randomCoordinates();
}

/**
* Stringifies this object.
* @return String version of this object.
*/
public String toString() {
return "{\"type\": \"" + geometryType + "\", \"coordinates\": " + coordinates + "}";
}

private String randomCoordinates() {
switch (geometryType) {
case Point:
return new Point(null, null, crs).toString();
case MultiPoint:
return Arrays.asList(
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString()
).toString();
case LineString:
return Arrays.asList(
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString()
).toString();
case MultiLineString:
return Arrays.asList(
Arrays.asList(
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString()
),
Arrays.asList(
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString()
)
).toString();
case Polygon:
String first = new Point(null, null, crs).toString();

return List.of(
Arrays.asList(
first,
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString(),
first
)
).toString();
case MultiPolygon:
String first1 = new Point(null, null, crs).toString();
String first2 = new Point(null, null, crs).toString();

return Arrays.asList(
List.of(
Arrays.asList(
first1,
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString(),
first1
)
),
List.of(
Arrays.asList(
first2,
new Point(null, null, crs).toString(),
new Point(null, null, crs).toString(),
first2
)
)
).toString();
default:
return null;
}
}

private static class Point {
private final double latitude;
private final double longitude;

public Point(
Double latitude,
Double longitude,
CRS referenceSystemAndProjection
) {
this.latitude = latitude == null ? referenceSystemAndProjection.randomLatitude() : latitude;
this.longitude = longitude == null ? referenceSystemAndProjection.randomLongitude() : longitude;
}

public String toString() {
return "[" + latitude + ", " + longitude + "]";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.confluent.avro.random.generator.geojson;

import java.util.Random;

enum GeometryType {
Point,
MultiPoint,
LineString,
MultiLineString,
Polygon,
MultiPolygon,
//GeometryCollection
;

public static GeometryType randomGeometryType() {
Random r = new Random();
return GeometryType.values()[r.nextInt(GeometryType.values().length)];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.confluent.avro.random.generator.geojson;

public enum Projection {
UTM
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.confluent.avro.random.generator.geojson;

public enum ReferenceSystem {
ETRS89,
WGS84
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.confluent.avro.random.generator.geojson.crs;

import java.util.Random;

public abstract class CRS {
protected double leftBound;
protected double rightBound;
protected double bottomBound;
protected double upperBound;

protected void setBounds(
double leftBound,
double rightBound,
double bottomBound,
double upperBound
) {
this.leftBound = leftBound;
this.rightBound = rightBound;
this.bottomBound = bottomBound;
this.upperBound = upperBound;
}

public double randomLatitude() {
Random r = new Random();
return bottomBound + (upperBound - bottomBound) * r.nextDouble();
}

public double randomLongitude() {
Random r = new Random();
return leftBound + (rightBound - leftBound) * r.nextDouble();
}

public abstract String toString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.confluent.avro.random.generator.geojson.crs;

public class EPSG4326 extends CRS {
public EPSG4326() {
super();
double leftBound = -180.0;
double rightBound = 180.0;
double bottomBound = 90.0;
double upperBound = -90.0;
this.setBounds(leftBound, rightBound, bottomBound, upperBound);
}

@Override
public String toString() {
return "EPSG4326";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

import com.telefonica.baikal.utils.Validations;
import org.apache.avro.generic.GenericRecord;
import org.geotools.data.geojson.GeoJSONReader;
import org.junit.Test;
import org.locationtech.jts.geom.Geometry;

import java.io.IOException;

import static io.confluent.avro.random.generator.util.ResourceUtil.builderWithSchema;
import static io.confluent.avro.random.generator.util.ResourceUtil.generateRecordWithSchema;
Expand Down Expand Up @@ -274,4 +278,34 @@ public void shouldCreateValidLongitude() {
assertTrue("Invalid latitude: " + value, Validations.isValidLongitude(value));
}

@Test
public void shouldCreateValidLGeometry() {
GenericRecord record = generateRecordWithSchema("test-schemas/logical-types/geometry.json");
String field = "geometry";
assertNotNull(record.get(field));
String value = record.get(field).toString();
System.out.println("Generated value is: " + value);
assertTrue("Invalid geometry: " + value, Validations.isValidGeometry(value));
}

@Test
public void shouldCreateValidLFeature() {
GenericRecord record = generateRecordWithSchema("test-schemas/logical-types/feature.json");
String field = "feature";
assertNotNull(record.get(field));
String value = record.get(field).toString();
System.out.println("Generated value is: " + value);
assertTrue("Invalid feature: " + value, Validations.isValidFeature(value));
}

@Test
public void shouldCreateValidLFeatureCollection() {
GenericRecord record = generateRecordWithSchema("test-schemas/logical-types/feature-collection.json");
String field = "featureCollection";
assertNotNull(record.get(field));
String value = record.get(field).toString();
System.out.println("Generated value is: " + value);
assertTrue("Invalid feature-collection: " + value, Validations.isValidFeatureCollection(value));
}

}
Loading

0 comments on commit f68665f

Please sign in to comment.