Skip to content

Commit

Permalink
created a base class for spatial data types and basic functions to qu…
Browse files Browse the repository at this point in the history
…ery it; first successor of it - Point; added tests to check validity of points and equality
  • Loading branch information
danylokravchenko committed Nov 1, 2023
1 parent fa05d81 commit 0695fdc
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 4 deletions.
3 changes: 2 additions & 1 deletion core/src/main/java/org/polypheny/db/type/PolyType.java
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ public enum PolyType {
GEOMETRY(
PrecScale.NO_NO,
true,
ExtraPolyTypes.GEOMETRY,
// TODO: or should it be Types.VARCHAR because of WKT or Types.JAVA_OBJECT
Types.JAVA_OBJECT, // ExtraPolyTypes.GEOMETRY,
PolyTypeFamily.GEO ),

FILE(
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/polypheny/db/type/PolyTypeFamily.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public enum PolyTypeFamily implements AlgDataTypeFamily {
// PolyType.MULTISET shares Types.ARRAY with PolyType.ARRAY;
// PolyType.MAP has no corresponding JDBC type
// PolyType.COLUMN_LIST has no corresponding JDBC type
// All spatial data types
.put( Types.BIT, NUMERIC )
.put( Types.TINYINT, NUMERIC )
.put( Types.SMALLINT, NUMERIC )
Expand Down Expand Up @@ -174,6 +175,7 @@ public Collection<PolyType> getTypeNames() {
case DATETIME_INTERVAL:
return PolyType.INTERVAL_TYPES;
case GEO:
// TODO: add all spatial types
return ImmutableList.of( PolyType.GEOMETRY );
case MULTISET:
return ImmutableList.of( PolyType.MULTISET );
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/java/org/polypheny/db/type/entity/PolyValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
import org.polypheny.db.type.entity.PolyBoolean.PolyBooleanSerializerDef;
import org.polypheny.db.type.entity.PolyDouble.PolyDoubleSerializerDef;
import org.polypheny.db.type.entity.PolyFloat.PolyFloatSerializerDef;
//import org.polypheny.db.type.entity.spatial.PolyGeometry.PolyGeometrySerializerDef;
import org.polypheny.db.type.entity.spatial.PolyGeometry;
import org.polypheny.db.type.entity.spatial.PolyGeometry.PolyGeometrySerializerDef;
import org.polypheny.db.type.entity.PolyInteger.PolyIntegerSerializerDef;
import org.polypheny.db.type.entity.PolyList.PolyListSerializerDef;
import org.polypheny.db.type.entity.PolyLong.PolyLongSerializerDef;
Expand Down Expand Up @@ -108,7 +111,9 @@
PolyBinary.class,
PolyNode.class,
PolyEdge.class,
PolyPath.class }) // add on Constructor already exists exception
PolyPath.class,
PolyGeometry.class
}) // add on Constructor already exists exception
@JsonTypeInfo(use = Id.CLASS) // to allow typed json serialization
@JsonSubTypes({
@JsonSubTypes.Type(value = PolyList.class, name = "LIST"),
Expand All @@ -131,7 +136,8 @@
@JsonSubTypes.Type(value = PolyEdge.class, name = "EDGE"),
@JsonSubTypes.Type(value = PolyPath.class, name = "PATH"),
@JsonSubTypes.Type(value = PolyDictionary.class, name = "DICTIONARY"),
@JsonSubTypes.Type(value = PolyUserDefinedValue.class, name = "UDV")
@JsonSubTypes.Type(value = PolyUserDefinedValue.class, name = "UDV"),
@JsonSubTypes.Type(value = PolyGeometry.class, name = "GEOMETRY")
})
public abstract class PolyValue implements Expressible, Comparable<PolyValue>, PolySerializable {

Expand All @@ -153,6 +159,7 @@ public abstract class PolyValue implements Expressible, Comparable<PolyValue>, P
.with( PolyBoolean.class, ctx -> new PolyBooleanSerializerDef() )
.with( PolyGraph.class, ctx -> new PolyGraphSerializerDef() )
.with( PolyLong.class, ctx -> new PolyLongSerializerDef() )
.with( PolyGeometry.class, ctx -> new PolyGeometrySerializerDef() )
.build( PolyValue.class );


Expand Down Expand Up @@ -432,7 +439,7 @@ public static Class<? extends PolyValue> classFrom( PolyType polyType ) {
case DYNAMIC_STAR:
return PolyValue.class;
case GEOMETRY:
return PolyValue.class;
return PolyGeometry.class;
case FILE:
case IMAGE:
case VIDEO:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2019-2023 The Polypheny Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polypheny.db.type.entity.spatial;

public class InvalidGeometryException extends Exception {

public InvalidGeometryException( String message ) {
super( message );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* Copyright 2019-2023 The Polypheny Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.polypheny.db.type.entity.spatial;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.activej.serializer.BinaryInput;
import io.activej.serializer.BinaryOutput;
import io.activej.serializer.BinarySerializer;
import io.activej.serializer.CompatibilityLevel;
import io.activej.serializer.CorruptedDataException;
import io.activej.serializer.SimpleSerializerDef;
import io.activej.serializer.annotations.Deserialize;
import java.util.Objects;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.calcite.linq4j.tree.Expression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.polypheny.db.type.PolySerializable;
import org.polypheny.db.type.PolyType;
import org.polypheny.db.type.entity.PolyValue;

/**
* {@link PolyGeometry} is an abstraction for all spatial data types.
* <p>
* {@link PolyGeometry} wraps the {@link Geometry} of the JTS library
* and the functionality of {@link PolyGeometry} is provided my means of JTS.
*/
@Slf4j
public class PolyGeometry extends PolyValue {

// default plane
private static final int NO_SRID = 0;
// World Geodetic System 1984
private static final int WGS_84 = 4326;

/**
* Wrap the JTS {@link Geometry} class.
* <p>
* It is possible to extract useful information from the {@link Geometry}:
* <code>jtsGeometry.getGeometryType()</code>
* <code>jtsGeometry.getSRID()</code>
* <code>jtsGeometry.getCoordinate()</code>
*/
protected Geometry jtsGeometry;
// Spatial Reference System ID
@Getter
protected Integer SRID;

/**
* Constructor creates the {@link Geometry} from the WKT text.
* <p>
* The WKT may contain SRID in the text, e.g. <code>"SRID=4326;POINT (13.4050 52.5200)"</code>.
* In this case SRID from the WKT would be used, otherwise the default one is selected.
* <p>
* @param wkt Well Know Text representation of the geometry
* @throws InvalidGeometryException if {@link Geometry} is invalid or provided WKT is invalid.
*/
public PolyGeometry( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throws InvalidGeometryException {
this( PolyType.GEOMETRY );
this.SRID = NO_SRID;
WKTReader reader = new WKTReader();
try {
// WKT is actually an extended EWKT with SRID before the WKT
if (wkt.startsWith( "SRID" )) {
// in WKT semicolon is invalid character, so we could safely split by it
String[] ewktParts = wkt.split( ";" );
this.SRID = Integer.valueOf( ewktParts[0].replace( "SRID=", "" ) );
wkt = ewktParts[1];
}
this.jtsGeometry = reader.read( wkt );
if (!jtsGeometry.isValid()) {
throw new ParseException("Provided geometry is not valid.");
}
this.jtsGeometry.setSRID( this.SRID );
} catch ( ParseException | NumberFormatException e) {
throw new InvalidGeometryException(e.getMessage());
}
}

public PolyGeometry( Geometry geometry ) {
super( PolyType.GEOMETRY );
this.jtsGeometry = geometry;
this.SRID = geometry.getSRID();
}

protected PolyGeometry( PolyType type ) {
super( type );
}

public static PolyGeometry of( String wkt ) throws InvalidGeometryException {
return new PolyGeometry( wkt );
}

public static PolyGeometry of( Geometry geometry ) {
return new PolyGeometry( geometry );
}

public String getGeometryType() {
return jtsGeometry.getGeometryType();
}

public PolyPoint asPoint() {
if (jtsGeometry.getGeometryType().equals( "Point" )) {
return PolyPoint.of( jtsGeometry );
}
return null;
}


/**
* Tests whether this {@link Geometry} is simple.
* @return <code>true</code> if {@link Geometry} is simple.
*/
public boolean isSimple() {
return jtsGeometry.isSimple();
}


/**
* Bound the {@link Geometry} my the minimum box that could fit this geometry.
* @return {@link PolyGeometry} with minimum bounding box
*/
public PolyGeometry getMinimumBoundingBox() {
return PolyGeometry.of( jtsGeometry.getEnvelope() );
}


/**
* {@link #equals(Object) equals} ensures that the {@link Geometry} types and coordinates are the same.
* And {@link #SRID} used for representation of coordinates are also identical.
*/
@Override
public boolean equals( Object o ) {
if ( !( o instanceof PolyGeometry )) {
return false;
}
PolyGeometry that = (PolyGeometry) o;
return jtsGeometry.equals( that.jtsGeometry ) && Objects.equals( SRID, that.SRID );
}


@Override
public int hashCode() {
return Objects.hash( super.hashCode(), jtsGeometry.hashCode(), SRID );
}


@Override
public PolySerializable copy() {
return PolySerializable.deserialize( serialize(), PolyGeometry.class );
}


@Override
public Expression asExpression() {
// TODO: dont know what is it
return null;
}


@Override
public int compareTo( @NotNull PolyValue o ) {
if ( !isSameType( o ) ) {
return -1;
}
return jtsGeometry.compareTo( o );
}


@Override
public @Nullable Long deriveByteSize() {
String wkt = toString();
return (long) (wkt == null ? 1 : wkt.getBytes().length);
}


/**
* Output the {@link Geometry} in a WKT format with its SRID. So-called EWKT
*/
@Override
public String toString() {
return String.format( "SRID=%d;%s" , SRID, jtsGeometry.toString() );
}

public static class PolyGeometrySerializerDef extends SimpleSerializerDef<PolyGeometry> {

@Override
protected BinarySerializer<PolyGeometry> createSerializer( int version, CompatibilityLevel compatibilityLevel ) {
return new BinarySerializer<>() {
@Override
public void encode( BinaryOutput out, PolyGeometry item ) {
out.writeUTF8( item.toString() );
}

@Override
public PolyGeometry decode( BinaryInput in ) throws CorruptedDataException {
try {
return PolyGeometry.of( PolySerializable.deserialize( in.readUTF8(), serializer ).asString().value );
} catch ( InvalidGeometryException e ) {
throw new CorruptedDataException( e.getMessage() );
}
}
};
}

}

}
Loading

0 comments on commit 0695fdc

Please sign in to comment.