Skip to content

Commit c5fb343

Browse files
authored
Merge pull request #1 from stolstov/ogc_fixes
Ogc fixes
2 parents 873d073 + b0b407e commit c5fb343

9 files changed

Lines changed: 544 additions & 112 deletions

File tree

src/main/java/com/esri/core/geometry/ogc/OGCConcreteGeometryCollection.java

Lines changed: 111 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.esri.core.geometry.ogc;
2626

2727
import com.esri.core.geometry.Envelope;
28+
import com.esri.core.geometry.GeoJsonExportFlags;
2829
import com.esri.core.geometry.Geometry;
2930
import com.esri.core.geometry.GeometryCursor;
3031
import com.esri.core.geometry.GeometryException;
@@ -35,15 +36,15 @@
3536
import com.esri.core.geometry.OGCStructureInternal;
3637
import com.esri.core.geometry.OperatorConvexHull;
3738
import com.esri.core.geometry.OperatorDifference;
39+
import com.esri.core.geometry.OperatorExportToGeoJson;
40+
import com.esri.core.geometry.OperatorIntersection;
41+
import com.esri.core.geometry.OperatorUnion;
42+
import com.esri.core.geometry.Point;
3843
import com.esri.core.geometry.Polygon;
3944
import com.esri.core.geometry.Polyline;
4045
import com.esri.core.geometry.SimpleGeometryCursor;
4146
import com.esri.core.geometry.SpatialReference;
4247
import com.esri.core.geometry.VertexDescription;
43-
import com.esri.core.geometry.GeoJsonExportFlags;
44-
import com.esri.core.geometry.OperatorExportToGeoJson;
45-
import com.esri.core.geometry.OperatorUnion;
46-
import com.esri.core.geometry.Point;
4748

4849
import java.nio.ByteBuffer;
4950
import java.nio.ByteOrder;
@@ -546,9 +547,11 @@ public double distance(OGCGeometry another) {
546547

547548
double minD = Double.NaN;
548549
for (int i = 0, n = numGeometries(); i < n; ++i) {
550+
// TODO Skip expensive distance computation if bounding boxes are further away than minD
549551
double d = geometryN(i).distance(another);
550552
if (d < minD || Double.isNaN(minD)) {
551553
minD = d;
554+
// TODO Replace zero with tolerance defined by the spatial reference
552555
if (minD == 0) {
553556
break;
554557
}
@@ -560,6 +563,29 @@ public double distance(OGCGeometry another) {
560563

561564
//
562565
//Relational operations
566+
@Override
567+
public boolean overlaps(OGCGeometry another) {
568+
//TODO
569+
throw new UnsupportedOperationException();
570+
}
571+
572+
@Override
573+
public boolean touches(OGCGeometry another) {
574+
//TODO
575+
throw new UnsupportedOperationException();
576+
}
577+
578+
@Override
579+
public boolean crosses(OGCGeometry another) {
580+
//TODO
581+
throw new UnsupportedOperationException();
582+
}
583+
584+
@Override
585+
public boolean relate(OGCGeometry another, String matrix) {
586+
throw new UnsupportedOperationException();
587+
}
588+
563589
@Override
564590
public boolean disjoint(OGCGeometry another) {
565591
if (isEmpty() || another.isEmpty())
@@ -623,106 +649,110 @@ public boolean Equals(OGCGeometry another) {
623649

624650
OGCConcreteGeometryCollection gc1 = (OGCConcreteGeometryCollection)g1;
625651
OGCConcreteGeometryCollection gc2 = (OGCConcreteGeometryCollection)g2;
652+
// TODO Assuming input geometries are simple and valid, remove-overlaps would be a no-op.
653+
// Hence, calling flatten() should be sufficient.
626654
gc1 = gc1.flattenAndRemoveOverlaps();
627655
gc2 = gc2.flattenAndRemoveOverlaps();
628656
int n = gc1.numGeometries();
629657
if (n != gc2.numGeometries()) {
630658
return false;
631659
}
632660

633-
boolean res = false;
634661
for (int i = 0; i < n; ++i) {
635662
if (!gc1.geometryN(i).Equals(gc2.geometryN(i))) {
636663
return false;
637664
}
638-
639-
res = true;
640665
}
641666

642-
return res;
667+
return n > 0;
643668
}
644-
669+
670+
private static OGCConcreteGeometryCollection toGeometryCollection(OGCGeometry geometry)
671+
{
672+
if (geometry.geometryType() != OGCConcreteGeometryCollection.TYPE) {
673+
return new OGCConcreteGeometryCollection(geometry, geometry.getEsriSpatialReference());
674+
}
675+
676+
return (OGCConcreteGeometryCollection) geometry;
677+
}
678+
679+
private static List<Geometry> toList(GeometryCursor cursor)
680+
{
681+
List<Geometry> geometries = new ArrayList<Geometry>();
682+
for (Geometry geometry = cursor.next(); geometry != null; geometry = cursor.next()) {
683+
geometries.add(geometry);
684+
}
685+
return geometries;
686+
}
687+
645688
//Topological
646689
@Override
647690
public OGCGeometry difference(OGCGeometry another) {
648-
List<OGCConcreteGeometryCollection> list = wrapGeomsIntoList_(this, another);
649-
list = prepare_for_ops_(list);
650-
if (list.size() != 2) // this should not happen
651-
throw new GeometryException("internal error");
652-
691+
if (isEmpty() || another.isEmpty()) {
692+
return this;
693+
}
694+
695+
List<Geometry> geometries = toList(prepare_for_ops_(toGeometryCollection(this)));
696+
List<Geometry> otherGeometries = toList(prepare_for_ops_(toGeometryCollection(another)));
697+
653698
List<OGCGeometry> result = new ArrayList<OGCGeometry>();
654-
OGCConcreteGeometryCollection coll1 = list.get(0);
655-
OGCConcreteGeometryCollection coll2 = list.get(1);
656-
for (int i = 0, n = coll1.numGeometries(); i < n; ++i) {
657-
OGCGeometry cur = coll1.geometryN(i);
658-
for (int j = 0, n2 = coll2.numGeometries(); j < n2; ++j) {
659-
OGCGeometry geom2 = coll2.geometryN(j);
660-
if (cur.dimension() > geom2.dimension())
699+
for (Geometry geometry : geometries) {
700+
for (Geometry otherGeometry : otherGeometries) {
701+
if (geometry.getDimension() > otherGeometry.getDimension()) {
661702
continue; //subtracting lower dimension has no effect.
662-
663-
cur = cur.difference(geom2);
664-
if (cur.isEmpty())
703+
}
704+
705+
geometry = OperatorDifference.local().execute(geometry, otherGeometry, esriSR, null);
706+
if (geometry.isEmpty()) {
665707
break;
708+
}
709+
}
710+
711+
if (!geometry.isEmpty()) {
712+
result.add(OGCGeometry.createFromEsriGeometry(geometry, esriSR));
666713
}
667-
668-
if (cur.isEmpty())
669-
continue;
670-
671-
result.add(cur);
672714
}
673-
715+
674716
if (result.size() == 1) {
675-
result.get(0).reduceFromMulti();
717+
return result.get(0).reduceFromMulti();
676718
}
677719

678-
return new OGCConcreteGeometryCollection(result, esriSR);
720+
return new OGCConcreteGeometryCollection(result, esriSR).flattenAndRemoveOverlaps();
679721
}
680722

681723
@Override
682724
public OGCGeometry intersection(OGCGeometry another) {
683-
List<OGCConcreteGeometryCollection> list = wrapGeomsIntoList_(this, another);
684-
list = prepare_for_ops_(list);
685-
if (list.size() != 2) // this should not happen
686-
throw new GeometryException("internal error");
687-
725+
if (isEmpty() || another.isEmpty()) {
726+
return new OGCConcreteGeometryCollection(esriSR);
727+
}
728+
729+
List<Geometry> geometries = toList(prepare_for_ops_(toGeometryCollection(this)));
730+
List<Geometry> otherGeometries = toList(prepare_for_ops_(toGeometryCollection(another)));
731+
688732
List<OGCGeometry> result = new ArrayList<OGCGeometry>();
689-
OGCConcreteGeometryCollection coll1 = list.get(0);
690-
OGCConcreteGeometryCollection coll2 = list.get(1);
691-
for (int i = 0, n = coll1.numGeometries(); i < n; ++i) {
692-
OGCGeometry cur = coll1.geometryN(i);
693-
for (int j = 0, n2 = coll2.numGeometries(); j < n2; ++j) {
694-
OGCGeometry geom2 = coll2.geometryN(j);
695-
696-
OGCGeometry partialIntersection = cur.intersection(geom2);
697-
if (partialIntersection.isEmpty())
698-
continue;
699-
700-
result.add(partialIntersection);
733+
for (Geometry geometry : geometries) {
734+
for (Geometry otherGeometry : otherGeometries) {
735+
GeometryCursor intersectionCursor = OperatorIntersection.local().execute(new SimpleGeometryCursor(geometry), new SimpleGeometryCursor(otherGeometry), esriSR, null, 7);
736+
OGCGeometry intersection = OGCGeometry.createFromEsriCursor(intersectionCursor, esriSR, true);
737+
if (!intersection.isEmpty()) {
738+
result.add(intersection);
739+
}
701740
}
702741
}
703742

704743
if (result.size() == 1) {
705-
result.get(0).reduceFromMulti();
744+
return result.get(0).reduceFromMulti();
706745
}
707746

708-
return (new OGCConcreteGeometryCollection(result, esriSR)).flattenAndRemoveOverlaps();
747+
return new OGCConcreteGeometryCollection(result, esriSR).flattenAndRemoveOverlaps();
709748
}
710-
711-
//make a list of collections out of two geometries
712-
private static List<OGCConcreteGeometryCollection> wrapGeomsIntoList_(OGCGeometry g1, OGCGeometry g2) {
713-
List<OGCConcreteGeometryCollection> list = new ArrayList<OGCConcreteGeometryCollection>();
714-
if (g1.geometryType() != OGCConcreteGeometryCollection.TYPE) {
715-
g1 = new OGCConcreteGeometryCollection(g1, g1.getEsriSpatialReference());
716-
}
717-
if (g2.geometryType() != OGCConcreteGeometryCollection.TYPE) {
718-
g2 = new OGCConcreteGeometryCollection(g2, g2.getEsriSpatialReference());
719-
}
720-
721-
list.add((OGCConcreteGeometryCollection)g1);
722-
list.add((OGCConcreteGeometryCollection)g2);
723-
724-
return list;
749+
750+
@Override
751+
public OGCGeometry symDifference(OGCGeometry another) {
752+
//TODO
753+
throw new UnsupportedOperationException();
725754
}
755+
726756
/**
727757
* Checks if collection is flattened.
728758
* @return True for the flattened collection. A flattened collection contains up to three non-empty geometries:
@@ -834,24 +864,23 @@ public OGCConcreteGeometryCollection flatten() {
834864
/**
835865
* Fixes topological overlaps in the GeometryCollecion.
836866
* This is equivalent to union of the geometry collection elements.
837-
*
867+
*
868+
* TODO "flattened" collection is supposed to contain only mutli-geometries, but this method may return single geometries
869+
* e.g. for GEOMETRYCOLLECTION (LINESTRING (...)) it returns GEOMETRYCOLLECTION (LINESTRING (...))
870+
* and not GEOMETRYCOLLECTION (MULTILINESTRING (...))
838871
* @return A geometry collection that is flattened and has no overlapping elements.
839872
*/
840873
public OGCConcreteGeometryCollection flattenAndRemoveOverlaps() {
841-
ArrayList<Geometry> geoms = new ArrayList<Geometry>();
842-
874+
843875
//flatten and crack/cluster
844876
GeometryCursor cursor = OGCStructureInternal.prepare_for_ops_(flatten().getEsriGeometryCursor(), esriSR);
845-
for (Geometry g = cursor.next(); g != null; g = cursor.next()) {
846-
geoms.add(g);
847-
}
848-
877+
849878
//make sure geometries don't overlap
850-
return removeOverlapsHelper_(geoms);
879+
return new OGCConcreteGeometryCollection(removeOverlapsHelper_(toList(cursor)), esriSR);
851880
}
852-
853-
private OGCConcreteGeometryCollection removeOverlapsHelper_(List<Geometry> geoms) {
854-
ArrayList<Geometry> result = new ArrayList<Geometry>();
881+
882+
private GeometryCursor removeOverlapsHelper_(List<Geometry> geoms) {
883+
List<Geometry> result = new ArrayList<Geometry>();
855884
for (int i = 0; i < geoms.size(); ++i) {
856885
Geometry current = geoms.get(i);
857886
if (current.isEmpty())
@@ -870,7 +899,7 @@ private OGCConcreteGeometryCollection removeOverlapsHelper_(List<Geometry> geoms
870899
result.add(current);
871900
}
872901

873-
return new OGCConcreteGeometryCollection(new SimpleGeometryCursor(result), esriSR);
902+
return new SimpleGeometryCursor(result);
874903
}
875904

876905
private static class FlatteningCollectionCursor extends GeometryCursor {
@@ -921,36 +950,9 @@ public int getGeometryID() {
921950
//Collectively processes group of geometry collections (intersects all segments and clusters points).
922951
//Flattens collections, removes overlaps.
923952
//Once done, the result collections would work well for topological and relational operations.
924-
private List<OGCConcreteGeometryCollection> prepare_for_ops_(List<OGCConcreteGeometryCollection> geoms) {
925-
assert(geoms != null && !geoms.isEmpty());
926-
GeometryCursor prepared = OGCStructureInternal.prepare_for_ops_(new FlatteningCollectionCursor(geoms), esriSR);
927-
928-
List<OGCConcreteGeometryCollection> result = new ArrayList<OGCConcreteGeometryCollection>();
929-
int prevCollectionIndex = -1;
930-
List<Geometry> list = null;
931-
for (Geometry g = prepared.next(); g != null; g = prepared.next()) {
932-
int c = prepared.getGeometryID();
933-
if (c != prevCollectionIndex) {
934-
//add empty collections for all skipped indices
935-
for (int i = prevCollectionIndex; i < c - 1; i++) {
936-
result.add(new OGCConcreteGeometryCollection(esriSR));
937-
}
938-
939-
if (list != null) {
940-
result.add(removeOverlapsHelper_(list));
941-
}
942-
943-
list = new ArrayList<Geometry>();
944-
prevCollectionIndex = c;
945-
}
946-
947-
list.add(g);
948-
}
949-
950-
if (list != null) {
951-
result.add(removeOverlapsHelper_(list));
952-
}
953-
954-
return result;
953+
private GeometryCursor prepare_for_ops_(OGCConcreteGeometryCollection collection) {
954+
assert(collection != null && !collection.isEmpty());
955+
GeometryCursor prepared = OGCStructureInternal.prepare_for_ops_(collection.flatten().getEsriGeometryCursor(), esriSR);
956+
return removeOverlapsHelper_(toList(prepared));
955957
}
956958
}

0 commit comments

Comments
 (0)