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 }
@@ -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