2525package com .esri .core .geometry .ogc ;
2626
2727import com .esri .core .geometry .Envelope ;
28+ import com .esri .core .geometry .GeoJsonExportFlags ;
2829import com .esri .core .geometry .Geometry ;
2930import com .esri .core .geometry .GeometryCursor ;
3031import com .esri .core .geometry .GeometryException ;
3536import com .esri .core .geometry .OGCStructureInternal ;
3637import com .esri .core .geometry .OperatorConvexHull ;
3738import 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 ;
3843import com .esri .core .geometry .Polygon ;
3944import com .esri .core .geometry .Polyline ;
4045import com .esri .core .geometry .SimpleGeometryCursor ;
4146import com .esri .core .geometry .SpatialReference ;
4247import 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
4849import java .nio .ByteBuffer ;
4950import 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 }
@@ -646,93 +649,102 @@ public boolean Equals(OGCGeometry another) {
646649
647650 OGCConcreteGeometryCollection gc1 = (OGCConcreteGeometryCollection )g1 ;
648651 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.
649654 gc1 = gc1 .flattenAndRemoveOverlaps ();
650655 gc2 = gc2 .flattenAndRemoveOverlaps ();
651656 int n = gc1 .numGeometries ();
652657 if (n != gc2 .numGeometries ()) {
653658 return false ;
654659 }
655660
656- boolean res = false ;
657661 for (int i = 0 ; i < n ; ++i ) {
658662 if (!gc1 .geometryN (i ).Equals (gc2 .geometryN (i ))) {
659663 return false ;
660664 }
661-
662- res = true ;
663665 }
664666
665- return res ;
667+ return n > 0 ;
666668 }
667-
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+
668688 //Topological
669689 @ Override
670690 public OGCGeometry difference (OGCGeometry another ) {
671- List <OGCConcreteGeometryCollection > list = wrapGeomsIntoList_ (this , another );
672- list = prepare_for_ops_ (list );
673- if (list .size () != 2 ) // this should not happen
674- throw new GeometryException ("internal error" );
675-
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+
676698 List <OGCGeometry > result = new ArrayList <OGCGeometry >();
677- OGCConcreteGeometryCollection coll1 = list .get (0 );
678- OGCConcreteGeometryCollection coll2 = list .get (1 );
679- for (int i = 0 , n = coll1 .numGeometries (); i < n ; ++i ) {
680- OGCGeometry cur = coll1 .geometryN (i );
681- for (int j = 0 , n2 = coll2 .numGeometries (); j < n2 ; ++j ) {
682- OGCGeometry geom2 = coll2 .geometryN (j );
683- if (cur .dimension () > geom2 .dimension ())
699+ for (Geometry geometry : geometries ) {
700+ for (Geometry otherGeometry : otherGeometries ) {
701+ if (geometry .getDimension () > otherGeometry .getDimension ()) {
684702 continue ; //subtracting lower dimension has no effect.
685-
686- cur = cur .difference (geom2 );
687- if (cur .isEmpty ())
703+ }
704+
705+ geometry = OperatorDifference .local ().execute (geometry , otherGeometry , esriSR , null );
706+ if (geometry .isEmpty ()) {
688707 break ;
708+ }
709+ }
710+
711+ if (!geometry .isEmpty ()) {
712+ result .add (OGCGeometry .createFromEsriGeometry (geometry , esriSR ));
689713 }
690-
691- if (cur .isEmpty ())
692- continue ;
693-
694- result .add (cur );
695714 }
696-
715+
697716 if (result .size () == 1 ) {
698717 return result .get (0 ).reduceFromMulti ();
699718 }
700719
701- return new OGCConcreteGeometryCollection (result , esriSR );
720+ return new OGCConcreteGeometryCollection (result , esriSR ). flattenAndRemoveOverlaps () ;
702721 }
703722
704723 @ Override
705724 public OGCGeometry intersection (OGCGeometry another ) {
706725 if (isEmpty () || another .isEmpty ()) {
707726 return new OGCConcreteGeometryCollection (esriSR );
708727 }
709-
710- List <OGCConcreteGeometryCollection > list = wrapGeomsIntoList_ (this , another );
711- list = prepare_for_ops_ (list );
712- if (list .size () != 2 ) // this should not happen
713- throw new GeometryException ("internal error" );
714-
728+
729+ List <Geometry > geometries = toList (prepare_for_ops_ (toGeometryCollection (this )));
730+ List <Geometry > otherGeometries = toList (prepare_for_ops_ (toGeometryCollection (another )));
731+
715732 List <OGCGeometry > result = new ArrayList <OGCGeometry >();
716- OGCConcreteGeometryCollection coll1 = list .get (0 );
717- OGCConcreteGeometryCollection coll2 = list .get (1 );
718- for (int i = 0 , n = coll1 .numGeometries (); i < n ; ++i ) {
719- OGCGeometry cur = coll1 .geometryN (i );
720- for (int j = 0 , n2 = coll2 .numGeometries (); j < n2 ; ++j ) {
721- OGCGeometry geom2 = coll2 .geometryN (j );
722-
723- OGCGeometry partialIntersection = cur .intersection (geom2 );
724- if (partialIntersection .isEmpty ())
725- continue ;
726-
727- 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+ }
728740 }
729741 }
730742
731743 if (result .size () == 1 ) {
732744 return result .get (0 ).reduceFromMulti ();
733745 }
734746
735- return ( new OGCConcreteGeometryCollection (result , esriSR ) ).flattenAndRemoveOverlaps ();
747+ return new OGCConcreteGeometryCollection (result , esriSR ).flattenAndRemoveOverlaps ();
736748 }
737749
738750 @ Override
@@ -741,21 +753,6 @@ public OGCGeometry symDifference(OGCGeometry another) {
741753 throw new UnsupportedOperationException ();
742754 }
743755
744- //make a list of collections out of two geometries
745- private static List <OGCConcreteGeometryCollection > wrapGeomsIntoList_ (OGCGeometry g1 , OGCGeometry g2 ) {
746- List <OGCConcreteGeometryCollection > list = new ArrayList <OGCConcreteGeometryCollection >();
747- if (g1 .geometryType () != OGCConcreteGeometryCollection .TYPE ) {
748- g1 = new OGCConcreteGeometryCollection (g1 , g1 .getEsriSpatialReference ());
749- }
750- if (g2 .geometryType () != OGCConcreteGeometryCollection .TYPE ) {
751- g2 = new OGCConcreteGeometryCollection (g2 , g2 .getEsriSpatialReference ());
752- }
753-
754- list .add ((OGCConcreteGeometryCollection )g1 );
755- list .add ((OGCConcreteGeometryCollection )g2 );
756-
757- return list ;
758- }
759756 /**
760757 * Checks if collection is flattened.
761758 * @return True for the flattened collection. A flattened collection contains up to three non-empty geometries:
@@ -867,24 +864,23 @@ public OGCConcreteGeometryCollection flatten() {
867864 /**
868865 * Fixes topological overlaps in the GeometryCollecion.
869866 * This is equivalent to union of the geometry collection elements.
870- *
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 (...))
871871 * @return A geometry collection that is flattened and has no overlapping elements.
872872 */
873873 public OGCConcreteGeometryCollection flattenAndRemoveOverlaps () {
874- ArrayList <Geometry > geoms = new ArrayList <Geometry >();
875-
874+
876875 //flatten and crack/cluster
877876 GeometryCursor cursor = OGCStructureInternal .prepare_for_ops_ (flatten ().getEsriGeometryCursor (), esriSR );
878- for (Geometry g = cursor .next (); g != null ; g = cursor .next ()) {
879- geoms .add (g );
880- }
881-
877+
882878 //make sure geometries don't overlap
883- return removeOverlapsHelper_ (geoms );
879+ return new OGCConcreteGeometryCollection ( removeOverlapsHelper_ (toList ( cursor )), esriSR );
884880 }
885-
886- private OGCConcreteGeometryCollection removeOverlapsHelper_ (List <Geometry > geoms ) {
887- ArrayList <Geometry > result = new ArrayList <Geometry >();
881+
882+ private GeometryCursor removeOverlapsHelper_ (List <Geometry > geoms ) {
883+ List <Geometry > result = new ArrayList <Geometry >();
888884 for (int i = 0 ; i < geoms .size (); ++i ) {
889885 Geometry current = geoms .get (i );
890886 if (current .isEmpty ())
@@ -903,7 +899,7 @@ private OGCConcreteGeometryCollection removeOverlapsHelper_(List<Geometry> geoms
903899 result .add (current );
904900 }
905901
906- return new OGCConcreteGeometryCollection ( new SimpleGeometryCursor (result ), esriSR );
902+ return new SimpleGeometryCursor (result );
907903 }
908904
909905 private static class FlatteningCollectionCursor extends GeometryCursor {
@@ -954,36 +950,9 @@ public int getGeometryID() {
954950 //Collectively processes group of geometry collections (intersects all segments and clusters points).
955951 //Flattens collections, removes overlaps.
956952 //Once done, the result collections would work well for topological and relational operations.
957- private List <OGCConcreteGeometryCollection > prepare_for_ops_ (List <OGCConcreteGeometryCollection > geoms ) {
958- assert (geoms != null && !geoms .isEmpty ());
959- GeometryCursor prepared = OGCStructureInternal .prepare_for_ops_ (new FlatteningCollectionCursor (geoms ), esriSR );
960-
961- List <OGCConcreteGeometryCollection > result = new ArrayList <OGCConcreteGeometryCollection >();
962- int prevCollectionIndex = -1 ;
963- List <Geometry > list = null ;
964- for (Geometry g = prepared .next (); g != null ; g = prepared .next ()) {
965- int c = prepared .getGeometryID ();
966- if (c != prevCollectionIndex ) {
967- //add empty collections for all skipped indices
968- for (int i = prevCollectionIndex ; i < c - 1 ; i ++) {
969- result .add (new OGCConcreteGeometryCollection (esriSR ));
970- }
971-
972- if (list != null ) {
973- result .add (removeOverlapsHelper_ (list ));
974- }
975-
976- list = new ArrayList <Geometry >();
977- prevCollectionIndex = c ;
978- }
979-
980- list .add (g );
981- }
982-
983- if (list != null ) {
984- result .add (removeOverlapsHelper_ (list ));
985- }
986-
987- 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 ));
988957 }
989958}
0 commit comments