diff --git a/.gitignore b/.gitignore index 85898c5a..f31512c4 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ Icon? ehthumbs.db Thumbs.db target/* +/bin/ diff --git a/src/main/java/com/esri/core/geometry/AttributeStreamOfDbl.java b/src/main/java/com/esri/core/geometry/AttributeStreamOfDbl.java index 18ae0fc1..bbf40fde 100644 --- a/src/main/java/com/esri/core/geometry/AttributeStreamOfDbl.java +++ b/src/main/java/com/esri/core/geometry/AttributeStreamOfDbl.java @@ -89,7 +89,7 @@ public AttributeStreamOfDbl(AttributeStreamOfDbl other, int maxSize) { /** * Reads a value from the buffer at given offset. - * + * * @param offset * is the element number in the stream. */ @@ -103,7 +103,7 @@ public double get(int offset) { /** * Overwrites given element with new value. - * + * * @param offset * is the element number in the stream. * @param value @@ -125,7 +125,7 @@ public void set(int offset, double value) { /** * Reads a value from the buffer at given offset. - * + * * @param offset * is the element number in the stream. */ @@ -136,7 +136,7 @@ public void read(int offset, Point2D outPoint) { /** * Overwrites given element with new value. - * + * * @param offset * is the element number in the stream. * @param value @@ -213,7 +213,7 @@ public void resize(int newSize) { if (newSize <= m_size) { if ((newSize * 5) / 4 < m_buffer.length) {// decrease when the 25% - // margin is exceeded + // margin is exceeded double[] newBuffer = new double[newSize]; System.arraycopy(m_buffer, 0, newBuffer, 0, newSize); m_buffer = newBuffer; @@ -251,7 +251,7 @@ public void resize(int newSize, double defaultValue) { "invalid call. Attribute Stream is locked and cannot be resized."); if (newSize <= m_size) { if ((newSize * 5) / 4 < m_buffer.length) {// decrease when the 25% - // margin is exceeded + // margin is exceeded double[] newBuffer = new double[newSize]; System.arraycopy(m_buffer, 0, newBuffer, 0, newSize); m_buffer = newBuffer; @@ -579,8 +579,8 @@ public void eraseRange(int index, int count, int validSize) { throw new GeometryException("invalid_call"); if (validSize - (index + count) > 0) { - System.arraycopy(m_buffer, index + count, m_buffer, index, validSize - - (index + count)); + System.arraycopy(m_buffer, index + count, m_buffer, index, + validSize - (index + count)); } m_size -= count; } @@ -656,8 +656,8 @@ public void writeRange(int startElement, int count, throw new IllegalArgumentException(); AttributeStreamOfDbl src = (AttributeStreamOfDbl) _src; // the input - // type must - // match + // type must + // match if (src.size() < (int) (srcStart + count)) throw new IllegalArgumentException(); diff --git a/src/main/java/com/esri/core/geometry/Bufferer.java b/src/main/java/com/esri/core/geometry/Bufferer.java index ebe961ce..b7008559 100644 --- a/src/main/java/com/esri/core/geometry/Bufferer.java +++ b/src/main/java/com/esri/core/geometry/Bufferer.java @@ -24,13 +24,32 @@ package com.esri.core.geometry; import java.util.ArrayList; +import java.util.List; class Bufferer { + Bufferer() { + m_buffer_commands = new ArrayList(128); + m_progress_tracker = null; + m_tolerance = 0; + m_small_tolerance = 0; + m_filter_tolerance = 0; + m_distance = 0; + m_original_geom_type = Geometry.GeometryType.Unknown; + m_abs_distance_reversed = 0; + m_abs_distance = 0; + m_densify_dist = -1; + m_dA = -1; + m_b_output_loops = true; + m_bfilter = true; + m_old_circle_template_size = 0; + } + + /** * Result is always a polygon. For non positive distance and non-areas * returns an empty polygon. For points returns circles. */ - static Geometry buffer(Geometry geometry, double distance, + Geometry buffer(Geometry geometry, double distance, SpatialReference sr, double densify_dist, int max_vertex_in_complete_circle, ProgressTracker progress_tracker) { if (geometry == null) @@ -47,34 +66,36 @@ static Geometry buffer(Geometry geometry, double distance, if (distance > 0) env2D.inflate(distance, distance); - Bufferer bufferer = new Bufferer(progress_tracker); - bufferer.m_spatialReference = sr; - bufferer.m_geometry = geometry; - bufferer.m_tolerance = InternalUtils.calculateToleranceFromGeometry(sr, + m_progress_tracker = progress_tracker; + + m_original_geom_type = geometry.getType().value(); + m_geometry = geometry; + m_tolerance = InternalUtils.calculateToleranceFromGeometry(sr, env2D, true);// conservative to have same effect as simplify - bufferer.m_small_tolerance = InternalUtils + m_small_tolerance = InternalUtils .calculateToleranceFromGeometry(null, env2D, true);// conservative // to have // same // effect as // simplify - bufferer.m_distance = distance; - bufferer.m_original_geom_type = geometry.getType().value(); + if (max_vertex_in_complete_circle <= 0) { max_vertex_in_complete_circle = 96;// 96 is the value used by SG. // This is the number of // vertices in the full circle. } - - bufferer.m_abs_distance = Math.abs(bufferer.m_distance); - bufferer.m_abs_distance_reversed = bufferer.m_abs_distance != 0 ? 1.0 / bufferer.m_abs_distance + + m_spatialReference = sr; + m_distance = distance; + m_abs_distance = Math.abs(m_distance); + m_abs_distance_reversed = m_abs_distance != 0 ? 1.0 / m_abs_distance : 0; if (NumberUtils.isNaN(densify_dist) || densify_dist == 0) { - densify_dist = bufferer.m_abs_distance * 1e-5; + densify_dist = m_abs_distance * 1e-5; } else { - if (densify_dist > bufferer.m_abs_distance * 0.5) - densify_dist = bufferer.m_abs_distance * 0.5;// do not allow too + if (densify_dist > m_abs_distance * 0.5) + densify_dist = m_abs_distance * 0.5;// do not allow too // large densify // distance (the // value will be @@ -85,6 +106,7 @@ static Geometry buffer(Geometry geometry, double distance, if (max_vertex_in_complete_circle < 12) max_vertex_in_complete_circle = 12; + double max_dd = Math.abs(distance) * (1 - Math.cos(Math.PI / max_vertex_in_complete_circle)); @@ -105,13 +127,26 @@ static Geometry buffer(Geometry geometry, double distance, } } - bufferer.m_densify_dist = densify_dist; - bufferer.m_max_vertex_in_complete_circle = max_vertex_in_complete_circle; + m_densify_dist = densify_dist; + m_max_vertex_in_complete_circle = max_vertex_in_complete_circle; // when filtering close points we do not want the filter to distort // generated buffer too much. - bufferer.m_filter_tolerance = Math.min(bufferer.m_small_tolerance, + m_filter_tolerance = Math.min(m_small_tolerance, densify_dist * 0.25); - return bufferer.buffer_(); + + + m_circle_template_size = calcN_(); + if (m_circle_template_size != m_old_circle_template_size) { + // we have an optimization for this method to be called several + // times. Here we detected too many changes and need to regenerate + // the data. + m_circle_template.clear(); + m_old_circle_template_size = m_circle_template_size; + } + + Geometry result_geom = buffer_(); + m_geometry = null; + return result_geom; } private Geometry m_geometry; @@ -120,8 +155,6 @@ private static final class BufferCommand { private interface Flags { static final int enum_line = 1; static final int enum_arc = 2; - static final int enum_dummy = 4; - static final int enum_concave_dip = 8; static final int enum_connection = enum_arc | enum_line; } @@ -175,22 +208,22 @@ private BufferCommand(Point2D from, Point2D to, int next, int prev, private double m_dA; private boolean m_b_output_loops; private boolean m_bfilter; - private ArrayList m_circle_template; + private ArrayList m_circle_template = new ArrayList(0); private ArrayList m_left_stack; private ArrayList m_middle_stack; private Line m_helper_line_1; private Line m_helper_line_2; private Point2D[] m_helper_array; private int m_progress_counter; + private int m_circle_template_size; + private int m_old_circle_template_size; private void generateCircleTemplate_() { - if (m_circle_template == null) { - m_circle_template = new ArrayList(0); - } else if (!m_circle_template.isEmpty()) { + if (!m_circle_template.isEmpty()) { return; } - int N = calcN_(4); + int N = m_circle_template_size; assert (N >= 4); int real_size = (N + 3) / 4; @@ -218,6 +251,7 @@ private void generateCircleTemplate_() { private static final class GeometryCursorForMultiPoint extends GeometryCursor { + private Bufferer m_parent; private int m_index; private Geometry m_buffered_polygon; private MultiPoint m_mp; @@ -229,10 +263,11 @@ private static final class GeometryCursorForMultiPoint extends private int m_max_vertex_in_complete_circle; private ProgressTracker m_progress_tracker; - GeometryCursorForMultiPoint(MultiPoint mp, double distance, + GeometryCursorForMultiPoint(Bufferer parent, MultiPoint mp, double distance, SpatialReference sr, double densify_dist, int max_vertex_in_complete_circle, ProgressTracker progress_tracker) { + m_parent = parent; m_index = 0; m_mp = mp; m_x = 0; @@ -263,7 +298,7 @@ public Geometry next() { m_x = point.getX(); m_y = point.getY(); - m_buffered_polygon = Bufferer.buffer(point, m_distance, + m_buffered_polygon = m_parent.buffer(point, m_distance, m_spatialReference, m_densify_dist, m_max_vertex_in_complete_circle, m_progress_tracker); b_first = true; @@ -295,64 +330,113 @@ public int getGeometryID() { } } - private static final class GeometryCursorForPolyline extends GeometryCursor { - private Bufferer m_bufferer; - private int m_index; - private boolean m_bfilter; + private static final class GlueingCursorForPolyline extends GeometryCursor { + private Polyline m_polyline; + private int m_current_path_index; - GeometryCursorForPolyline(Bufferer bufferer, boolean bfilter) { - m_bufferer = bufferer; - m_index = 0; - m_bfilter = bfilter; + GlueingCursorForPolyline(Polyline polyline) { + m_polyline = polyline; + m_current_path_index = 0; } @Override public Geometry next() { - MultiPathImpl mp = (MultiPathImpl) (m_bufferer.m_geometry - ._getImpl()); - if (m_index < mp.getPathCount()) { - int ind = m_index; - m_index++; + if (m_polyline == null) + return null; + + MultiPathImpl mp = (MultiPathImpl) m_polyline._getImpl(); + int npaths = mp.getPathCount(); + if (m_current_path_index < npaths) { + int ind = m_current_path_index; + m_current_path_index++; if (!mp.isClosedPathInXYPlane(ind)) { + // connect paths that follow one another as an optimization + // for buffering (helps when one polyline is split into many + // segments). Point2D prev_end = mp.getXY(mp.getPathEnd(ind) - 1); - while (m_index < mp.getPathCount()) { - Point2D start = mp.getXY(mp.getPathStart(m_index)); - if (mp.isClosedPathInXYPlane(m_index)) + while (m_current_path_index < mp.getPathCount()) { + Point2D start = mp.getXY(mp + .getPathStart(m_current_path_index)); + if (mp.isClosedPathInXYPlane(m_current_path_index)) break; if (start != prev_end) break; - prev_end = mp.getXY(mp.getPathEnd(m_index) - 1); - m_index++; + prev_end = mp + .getXY(mp.getPathEnd(m_current_path_index) - 1); + m_current_path_index++; } } - if (m_index - ind == 1) - return m_bufferer.bufferPolylinePath_( - (Polyline) (m_bufferer.m_geometry), ind, m_bfilter); - else { - Polyline tmp_polyline = new Polyline( - m_bufferer.m_geometry.getDescription()); - tmp_polyline.addPath((Polyline) (m_bufferer.m_geometry), - ind, true); - for (int i = ind + 1; i < m_index; i++) { - ((MultiPathImpl) tmp_polyline._getImpl()) - .addSegmentsFromPath( - (MultiPathImpl) m_bufferer.m_geometry - ._getImpl(), i, 0, mp - .getSegmentCount(i), false); - } - // Operator_factory_local::SaveJSONToTextFileDbg("c:/temp/buffer_ppp.txt", - // tmp_polyline, nullptr); - Polygon res = m_bufferer.bufferPolylinePath_(tmp_polyline, - 0, m_bfilter); - // Operator_factory_local::SaveJSONToTextFileDbg("c:/temp/buffer_ppp_res.txt", - // *res, nullptr); - return res; + if (ind == 0 + && m_current_path_index == m_polyline.getPathCount()) { + Polyline pol = m_polyline; + m_polyline = null; + return pol; } + + Polyline tmp_polyline = new Polyline( + m_polyline.getDescription()); + tmp_polyline.addPath(m_polyline, ind, true); + for (int i = ind + 1; i < m_current_path_index; i++) { + tmp_polyline.addSegmentsFromPath(m_polyline, i, 0, + mp.getSegmentCount(i), false); + } + + if (false) { + OperatorFactoryLocal.saveGeometryToEsriShapeDbg( + "c:/temp/_geom.bin", tmp_polyline); + } + + if (m_current_path_index == m_polyline.getPathCount()) + m_polyline = null; + + return tmp_polyline; + } else { + return null; } + } - return null; + @Override + public int getGeometryID() { + return 0; + } + } + + private static final class GeometryCursorForPolyline extends GeometryCursor { + private Bufferer m_bufferer; + GeometryCursor m_geoms; + Geometry m_geometry; + private int m_index; + private boolean m_bfilter; + + GeometryCursorForPolyline(Bufferer bufferer, GeometryCursor geoms, + boolean bfilter) { + m_bufferer = bufferer; + m_geoms = geoms; + m_index = 0; + m_bfilter = bfilter; + } + + @Override + public Geometry next() { + if (m_geometry == null) { + m_index = 0; + m_geometry = m_geoms.next(); + if (m_geometry == null) + return null; + } + + MultiPath mp = (MultiPath) (m_geometry); + if (m_index < mp.getPathCount()) { + int ind = m_index; + m_index++; + return m_bufferer.bufferPolylinePath_((Polyline) m_geometry, + ind, m_bfilter); + } + + m_geometry = null; + return next(); } @Override @@ -404,22 +488,6 @@ public int getGeometryID() { } } - private Bufferer(ProgressTracker progress_tracker) { - m_buffer_commands = new ArrayList(0); - m_progress_tracker = progress_tracker; - m_tolerance = 0; - m_small_tolerance = 0; - m_filter_tolerance = 0; - m_distance = 0; - m_original_geom_type = Geometry.GeometryType.Unknown; - m_abs_distance_reversed = 0; - m_abs_distance = 0; - m_densify_dist = -1; - m_dA = -1; - m_b_output_loops = true; - m_bfilter = true; - } - private Geometry buffer_() { int gt = m_geometry.getType().value(); if (Geometry.isSegment(gt)) {// convert segment to a polyline and repeat @@ -485,13 +553,15 @@ private Geometry bufferPolyline_() { } assert (m_distance > 0); - m_geometry = preparePolyline_((Polyline) (m_geometry)); - - GeometryCursorForPolyline cursor = new GeometryCursorForPolyline(this, - m_bfilter); - GeometryCursor union_cursor = ((OperatorUnion) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Union)).execute( - cursor, m_spatialReference, m_progress_tracker); + Polyline poly = (Polyline)m_geometry; m_geometry = null; + + GeometryCursor glueing_cursor = new GlueingCursorForPolyline(poly);//glues paths together if they connect at one point + poly = null; + GeometryCursor generalized_paths = OperatorGeneralize.local().execute(glueing_cursor, m_densify_dist * 0.25, false, m_progress_tracker); + GeometryCursor simple_paths = OperatorSimplifyOGC.local().execute(generalized_paths, null, true, m_progress_tracker);//make a planar graph. + generalized_paths = null; + GeometryCursor path_buffering_cursor = new GeometryCursorForPolyline(this, simple_paths, m_bfilter); simple_paths = null; + GeometryCursor union_cursor = OperatorUnion.local().execute(path_buffering_cursor, m_spatialReference, m_progress_tracker);//(int)Operator_union::Options::enum_disable_edge_dissolver Geometry result = union_cursor.next(); return result; } @@ -742,7 +812,7 @@ private Geometry bufferPoint_(Point point) { private Geometry bufferMultiPoint_() { assert (m_distance > 0); - GeometryCursorForMultiPoint mpCursor = new GeometryCursorForMultiPoint( + GeometryCursorForMultiPoint mpCursor = new GeometryCursorForMultiPoint(this, (MultiPoint) (m_geometry), m_distance, m_spatialReference, m_densify_dist, m_max_vertex_in_complete_circle, m_progress_tracker); @@ -861,8 +931,6 @@ private Polygon bufferPolylinePath_(Polyline polyline, int ipath, } Polyline result_polyline = new Polyline(polyline.getDescription()); - // result_polyline.reserve((m_circle_template.size() / 10 + 4) * - // mp_impl.getPathSize(ipath)); MultiPathImpl result_mp = (MultiPathImpl) result_polyline._getImpl(); boolean b_closed = mp_impl.isClosedPathInXYPlane(ipath); @@ -877,8 +945,6 @@ private Polygon bufferPolylinePath_(Polyline polyline, int ipath, (MultiPathImpl) input_multi_path._getImpl(), ipath, 0, input_multi_path.getSegmentCount(ipath), false); bufferClosedPath_(tmpPoly, 0, result_mp, bfilter, 1); - // Operator_factory_local::SaveJSONToTextFileDbg("c:/temp/buffer_prepare.txt", - // *result_polyline, nullptr); } return bufferCleanup_(result_polyline, false); @@ -902,7 +968,9 @@ private Polygon bufferCleanup_(MultiPath multi_path, boolean simplify_result) { return resultPolygon; } - private int calcN_(int minN) { + private int calcN_() { + //this method should be called only once m_circle_template_size is set then; + final int minN = 4; if (m_densify_dist == 0) return m_max_vertex_in_complete_circle; @@ -998,7 +1066,7 @@ private int bufferClosedPath_(Geometry input_geom, int ipath, ipath, true); edit_shape.filterClosePoints(m_filter_tolerance, false, false); if (edit_shape.getPointCount(geom) < 2) {// Got degenerate output. - // Wither bail out or + // Either bail out or // produce a circle. if (dir < 0) return 1;// negative direction produces nothing. @@ -1023,7 +1091,7 @@ private int bufferClosedPath_(Geometry input_geom, int ipath, if (bfilter) { // try removing the noise that does not contribute to the buffer. - int res_filter = filterPath_(edit_shape, geom, dir, true); + int res_filter = filterPath_(edit_shape, geom, dir, true, m_abs_distance, m_filter_tolerance, m_densify_dist); assert (res_filter == 1); // Operator_factory_local::SaveJSONToTextFileDbg("c:/temp/buffer_filter.txt", // *edit_shape.get_geometry(geom), nullptr); @@ -1216,228 +1284,484 @@ private int cleanupBufferCommands_() { return istart; } - private boolean isGap_(Point2D pt_before, Point2D pt_current, - Point2D pt_after) { - Point2D v_gap = new Point2D(); - v_gap.sub(pt_after, pt_before); - double gap_length = v_gap.length(); - double sqr_delta = m_abs_distance * m_abs_distance - gap_length - * gap_length * 0.25; - if (sqr_delta > 0) { - double delta = Math.sqrt(sqr_delta); - v_gap.normalize(); - v_gap.rightPerpendicular(); - Point2D p = new Point2D(); - p.sub(pt_current, pt_before); - double d = p.dotProduct(v_gap); - if (d + delta >= m_abs_distance) { - return true; + private static void protectExtremeVertices_(EditShape edit_shape, + int protection_index, int geom, int path) { + // detect very narrow corners and preserve them. We cannot reliably + // delete these. + int vprev = -1; + Point2D pt_prev = new Point2D(); + pt_prev.setNaN(); + Point2D pt = new Point2D(); + pt.setNaN(); + Point2D v_before = new Point2D(); + v_before.setNaN(); + Point2D pt_next = new Point2D(); + Point2D v_after = new Point2D(); + for (int i = 0, n = edit_shape.getPathSize(path), v = edit_shape + .getFirstVertex(path); i < n; ++i) { + if (vprev == -1) { + edit_shape.getXY(v, pt); + + vprev = edit_shape.getPrevVertex(v); + if (vprev != -1) { + edit_shape.getXY(vprev, pt_prev); + v_before.sub(pt, pt_prev); + v_before.normalize(); + } } - } - return false; + int vnext = edit_shape.getNextVertex(v); + if (vnext == -1) + break; + + edit_shape.getXY(vnext, pt_next); + v_after.sub(pt_next, pt); + v_after.normalize(); + + if (vprev != -1) { + double d = v_after.dotProduct(v_before); + if (d < -0.99 + && Math.abs(v_after.crossProduct(v_before)) < 1e-7) { + edit_shape.setUserIndex(v, protection_index, 1); + } + } + + vprev = v; + v = vnext; + pt_prev.setCoords(pt); + pt.setCoords(pt_next); + v_before.setCoords(v_after); + } } + + static private int filterPath_(EditShape edit_shape, int geom, int dir, + boolean closed, double abs_distance, double filter_tolerance, + double densify_distance) { + int path = edit_shape.getFirstPath(geom); - private int filterPath_(EditShape edit_shape, int geom, int dir, - boolean closed) { - // **********************!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // return 1; - - boolean bConvex = true; - for (int pass = 0; pass < 1; pass++) { - boolean b_filtered = false; - int ipath = edit_shape.getFirstPath(geom); - int isize = edit_shape.getPathSize(ipath); - if (isize == 0) - return 0; + int concave_index = -1; + int fixed_vertices_index = edit_shape.createUserIndex(); + protectExtremeVertices_(edit_shape, fixed_vertices_index, geom, path); + + for (int iter = 0; iter < 100; ++iter) { + int isize = edit_shape.getPathSize(path); + if (isize == 0) { + edit_shape.removeUserIndex(fixed_vertices_index); + ; + return 1; + } - int ncount = isize; - if (isize < 3) + int ivert = edit_shape.getFirstVertex(path); + int nvertices = edit_shape.getPathSize(path); + if (nvertices < 3) { + edit_shape.removeUserIndex(fixed_vertices_index); + ; return 1; + } - if (closed && !edit_shape.isClosedPath(ipath))// the path is closed + if (closed && !edit_shape.isClosedPath(path))// the path is closed // only virtually { - ncount = isize - 1; + nvertices -= 1; } - assert (dir == 1 || dir == -1); - int ivert = edit_shape.getFirstVertex(ipath); - if (!closed) - edit_shape.getNextVertex(ivert); + double abs_d = abs_distance; + final int nfilter = 64; + boolean filtered = false; + int filtered_in_pass = 0; + boolean go_back = false; + for (int i = 0; i < nvertices && ivert != -1; i++) { + int filtered_now = 0; + int v = ivert; // filter == 0 + for (int filter = 1, n = (int) Math.min(nfilter, nvertices - i); filter < n; filter++) { + v = edit_shape.getNextVertex(v, dir); + if (filter > 1) { + int num = clipFilter_(edit_shape, + fixed_vertices_index, ivert, v, dir, + abs_distance, densify_distance, nfilter); + if (num == -1) + break; - int iprev = dir > 0 ? edit_shape.getPrevVertex(ivert) : edit_shape - .getNextVertex(ivert); - int inext = dir > 0 ? edit_shape.getNextVertex(ivert) : edit_shape - .getPrevVertex(ivert); - int ibefore = iprev; - boolean reload = true; - Point2D pt_current = new Point2D(), pt_after = new Point2D(), pt_before = new Point2D(), pt_before_before = new Point2D(), pt_middle = new Point2D(), pt_gap_last = new Point2D( - 0, 0); - Point2D v_after = new Point2D(), v_before = new Point2D(), v_gap = new Point2D(); - Point2D temp = new Point2D(); - double abs_d = m_abs_distance; - // When the path is open we cannot process the first and the last - // vertices, so we process size - 2. - // When the path is closed, we can process all vertices. - int iter_count = closed ? ncount : isize - 2; - int gap_counter = 0; - for (int iter = 0; iter < iter_count;) { - edit_shape.getXY(inext, pt_after); - - if (reload) { - edit_shape.getXY(ivert, pt_current); - edit_shape.getXY(iprev, pt_before); - ibefore = iprev; + filtered |= num > 0; + filtered_now += num; + nvertices -= num; + } } - v_before.sub(pt_current, pt_before); - v_before.normalize(); + filtered_in_pass += filtered_now; - v_after.sub(pt_after, pt_current); - v_after.normalize(); + go_back = filtered_now > 0; - if (ibefore == inext) { + if (go_back) { + int prev = edit_shape.getPrevVertex(ivert, dir); + if (prev != -1) { + ivert = prev; + nvertices++; + continue; + } + } + + ivert = edit_shape.getNextVertex(ivert, dir); + } + + if (filtered_in_pass == 0) + break; + } + + edit_shape.removeUserIndex(fixed_vertices_index); + edit_shape.filterClosePoints(filter_tolerance, false, false); + + return 1; + } + + // This function clips out segments connecting from_vertiex to to_vertiex if + // they do not contribute to the buffer. + private static int clipFilter_(EditShape edit_shape, + int fixed_vertices_index, int from_vertex, int to_vertex, int dir, + double abs_distance, double densify_distance, final int max_filter) { + // Note: vertices marked with fixed_vertices_index cannot be deleted. + + Point2D pt1 = edit_shape.getXY(from_vertex); + Point2D pt2 = edit_shape.getXY(to_vertex); + if (pt1.equals(pt2)) + return -1; + + double densify_distance_delta = densify_distance * 0.25;// distance by + // which we can + // move the + // point closer + // to the chord + // (introducing + // an error into + // the buffer). + double erase_distance_delta = densify_distance * 0.25;// distance when + // we can erase + // the point + // (introducing + // an error into + // the buffer). + // This function goal is to modify or remove vertices between + // from_vertex and to_vertex in such a way that the result would not + // affect buffer to the left of the + // chain. + Point2D v_gap = new Point2D(); + v_gap.sub(pt2, pt1); + double gap_length = v_gap.length(); + double h2_4 = gap_length * gap_length * 0.25; + double sqr_center_to_chord = abs_distance * abs_distance - h2_4; // squared + // distance + // from + // the + // chord + // to + // the + // circle + // center + if (sqr_center_to_chord <= h2_4) + return -1;// center to chord distance is less than half gap, that + // means the gap is too wide for useful filtering (maybe + // this). + + double center_to_chord = Math.sqrt(sqr_center_to_chord); // distance + // from + // circle + // center to + // the + // chord. + + v_gap.normalize(); + Point2D v_gap_norm = new Point2D(v_gap); + v_gap_norm.rightPerpendicular(); + double chord_to_corner = h2_4 / center_to_chord; // cos(a) = + // center_to_chord / + // distance; + // chord_to_corner = + // distance / cos(a) + // - + // center_to_chord; + boolean can_erase_corner_point = chord_to_corner <= erase_distance_delta; + Point2D chord_midpoint = new Point2D(); + MathUtils.lerp(pt2, pt1, 0.5, chord_midpoint); + Point2D corner = new Point2D(v_gap_norm); + double corrected_chord_to_corner = chord_to_corner + - densify_distance_delta;// using slightly smaller than needed + // distance let us filter more. + corner.scaleAdd(Math.max(0.0, corrected_chord_to_corner), + chord_midpoint); + // corner = (p1 + p2) * 0.5 + v_gap_norm * chord_to_corner; + + Point2D center = new Point2D(v_gap_norm); + center.negate(); + center.scaleAdd(center_to_chord, chord_midpoint); + + double allowed_distance = abs_distance - erase_distance_delta; + double sqr_allowed_distance = MathUtils.sqr(allowed_distance); + double sqr_large_distance = sqr_allowed_distance * (1.9 * 1.9); + + Point2D co_p1 = new Point2D(); + co_p1.sub(corner, pt1); + Point2D co_p2 = new Point2D(); + co_p2.sub(corner, pt2); + + boolean large_distance = false;// set to true when distance + int cnt = 0; + char[] locations = new char[64]; + { + // check all vertices in the gap verifying that the gap can be + // clipped. + // + + Point2D pt = new Point2D(); + // firstly remove any duplicate vertices in the end. + for (int v = edit_shape.getPrevVertex(to_vertex, dir); v != from_vertex;) { + if (edit_shape.getUserIndex(v, fixed_vertices_index) == 1) + return -1;// this range contains protected vertex + + edit_shape.getXY(v, pt); + if (pt.equals(pt2)) { + int v1 = edit_shape.getPrevVertex(v, dir); + edit_shape.removeVertex(v, false); + v = v1; + continue; + } else { break; } + } - double cross = v_before.crossProduct(v_after); - double dot = v_before.dotProduct(v_after); - boolean bDoJoin = cross < 0 || (dot < 0 && cross == 0); - boolean b_write = true; - if (!bDoJoin) { - if (isGap_(pt_before, pt_current, pt_after)) { - pt_gap_last.setCoords(pt_after); - b_write = false; - ++gap_counter; - b_filtered = true; - } + Point2D prev_prev_pt = new Point2D(); + prev_prev_pt.setNaN(); + Point2D prev_pt = new Point2D(); + prev_pt.setCoords(pt1); + locations[cnt++] = 1; + int prev_v = from_vertex; + Point2D dummyPt = new Point2D(); + for (int v = edit_shape.getNextVertex(from_vertex, dir); v != to_vertex;) { + if (edit_shape.getUserIndex(v, fixed_vertices_index) == 1) + return -1;// this range contains protected vertex + + edit_shape.getXY(v, pt); + if (pt.equals(prev_pt)) { + int v1 = edit_shape.getNextVertex(v, dir); + edit_shape.removeVertex(v, false); + v = v1; + continue; + } - bConvex = false; + locations[cnt++] = 0; + + Point2D v1 = new Point2D(); + v1.sub(pt, pt1); + if (v1.dotProduct(v_gap_norm) < 0)// we are crossing on the + // wrong site of the chord. + // Just bail out earlier. + // Maybe we could continue + // clipping though here, but + // it seems to be + // unnecessary complicated. + return 0; + + if (Point2D.sqrDistance(pt, pt1) > sqr_large_distance + || Point2D.sqrDistance(pt, pt2) > sqr_large_distance) + large_distance = true; // too far from points, may + // contribute to the outline (in + // case of a large loop) + + char next_location = 0; + + dummyPt.sub(pt, pt1); + double cs1 = dummyPt.crossProduct(co_p1); + if (cs1 >= 0) { + next_location = 1; } - if (b_write) { - if (gap_counter > 0) { - for (;;) {// re-test back - int ibefore_before = dir > 0 ? edit_shape - .getPrevVertex(ibefore) : edit_shape - .getNextVertex(ibefore); - if (ibefore_before == ivert) - break; - - edit_shape.getXY(ibefore_before, pt_before_before); - if (isGap_(pt_before_before, pt_before, pt_gap_last)) { - pt_before.setCoords(pt_before_before); - ibefore = ibefore_before; - b_write = false; - ++gap_counter; - continue; - } else { - if (ibefore_before != inext - && isGap_(pt_before_before, pt_before, - pt_after) - && isGap_(pt_before_before, pt_current, - pt_after)) {// now the current - // point is a part - // of the gap also. - // We retest it. - pt_before.setCoords(pt_before_before); - ibefore = ibefore_before; - b_write = false; - ++gap_counter; - } - } - break; - } - } + dummyPt.sub(pt, pt2); + double cs2 = dummyPt.crossProduct(co_p2); + if (cs2 <= 0) { + next_location |= 2; + } - if (!b_write) - continue;// retest forward - - if (gap_counter > 0) { - // remove all but one gap vertices. - int p = dir > 0 ? edit_shape.getPrevVertex(iprev) - : edit_shape.getNextVertex(iprev); - for (int i = 1; i < gap_counter; i++) { - int pp = dir > 0 ? edit_shape.getPrevVertex(p) - : edit_shape.getNextVertex(p); - edit_shape.removeVertex(p, true); - p = pp; - } + if (next_location == 0) + return 0; - v_gap.sub(pt_current, pt_before); - double gap_length = v_gap.length(); - double sqr_delta = abs_d * abs_d - gap_length - * gap_length * 0.25; - double delta = Math.sqrt(sqr_delta); - if (abs_d - delta > m_densify_dist * 0.5) { - pt_middle.add(pt_before, pt_current); - pt_middle.scale(0.5); - v_gap.normalize(); - v_gap.rightPerpendicular(); - temp.setCoords(v_gap); - temp.scale(abs_d - delta); - pt_middle.add(temp); - edit_shape.setXY(iprev, pt_middle); - } else { - // the gap is too short to be considered. Can close - // it with the straight segment; - edit_shape.removeVertex(iprev, true); - } + locations[cnt - 1] = next_location; + prev_prev_pt.setCoords(prev_pt); + prev_pt.setCoords(pt); + prev_v = v; + v = edit_shape.getNextVertex(v, dir); + } - gap_counter = 0; - } + if (cnt == 1) + return 0; - pt_before.setCoords(pt_current); - ibefore = ivert; - } + assert (!pt2.equals(prev_pt)); + locations[cnt++] = 2; + } - pt_current.setCoords(pt_after); - iprev = ivert; - ivert = inext; - // reload = false; - inext = dir > 0 ? edit_shape.getNextVertex(ivert) : edit_shape - .getPrevVertex(ivert); - iter++; - reload = false; + boolean can_clip_all = true; + // we can remove all points and replace them with a single corner point + // if we are moving from location 1 via location 3 to location 2 + for (int i = 1, k = 0; i < cnt; i++) { + if (locations[i] != locations[i - 1]) { + k++; + can_clip_all = k < 3 + && ((k == 1 && locations[i] == 3) || (k == 2 && locations[i] == 2)); + if (!can_clip_all) + return 0; + } + } + + if (cnt > 2 && can_clip_all && (cnt == 3 || !large_distance)) { + int clip_count = 0; + int v = edit_shape.getNextVertex(from_vertex, dir); + if (!can_erase_corner_point) { + edit_shape.setXY(v, corner); + v = edit_shape.getNextVertex(v, dir); + } + + // we can remove all vertices between from and to, because they + // don't contribute + while (v != to_vertex) { + int v1 = edit_shape.getNextVertex(v, dir); + edit_shape.removeVertex(v, false); + v = v1; + ++clip_count; } - if (gap_counter > 0) { - int p = dir > 0 ? edit_shape.getPrevVertex(iprev) : edit_shape - .getNextVertex(iprev); - for (int i = 1; i < gap_counter; i++) { - int pp = dir > 0 ? edit_shape.getPrevVertex(p) : edit_shape - .getNextVertex(p); - edit_shape.removeVertex(p, true); - p = pp; + return clip_count; + } + + if (cnt == 3) { + boolean case1 = (locations[0] == 1 && locations[1] == 2 && locations[2] == 2); + boolean case2 = (locations[0] == 1 && locations[1] == 1 && locations[2] == 2); + if (case1 || case2) { + // special case, when we cannot clip, but we can move the point + Point2D p1 = edit_shape.getXY(from_vertex); + int v = edit_shape.getNextVertex(from_vertex, dir); + Point2D p2 = edit_shape.getXY(v); + Point2D p3 = edit_shape.getXY(edit_shape.getNextVertex(v, dir)); + if (case2) { + Point2D temp = p1; + p1 = p3; + p3 = temp; } - pt_middle.add(pt_before, pt_current); - pt_middle.scale(0.5); - - v_gap.sub(pt_current, pt_before); - double gap_length = v_gap.length(); - double sqr_delta = abs_d * abs_d - gap_length * gap_length - * 0.25; - assert (sqr_delta > 0); - double delta = Math.sqrt(sqr_delta); - v_gap.normalize(); - v_gap.rightPerpendicular(); - temp.setCoords(v_gap); - temp.scale(abs_d - delta); - pt_middle.add(temp); - edit_shape.setXY(iprev, pt_middle); + Point2D vec = new Point2D(); + vec.sub(p1, p2); + p3.sub(p2); + double veclen = vec.length(); + double w = p3.length(); + double wcosa = vec.dotProduct(p3) / veclen; + double wsina = Math.abs(p3.crossProduct(vec) / veclen); + double z = 2 * abs_distance - wsina; + if (z < 0) + return 0; + + double x = wcosa + Math.sqrt(wsina * z); + if (x > veclen) + return 0; + + Point2D hvec = new Point2D(); + hvec.scaleAdd(-x / veclen, vec, p3); // hvec = p3 - vec * (x / + // veclen); + double h = hvec.length(); + double y = -(h * h * veclen) / (2 * hvec.dotProduct(vec)); + + double t = (x - y) / veclen; + MathUtils.lerp(p2, p1, t, p2); + edit_shape.setXY(v, p2); + return 0; + } + } + + if (large_distance && cnt > 3) { + // we are processing more than 3 points and there are some points + // further than the + return 0; + } + + int v_prev = -1; + Point2D pt_prev = new Point2D(); + int v_cur = from_vertex; + Point2D pt_cur = new Point2D(pt1); + int cur_location = 1; + int prev_location = -1; // 1 - semiplane to the right of [f,c]. 3 - + // semiplane to the right of [c,t], 2 - both + // above fc and ct, 0 - cannot clip, -1 - + // unknown + int v_next = v_cur; + int clip_count = 0; + cnt = 1; + while (v_next != to_vertex) { + v_next = edit_shape.getNextVertex(v_next, dir); + int next_location = locations[cnt++]; + if (next_location == 0) { + if (v_next == to_vertex) + break; + + continue; } - edit_shape.filterClosePoints(m_filter_tolerance, false, false); + Point2D pt_next = edit_shape.getXY(v_next); + + if (prev_location != -1) { + int common_location = (prev_location & cur_location & next_location); + if ((common_location & 3) != 0) { + // prev and next are on the same semiplane as the current we + // can safely remove the current point. + edit_shape.removeVertex(v_cur, true); + clip_count++;// do not change prev point. + v_cur = v_next; + pt_cur.setCoords(pt_next); + cur_location = next_location; + continue; + } - if (!b_filtered) - break; + if (cur_location == 3 && prev_location != 0 + && next_location != 0) { + assert ((prev_location & next_location) == 0);// going from + // one semi + // plane to + // another + // via the + // mid. + pt_cur.setCoords(corner); + if (can_erase_corner_point || pt_cur.equals(pt_prev)) {// this + // point + // can + // be + // removed + edit_shape.removeVertex(v_cur, true); + clip_count++;// do not change prev point. + v_cur = v_next; + pt_cur.setCoords(pt_next); + cur_location = next_location; + continue; + } else { + edit_shape.setXY(v_cur, pt_cur); // snap to the corner + } + } else { + if (next_location == 0 + && cur_location != 0 + || next_location != 0 + && cur_location == 0 + || ((next_location | cur_location) == 3 + && next_location != 3 && cur_location != 3)) { + // clip + } + } + } + + prev_location = cur_location; + v_prev = v_cur; + pt_prev.setCoords(pt_cur); + v_cur = v_next; + cur_location = next_location; + pt_cur.setCoords(pt_next); } - return 1; + return clip_count; } - + private boolean isDegeneratePath_(MultiPathImpl mp_impl, int ipath) { if (mp_impl.getPathSize(ipath) == 1) return true; @@ -1510,7 +1834,7 @@ private void addCircle_(MultiPathImpl result_mp, Point point) { // avoid unnecessary memory allocation for the circle template. Just do // the point here. - int N = calcN_(4); + int N = m_circle_template_size; int real_size = (N + 3) / 4; double dA = (Math.PI * 0.5) / real_size; // result_mp.reserve(real_size * 4); @@ -1590,5 +1914,4 @@ private Polygon setStrongSimple_(Polygon poly) { ((MultiPathImpl) poly._getImpl())._updateOGCFlags(); return poly; } - } diff --git a/src/main/java/com/esri/core/geometry/ConvexHull.java b/src/main/java/com/esri/core/geometry/ConvexHull.java index bafe51e0..ab4b89c9 100644 --- a/src/main/java/com/esri/core/geometry/ConvexHull.java +++ b/src/main/java/com/esri/core/geometry/ConvexHull.java @@ -25,8 +25,7 @@ class ConvexHull { /* - * Constructor for a Convex_hull object.Used for dynamic insertion of - * geometries to create a convex hull. + * Constructor for a Convex_hull object. Used for dynamic insertion of geometries to create a convex hull. */ ConvexHull() { m_tree_hull = new Treap(); @@ -37,24 +36,23 @@ class ConvexHull { m_call_back = new CallBackShape(this); } - private ConvexHull(MultiVertexGeometry mvg) { + private ConvexHull(AttributeStreamOfDbl stream, int n) { m_tree_hull = new Treap(); - m_tree_hull.setCapacity(20); - m_mvg = mvg; - m_call_back = new CallBackMvg(this); + m_tree_hull.setCapacity(Math.min(20, n)); + m_stream = stream; + m_call_back = new CallBackStream(this); } - private ConvexHull(Point2D[] points) { + private ConvexHull(Point2D[] points, int n) { m_tree_hull = new Treap(); - m_tree_hull.setCapacity(20); + m_tree_hull.setCapacity(Math.min(20, n)); m_points = points; m_call_back = new CallBackPoints(this); } /** - * Adds a geometry to the current bounding geometry using an incremental - * algorithm for dynamic insertion. \param geometry The geometry to add to - * the bounding geometry. + * Adds a geometry to the current bounding geometry using an incremental algorithm for dynamic insertion. + * \param geometry The geometry to add to the bounding geometry. */ void addGeometry(Geometry geometry) { @@ -73,20 +71,19 @@ else if (type == Geometry.GeometryType.Point) } /** - * Gets the current bounding geometry. Returns a Geometry. + * Gets the current bounding geometry. + * Returns a Geometry. */ Geometry getBoundingGeometry() { - // Extracts the convex hull from the tree. Reading the tree in order - // from first to last is the resulting convex hull. + // Extracts the convex hull from the tree. Reading the tree in order from first to last is the resulting convex hull. Point point = new Point(); int first = m_tree_hull.getFirst(-1); Polygon hull = new Polygon(m_shape.getVertexDescription()); m_shape.queryPoint(m_tree_hull.getElement(first), point); hull.startPath(point); - for (int i = m_tree_hull.getNext(first); i != -1; i = m_tree_hull - .getNext(i)) { + for (int i = m_tree_hull.getNext(first); i != -1; i = m_tree_hull.getNext(i)) { m_shape.queryPoint(m_tree_hull.getElement(i), point); hull.lineTo(point); } @@ -96,58 +93,110 @@ Geometry getBoundingGeometry() { /** * Static method to construct the convex hull of a Multi_vertex_geometry. - * Returns a Polygon. \param mvg The geometry used to create the convex - * hull. + * Returns a Geometry. + * \param mvg The geometry used to create the convex hull. */ - static Polygon construct(MultiVertexGeometry mvg) { - ConvexHull convex_hull = new ConvexHull(mvg); + static Geometry construct(MultiVertexGeometry mvg) { + if (mvg.isEmpty()) + return new Polygon(mvg.getDescription()); + + MultiVertexGeometryImpl mvg_impl = (MultiVertexGeometryImpl) mvg._getImpl(); + int N = mvg_impl.getPointCount(); + + if (N <= 2) { + if (N == 1 || mvg_impl.getXY(0).equals(mvg_impl.getXY(1))) { + Point point = new Point(mvg_impl.getDescription()); + mvg_impl.getPointByVal(0, point); + return point; + } else { + Point pt = new Point(); + Polyline polyline = new Polyline(mvg_impl.getDescription()); + mvg_impl.getPointByVal(0, pt); + polyline.startPath(pt); + mvg_impl.getPointByVal(1, pt); + polyline.lineTo(pt); + return polyline; + } + } + + AttributeStreamOfDbl stream = (AttributeStreamOfDbl) mvg_impl.getAttributeStreamRef(VertexDescription.Semantics.POSITION); + ConvexHull convex_hull = new ConvexHull(stream, N); - int N = mvg.getPointCount(); int t0 = 0, tm = 1; Point2D pt_0 = new Point2D(); Point2D pt_m = new Point2D(); Point2D pt_p = new Point2D(); - mvg.getXY(t0, pt_0); + stream.read(t0 << 1, pt_0); while (true) { - mvg.getXY(tm, pt_m); - if (!(pt_m.isEqual(pt_0, NumberUtils.doubleEps()) && tm < N - 1)) + if (tm >= N) + break; + + stream.read(tm << 1, pt_m); + if (!pt_m.isEqual(pt_0, NumberUtils.doubleEps())) break; tm++; // We don't want to close the gap between t0 and tm. } convex_hull.m_tree_hull.addElement(t0, -1); - convex_hull.m_tree_hull.addBiggestElement(tm, -1); - for (int tp = tm + 1; tp < mvg.getPointCount(); tp++) {// Dynamically - // insert into - // the current - // convex hull + if (tm < N) { + convex_hull.m_tree_hull.addBiggestElement(tm, -1); - mvg.getXY(tp, pt_p); - int p = convex_hull.treeHull_(pt_p); + for (int tp = tm + 1; tp < mvg_impl.getPointCount(); tp++) {// Dynamically insert into the current convex hull - if (p != -1) - convex_hull.m_tree_hull.setElement(p, tp); // reset the place - // holder to the - // point index. + stream.read(tp << 1, pt_p); + int p = convex_hull.treeHull_(pt_p); + + if (p != -1) + convex_hull.m_tree_hull.setElement(p, tp); // reset the place holder to the point index. + } } - // Extracts the convex hull from the tree. Reading the tree in order - // from first to last is the resulting convex hull. - Point point = new Point(); - int first = convex_hull.m_tree_hull.getFirst(-1); - Polygon hull = new Polygon(mvg.getDescription()); - mvg.getPointByVal(convex_hull.m_tree_hull.getElement(first), point); - hull.startPath(point); + // Extracts the convex hull from the tree. Reading the tree in order from first to last is the resulting convex hull. - for (int i = convex_hull.m_tree_hull.getNext(first); i != -1; i = convex_hull.m_tree_hull - .getNext(i)) { - mvg.getPointByVal(convex_hull.m_tree_hull.getElement(i), point); - hull.lineTo(point); + VertexDescription description = mvg_impl.getDescription(); + boolean b_has_attributes = (description.getAttributeCount() > 1); + int point_count = convex_hull.m_tree_hull.size(-1); + + Geometry hull; + + if (point_count >= 2) { + if (point_count >= 3) + hull = new Polygon(description); + else + hull = new Polyline(description); + + MultiPathImpl hull_impl = (MultiPathImpl) hull._getImpl(); + hull_impl.addPath((Point2D[]) null, 0, true); + + Point point = null; + if (b_has_attributes) + point = new Point(); + + for (int i = convex_hull.m_tree_hull.getFirst(-1); i != -1; i = convex_hull.m_tree_hull.getNext(i)) { + if (b_has_attributes) { + mvg_impl.getPointByVal(convex_hull.m_tree_hull.getElement(i), point); + hull_impl.insertPoint(0, -1, point); + } else { + stream.read(convex_hull.m_tree_hull.getElement(i) << 1, pt_p); + hull_impl.insertPoint(0, -1, pt_p); + } + } + } else { + assert (point_count == 1); + + if (b_has_attributes) { + Point point = new Point(description); + mvg_impl.getPointByVal(convex_hull.m_tree_hull.getElement(convex_hull.m_tree_hull.getFirst(-1)), point); + hull = point; + } else { + stream.read(convex_hull.m_tree_hull.getElement(convex_hull.m_tree_hull.getFirst(-1)) << 1, pt_p); + hull = new Point(pt_p); + } } return hull; @@ -156,65 +205,57 @@ static Polygon construct(MultiVertexGeometry mvg) { /** * Static method to construct the convex hull from an array of points. The * out_convex_hull array will be populated with the subset of index - * positions which contribute to the convex hull. Returns the number of - * points in the convex hull. \param points The points used to create the - * convex hull. \param count The number of points in the input Point2D - * array. \param out_convex_hull An index array allocated by the user at - * least as big as the size of the input points array. + * positions which contribute to the convex hull. + * Returns the number of points in the convex hull. + * \param points The points used to create the convex hull. + * \param count The number of points in the input Point2D array. + * \param out_convex_hull An index array allocated by the user at least as big as the size of the input points array. */ - static int construct(Point2D[] points, int count, int[] bounding_geometry) { - ConvexHull convex_hull = new ConvexHull(points); + static int construct(Point2D[] points, int count, int[] out_convex_hull) { + ConvexHull convex_hull = new ConvexHull(points, count); int t0 = 0, tm = 1; Point2D pt_0 = points[t0]; - while (points[tm].isEqual(pt_0, NumberUtils.doubleEps()) - && tm < count - 1) + while (tm < count && points[tm].isEqual(pt_0, NumberUtils.doubleEps())) tm++; // We don't want to close the gap between t0 and tm. convex_hull.m_tree_hull.addElement(t0, -1); - convex_hull.m_tree_hull.addBiggestElement(tm, -1); - for (int tp = tm + 1; tp < count; tp++) {// Dynamically insert into the - // current convex hull. + if (tm < count) { + convex_hull.m_tree_hull.addBiggestElement(tm, -1); + + for (int tp = tm + 1; tp < count; tp++) {// Dynamically insert into the current convex hull. - Point2D pt_p = points[tp]; - int p = convex_hull.treeHull_(pt_p); + Point2D pt_p = points[tp]; + int p = convex_hull.treeHull_(pt_p); - if (p != -1) - convex_hull.m_tree_hull.setElement(p, tp); // reset the place - // holder to the - // point index. + if (p != -1) + convex_hull.m_tree_hull.setElement(p, tp); // reset the place holder to the point index. + } } - // Extracts the convex hull from the tree. Reading the tree in order - // from first to last is the resulting convex hull. + // Extracts the convex hull from the tree. Reading the tree in order from first to last is the resulting convex hull. int out_count = 0; - for (int i = convex_hull.m_tree_hull.getFirst(-1); i != -1; i = convex_hull.m_tree_hull - .getNext(i)) - bounding_geometry[out_count++] = convex_hull.m_tree_hull - .getElement(i); + for (int i = convex_hull.m_tree_hull.getFirst(-1); i != -1; i = convex_hull.m_tree_hull.getNext(i)) + out_convex_hull[out_count++] = convex_hull.m_tree_hull.getElement(i); return out_count; } /** - * Returns true if the given path of the input MultiPath is convex. Returns - * false otherwise. \param multi_path The MultiPath to check if the path is - * convex. \param path_index The path of the MultiPath to check if its - * convex. + * Returns true if the given path of the input MultiPath is convex. Returns false otherwise. + * \param multi_path The MultiPath to check if the path is convex. + * \param path_index The path of the MultiPath to check if its convex. */ - static boolean isPathConvex(MultiPath multi_path, int path_index, - ProgressTracker progress_tracker) { + static boolean isPathConvex(MultiPath multi_path, int path_index, ProgressTracker progress_tracker) { MultiPathImpl mimpl = (MultiPathImpl) multi_path._getImpl(); int path_start = mimpl.getPathStart(path_index); int path_end = mimpl.getPathEnd(path_index); - boolean bxyclosed = !mimpl.isClosedPath(path_index) - && mimpl.isClosedPathInXYPlane(path_index); + boolean bxyclosed = !mimpl.isClosedPath(path_index) && mimpl.isClosedPathInXYPlane(path_index); - AttributeStreamOfDbl position = (AttributeStreamOfDbl) (mimpl - .getAttributeStreamRef(VertexDescription.Semantics.POSITION)); + AttributeStreamOfDbl position = (AttributeStreamOfDbl) (mimpl.getAttributeStreamRef(VertexDescription.Semantics.POSITION)); int position_start = 2 * path_start; int position_end = 2 * path_end; @@ -224,23 +265,15 @@ static boolean isPathConvex(MultiPath multi_path, int path_index, if (position_end - position_start < 6) return true; - // This matches the logic for case 1 of the tree hull algorithm. The - // idea is inductive. We assume we have a convex hull pt_0,...,pt_m, and - // we see if - // a new point (pt_pivot) is among the transitive tournament for pt_0, - // knowing that pt_pivot comes after pt_m. + // This matches the logic for case 1 of the tree hull algorithm. The idea is inductive. We assume we have a convex hull pt_0,...,pt_m, and we see if + // a new point (pt_pivot) is among the transitive tournament for pt_0, knowing that pt_pivot comes after pt_m. // We check three conditions: - // 1) pt_m->pt_pivot->pt_0 is clockwise (closure across the boundary is - // convex) - // 2) pt_1->pt_pivot->pt_0 is clockwise (the first step forward is - // convex) (pt_1 is the next point after pt_0) - // 3) pt_m->pt_pivot->pt_m_prev is clockwise (the first step backwards - // is convex) (pt_m_prev is the previous point before pt_m) + // 1) pt_m->pt_pivot->pt_0 is clockwise (closure across the boundary is convex) + // 2) pt_1->pt_pivot->pt_0 is clockwise (the first step forward is convex) (pt_1 is the next point after pt_0) + // 3) pt_m->pt_pivot->pt_m_prev is clockwise (the first step backwards is convex) (pt_m_prev is the previous point before pt_m) - // If all three of the above conditions are clockwise, then pt_pivot is - // among the transitive tournament for pt_0, and therefore the polygon - // pt_0, ..., pt_m, pt_pivot is convex. + // If all three of the above conditions are clockwise, then pt_pivot is among the transitive tournament for pt_0, and therefore the polygon pt_0, ..., pt_m, pt_pivot is convex. Point2D pt_0 = new Point2D(), pt_m = new Point2D(), pt_pivot = new Point2D(); position.read(position_start, pt_0); @@ -256,8 +289,7 @@ static boolean isPathConvex(MultiPath multi_path, int path_index, Point2D pt_1 = new Point2D(pt_m.x, pt_m.y); Point2D pt_m_prev = new Point2D(); - // Assume that pt_0,...,pt_m is convex. Check if the next point, - // pt_pivot, maintains the convex invariant. + // Assume that pt_0,...,pt_m is convex. Check if the next point, pt_pivot, maintains the convex invariant. for (int i = position_start + 6; i < position_end; i += 2) { pt_m_prev.setCoords(pt_m); pt_m.setCoords(pt_pivot); @@ -325,7 +357,7 @@ private void addSegment_(Segment segment) { segment.queryStart(point); int t_start = m_shape.addPoint(m_path_handle, point); m_tree_hull.setElement(p_start, t_start); // reset the place holder - // to tp + // to tp } Point2D pt_end = segment.getEndXY(); @@ -335,7 +367,7 @@ private void addSegment_(Segment segment) { segment.queryEnd(point); int t_end = m_shape.addPoint(m_path_handle, point); m_tree_hull.setElement(p_end, t_end); // reset the place holder to - // tp + // tp } } @@ -353,9 +385,7 @@ private int addPoint_(Point2D pt_p) { int p = -1; if (m_tree_hull.size(-1) == 0) { - p = m_tree_hull.addElement(-4, -1); // set place holder to -4 to - // indicate the first element - // being added (t0). + p = m_tree_hull.addElement(-4, -1); // reset the place holder to tp return p; } @@ -363,15 +393,8 @@ private int addPoint_(Point2D pt_p) { int t0 = m_tree_hull.getElement(m_tree_hull.getFirst(-1)); Point2D pt_0 = m_shape.getXY(t0); - if (!pt_p.isEqual(pt_0, NumberUtils.doubleEps())) // We don't want - // to close the - // gap between - // t0 and tm. - p = m_tree_hull.addBiggestElement(-5, -1); // set place holder - // to -5 to indicate - // the second - // element being - // added (tm). + if (!pt_p.isEqual(pt_0, NumberUtils.doubleEps())) // We don't want to close the gap between t0 and tm. + p = m_tree_hull.addBiggestElement(-5, -1); // set place holder to -5 to indicate the second element being added (tm). return p; } @@ -380,8 +403,7 @@ private int addPoint_(Point2D pt_p) { return p; } - // Algorithm taken from "Axioms and Hulls" by D.E. Knuth, Lecture Notes in - // Computer Science 606, page 47. + // Algorithm taken from "Axioms and Hulls" by D.E. Knuth, Lecture Notes in Computer Science 606, page 47. private int treeHull_(Point2D pt_pivot) { assert (m_tree_hull.size(-1) >= 2); @@ -398,55 +420,31 @@ private int treeHull_(Point2D pt_pivot) { m_call_back.getXY(t0, pt_0); m_call_back.getXY(tm, pt_m); - assert (!pt_0.isEqual(pt_m, NumberUtils.doubleEps())); // assert - // that the - // gap is - // not - // closed + assert (!pt_0.isEqual(pt_m, NumberUtils.doubleEps())); // assert that the gap is not closed - int orient_m_p_0 = Point2D.orientationRobust(pt_m, pt_pivot, pt_0); // determines - // case - // 1, - // 2, - // 3 + int orient_m_p_0 = Point2D.orientationRobust(pt_m, pt_pivot, pt_0); // determines case 1, 2, 3 if (isClockwise_(orient_m_p_0)) {// Case 1: tp->t0->tm is clockwise - p = m_tree_hull.addBiggestElement(-1, -1); // set place holder - // to -1 for case 1. + p = m_tree_hull.addBiggestElement(-1, -1); // set place holder to -1 for case 1. int l = treeHullWalkBackward_(pt_pivot, last, first); if (l != first) - treeHullWalkForward_(pt_pivot, first, - m_tree_hull.getPrev(l)); + treeHullWalkForward_(pt_pivot, first, m_tree_hull.getPrev(l)); continue; } - if (isCounterClockwise_(orient_m_p_0)) {// Case 2: tp->tm->t0 is - // clockwise - int k = m_tree_hull.getRoot(-1), k_min = m_tree_hull - .getFirst(-1), k_max = m_tree_hull.getLast(-1), k_prev; + if (isCounterClockwise_(orient_m_p_0)) {// Case 2: tp->tm->t0 is clockwise + int k = m_tree_hull.getRoot(-1), k_min = m_tree_hull.getFirst(-1), k_max = m_tree_hull.getLast(-1), k_prev; int tk, tk_prev; Point2D pt_k = new Point2D(); Point2D pt_k_prev = new Point2D(); - while (k_min != m_tree_hull.getPrev(k_max)) {// binary search to - // find k such - // that - // t0->tp->tj - // holds (i.e. - // clockwise) - // for j >= k. - // Hence, - // tj->tp->t0 is - // clockwise (or - // degenerate) - // for j < k. + while (k_min != m_tree_hull.getPrev(k_max)) {// binary search to find k such that t0->tp->tj holds (i.e. clockwise) for j >= k. Hence, tj->tp->t0 is clockwise (or degenerate) for j < k. tk = m_tree_hull.getElement(k); m_call_back.getXY(tk, pt_k); - int orient_k_p_0 = Point2D.orientationRobust(pt_k, - pt_pivot, pt_0); + int orient_k_p_0 = Point2D.orientationRobust(pt_k, pt_pivot, pt_0); if (isCounterClockwise_(orient_k_p_0)) { k_max = k; @@ -463,23 +461,17 @@ private int treeHull_(Point2D pt_pivot) { tk_prev = m_tree_hull.getElement(k_prev); m_call_back.getXY(tk, pt_k); m_call_back.getXY(tk_prev, pt_k_prev); - assert (isCounterClockwise_(Point2D.orientationRobust(pt_k, - pt_pivot, pt_0)) && !isCounterClockwise_(Point2D - .orientationRobust(pt_k_prev, pt_pivot, pt_0))); - assert (k_prev != first || isCounterClockwise_(Point2D - .orientationRobust(pt_k, pt_pivot, pt_0))); + assert (isCounterClockwise_(Point2D.orientationRobust(pt_k, pt_pivot, pt_0)) && !isCounterClockwise_(Point2D.orientationRobust(pt_k_prev, pt_pivot, pt_0))); + assert (k_prev != first || isCounterClockwise_(Point2D.orientationRobust(pt_k, pt_pivot, pt_0))); if (k_prev != first) { - int orient_k_prev_p_k = Point2D.orientationRobust( - pt_k_prev, pt_pivot, pt_k); + int orient_k_prev_p_k = Point2D.orientationRobust(pt_k_prev, pt_pivot, pt_k); if (!isClockwise_(orient_k_prev_p_k)) - continue; // pt_pivot is inside the hull (or on the - // boundary) + continue; // pt_pivot is inside the hull (or on the boundary) } - p = m_tree_hull.addElementAtPosition(k_prev, k, -2, true, - false, -1); // set place holder to -2 for case 2. + p = m_tree_hull.addElementAtPosition(k_prev, k, -2, true, false, -1); // set place holder to -2 for case 2. treeHullWalkForward_(pt_pivot, k, last); treeHullWalkBackward_(pt_pivot, k_prev, first); @@ -488,40 +480,17 @@ private int treeHull_(Point2D pt_pivot) { assert (isDegenerate_(orient_m_p_0)); {// Case 3: degenerate + int between = isBetween_(pt_pivot, pt_m, pt_0); - if (m_line == null) - m_line = new Line(); - - m_line.setStartXY(pt_m); - m_line.setEndXY(pt_0); - - double scalar = m_line.getClosestCoordinate(pt_pivot, true); // if - // scalar - // is - // between - // 0 - // and - // 1, - // then - // we - // don't - // need - // to - // add - // tp. - - if (scalar < 0.0) { + if (between == -1) { int l = m_tree_hull.getPrev(last); m_tree_hull.deleteNode(last, -1); - p = m_tree_hull.addBiggestElement(-3, -1); // set place - // holder to -3 - // for case 3. + p = m_tree_hull.addBiggestElement(-3, -1); // set place holder to -3 for case 3. treeHullWalkBackward_(pt_pivot, l, first); - } else if (scalar > 1.0) { + } else if (between == 1) { int j = m_tree_hull.getNext(first); m_tree_hull.deleteNode(first, -1); - p = m_tree_hull.addElementAtPosition(-1, j, -3, true, - false, -1); // set place holder to -3 for case 3. + p = m_tree_hull.addElementAtPosition(-1, j, -3, true, false, -1); // set place holder to -3 for case 3. treeHullWalkForward_(pt_pivot, j, last); } @@ -545,19 +514,11 @@ private int treeHullWalkForward_(Point2D pt_pivot, int start, int end) { m_call_back.getXY(tj, pt_j); - while (j != end && m_tree_hull.size(-1) > 2) {// Stops when we find a - // clockwise triple - // containting the pivot - // point, or when the - // tree_hull size is 2. - // Deletes non-clockwise - // triples along the - // way. + while (j != end && m_tree_hull.size(-1) > 2) {//Stops when we find a clockwise triple containting the pivot point, or when the tree_hull size is 2. Deletes non-clockwise triples along the way. int tj_next = m_tree_hull.getElement(j_next); m_call_back.getXY(tj_next, pt_j_next); - int orient_j_next_p_j = Point2D.orientationRobust(pt_j_next, - pt_pivot, pt_j); + int orient_j_next_p_j = Point2D.orientationRobust(pt_j_next, pt_pivot, pt_j); if (isClockwise_(orient_j_next_p_j)) break; @@ -585,19 +546,11 @@ private int treeHullWalkBackward_(Point2D pt_pivot, int start, int end) { m_call_back.getXY(tl, pt_l); - while (l != end && m_tree_hull.size(-1) > 2) {// Stops when we find a - // clockwise triple - // containting the pivot - // point, or when the - // tree_hull size is 2. - // Deletes non-clockwise - // triples along the - // way. + while (l != end && m_tree_hull.size(-1) > 2) {//Stops when we find a clockwise triple containting the pivot point, or when the tree_hull size is 2. Deletes non-clockwise triples along the way. int tl_prev = m_tree_hull.getElement(l_prev); m_call_back.getXY(tl_prev, pt_l_prev); - int orient_l_p_l_prev = Point2D.orientationRobust(pt_l, pt_pivot, - pt_l_prev); + int orient_l_p_l_prev = Point2D.orientationRobust(pt_l, pt_pivot, pt_l_prev); if (isClockwise_(orient_l_p_l_prev)) break; @@ -661,6 +614,71 @@ private static boolean isDegenerate_(int orientation) { return orientation == 0.0; } + private static int isBetween_(Point2D pt_pivot, Point2D pt_m, Point2D pt_0) { + int ordinate = -1; + + if (pt_m.y == pt_0.y) { + ordinate = 0; + } else if (pt_m.x == pt_0.x) { + ordinate = 1; + } else {// use bigger ordinate, but shouldn't matter + + double diff_x = Math.abs(pt_m.x - pt_0.x); + double diff_y = Math.abs(pt_m.y - pt_0.y); + + if (diff_x >= diff_y) + ordinate = 0; + else + ordinate = 1; + } + + int res = -1; + + if (ordinate == 0) { + assert (pt_m.x != pt_0.x); + + if (pt_m.x < pt_0.x) { + if (pt_pivot.x < pt_m.x) + res = -1; + else if (pt_0.x < pt_pivot.x) + res = 1; + else + res = 0; + } else { + assert (pt_0.x < pt_m.x); + + if (pt_m.x < pt_pivot.x) + res = -1; + else if (pt_pivot.x < pt_0.x) + res = 1; + else + res = 0; + } + } else { + assert (pt_m.y != pt_0.y); + + if (pt_m.y < pt_0.y) { + if (pt_pivot.y < pt_m.y) + res = -1; + else if (pt_0.y < pt_pivot.y) + res = 1; + else + res = 0; + } else { + assert (pt_0.y < pt_m.y); + + if (pt_m.y < pt_pivot.y) + res = -1; + else if (pt_pivot.y < pt_0.y) + res = 1; + else + res = 0; + } + } + + return res; + } + private static abstract class CallBack { abstract void getXY(int ti, Point2D pt); @@ -687,16 +705,16 @@ void deleteNode(int i) { } } - private static final class CallBackMvg extends CallBack { + private static final class CallBackStream extends CallBack { private ConvexHull m_convex_hull; - CallBackMvg(ConvexHull convex_hull) { + CallBackStream(ConvexHull convex_hull) { m_convex_hull = convex_hull; } @Override void getXY(int ti, Point2D pt) { - m_convex_hull.m_mvg.getXY(ti, pt); + m_convex_hull.m_stream.read(ti << 1, pt); } @Override @@ -726,7 +744,7 @@ void deleteNode(int i) { // Members private Treap m_tree_hull; private EditShape m_shape; - private MultiVertexGeometry m_mvg; + private AttributeStreamOfDbl m_stream; private Point2D[] m_points; private int m_geometry_handle; private int m_path_handle; diff --git a/src/main/java/com/esri/core/geometry/ECoordinate.java b/src/main/java/com/esri/core/geometry/ECoordinate.java index c3d5c522..5bcf0460 100644 --- a/src/main/java/com/esri/core/geometry/ECoordinate.java +++ b/src/main/java/com/esri/core/geometry/ECoordinate.java @@ -27,6 +27,18 @@ class ECoordinate { private double m_value; private double m_eps; + ECoordinate() { + set(0.0, 0.0); + } + + ECoordinate(double v) { + set(v); + } + + ECoordinate(ECoordinate v) { + set(v); + } + double epsCoordinate() { return NumberUtils.doubleEps(); } @@ -160,7 +172,7 @@ void mul(double v) { } void mul(ECoordinate v_1, ECoordinate v_2) { - double r = Math.abs(v_1.m_value) * Math.abs(v_2.m_value); + double r = v_1.m_value * v_2.m_value; m_eps = v_1.m_eps * Math.abs(v_2.m_value) + v_2.m_eps * Math.abs(v_1.m_value) + v_1.m_eps * v_2.m_eps + epsCoordinate() * Math.abs(r); diff --git a/src/main/java/com/esri/core/geometry/EditShape.java b/src/main/java/com/esri/core/geometry/EditShape.java index 36ce09a8..1dbdb37b 100644 --- a/src/main/java/com/esri/core/geometry/EditShape.java +++ b/src/main/java/com/esri/core/geometry/EditShape.java @@ -1865,6 +1865,14 @@ int getPrevVertex(int currentVertex) { return m_vertex_index_list.getField(currentVertex, 1); } + int getPrevVertex(int currentVertex, int dir) { + return dir > 0 ? m_vertex_index_list.getField(currentVertex, 1) : m_vertex_index_list.getField(currentVertex, 2); + } + + int getNextVertex(int currentVertex, int dir) { + return dir > 0 ? m_vertex_index_list.getField(currentVertex, 2) : m_vertex_index_list.getField(currentVertex, 1); + } + // Returns a path the vertex belongs to. int getPathFromVertex(int vertex) { return m_vertex_index_list.getField(vertex, 3); diff --git a/src/main/java/com/esri/core/geometry/EnvSrlzr.java b/src/main/java/com/esri/core/geometry/EnvSrlzr.java new file mode 100644 index 00000000..a6cea40a --- /dev/null +++ b/src/main/java/com/esri/core/geometry/EnvSrlzr.java @@ -0,0 +1,95 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +//This is a writeReplace class for Envelope +public class EnvSrlzr implements Serializable { + private static final long serialVersionUID = 1L; + double[] attribs; + int descriptionBitMask; + + public Object readResolve() throws ObjectStreamException { + Envelope env = null; + if (descriptionBitMask == -1) + return null; + + try { + VertexDescription vd = VertexDescriptionDesignerImpl + .getVertexDescription(descriptionBitMask); + env = new Envelope(vd); + if (attribs != null) { + env.setCoords(attribs[0], attribs[1], attribs[2], attribs[3]); + int index = 4; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + env.setInterval(semantics, ord, attribs[index++], attribs[index++]); + } + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot read geometry from stream"); + } + + return env; + } + + public void setGeometryByValue(Envelope env) throws ObjectStreamException { + try { + attribs = null; + if (env == null) { + descriptionBitMask = -1; + } + + VertexDescription vd = env.getDescription(); + descriptionBitMask = vd.m_semanticsBitArray; + if (env.isEmpty()) { + return; + } + + attribs = new double[vd.getTotalComponentCount() * 2]; + attribs[0] = env.getXMin(); + attribs[1] = env.getYMin(); + attribs[2] = env.getXMax(); + attribs[3] = env.getYMax(); + int index = 4; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + Envelope1D e = env.queryInterval(semantics, ord); + attribs[index++] = e.vmin; + attribs[index++] = e.vmax; + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot serialize this geometry"); + } + } +} diff --git a/src/main/java/com/esri/core/geometry/Envelope.java b/src/main/java/com/esri/core/geometry/Envelope.java index 6991f26e..1370ca4f 100644 --- a/src/main/java/com/esri/core/geometry/Envelope.java +++ b/src/main/java/com/esri/core/geometry/Envelope.java @@ -32,8 +32,9 @@ /** * An envelope is an axis-aligned rectangle. */ -public final class Envelope extends Geometry implements Serializable { - private static final long serialVersionUID = 2L; +public class Envelope extends Geometry implements Serializable { + //We are using writeReplace instead. + //private static final long serialVersionUID = 2L; Envelope2D m_envelope = new Envelope2D(); @@ -58,20 +59,20 @@ public Envelope(Point center, double width, double height) { _setFromPoint(center, width, height); } - Envelope(Envelope2D env2D) { + public Envelope(Envelope2D env2D) { m_description = VertexDescriptionDesignerImpl.getDefaultDescriptor2D(); m_envelope.setCoords(env2D); m_envelope.normalize(); } - Envelope(VertexDescription vd) { + public Envelope(VertexDescription vd) { if (vd == null) throw new IllegalArgumentException(); m_description = vd; m_envelope.setEmpty(); } - Envelope(VertexDescription vd, Envelope2D env2D) { + public Envelope(VertexDescription vd, Envelope2D env2D) { if (vd == null) throw new IllegalArgumentException(); m_description = vd; @@ -215,16 +216,16 @@ public double getCenterY() { return m_envelope.getCenterY(); } - /** + /** * The x and y-coordinates of the center of the envelope. * * @return A point whose x and y-coordinates are that of the center of the envelope. */ - Point2D getCenterXY() { + public Point2D getCenterXY() { return m_envelope.getCenter(); } - void getCenter(Point point_out) { + public void getCenter(Point point_out) { point_out.assignVertexDescription(m_description); if (isEmpty()) { point_out.setEmpty(); @@ -244,7 +245,7 @@ void getCenter(Point point_out) { point_out.setXY(m_envelope.getCenter()); } - void merge(Point2D pt) { + public void merge(Point2D pt) { _touch(); m_envelope.merge(pt); } @@ -341,7 +342,7 @@ void _setFromPoint(Point centerPoint) { } } - void merge(Envelope2D other) { + public void merge(Envelope2D other) { _touch(); m_envelope.merge(other); } @@ -415,7 +416,7 @@ public void copyTo(Geometry dst) { { envDst._ensureAttributes(); System.arraycopy(m_attributes, 0, envDst.m_attributes, 0, - (m_description._getTotalComponents() - 2) * 2); + (m_description.getTotalComponentCount() - 2) * 2); } } @@ -493,7 +494,7 @@ public void setInterval(int semantics, int ordinate, Envelope1D env) { } } - void queryCoordinates(Point2D[] dst) { + public void queryCoordinates(Point2D[] dst) { if (dst == null || dst.length < 4 || m_envelope.isEmpty()) throw new IllegalArgumentException(); @@ -573,7 +574,7 @@ public void queryCornerByVal(int index, Point ptDst) { } } - void queryCorner(int index, Point2D ptDst) { + public void queryCorner(int index, Point2D ptDst) { Point2D p = m_envelope.queryCorner(index); ptDst.setCoords(p.x, p.y); } @@ -645,8 +646,8 @@ void setAttributeAsDblImpl_(int end_point, int semantics, int ordinate, void _ensureAttributes() { _touch(); - if (m_attributes == null && m_description._getTotalComponents() > 2) { - m_attributes = new double[(m_description._getTotalComponents() - 2) * 2]; + if (m_attributes == null && m_description.getTotalComponentCount() > 2) { + m_attributes = new double[(m_description.getTotalComponentCount() - 2) * 2]; int offset0 = _getEndPointOffset(m_description, 0); int offset1 = _getEndPointOffset(m_description, 1); @@ -672,10 +673,10 @@ protected void _assignVertexDescriptionImpl(VertexDescription newDescription) { return; } - if (newDescription._getTotalComponents() > 2) { + if (newDescription.getTotalComponentCount() > 2) { int[] mapping = VertexDescriptionDesignerImpl.mapAttributes(newDescription, m_description); - double[] newAttributes = new double[(newDescription._getTotalComponents() - 2) * 2]; + double[] newAttributes = new double[(newDescription.getTotalComponentCount() - 2) * 2]; int old_offset0 = _getEndPointOffset(m_description, 0); int old_offset1 = _getEndPointOffset(m_description, 1); @@ -793,10 +794,10 @@ int _getAttributeAsInt(int endPoint, int semantics, int ordinate) { } static int _getEndPointOffset(VertexDescription vd, int endPoint) { - return endPoint * (vd._getTotalComponents() - 2); + return endPoint * (vd.getTotalComponentCount() - 2); } - boolean isIntersecting(Envelope2D other) { + public boolean isIntersecting(Envelope2D other) { return m_envelope.isIntersecting(other); } @@ -875,7 +876,7 @@ public void normalize() {// TODO: attributes * * @return The center point of the envelope. */ - Point2D getCenter2D() { + public Point2D getCenter2D() { return m_envelope.getCenter(); } @@ -1005,7 +1006,7 @@ public boolean equals(Object _other) { if (!this.m_envelope.equals(other.m_envelope)) return false; - for (int i = 0, n = (m_description._getTotalComponents() - 2) * 2; i < n; i++) + for (int i = 0, n = (m_description.getTotalComponentCount() - 2) * 2; i < n; i++) if (m_attributes[i] != other.m_attributes[i]) return false; @@ -1022,7 +1023,7 @@ public int hashCode() { int hashCode = m_description.hashCode(); hashCode = NumberUtils.hash(hashCode, m_envelope.hashCode()); if (!isEmpty() && m_attributes != null) { - for (int i = 0, n = (m_description._getTotalComponents() - 2) * 2; i < n; i++) { + for (int i = 0, n = (m_description.getTotalComponentCount() - 2) * 2; i < n; i++) { hashCode = NumberUtils.hash(hashCode, m_attributes[i]); } } diff --git a/src/main/java/com/esri/core/geometry/Envelope1D.java b/src/main/java/com/esri/core/geometry/Envelope1D.java index 9e30e08b..96540895 100644 --- a/src/main/java/com/esri/core/geometry/Envelope1D.java +++ b/src/main/java/com/esri/core/geometry/Envelope1D.java @@ -45,12 +45,20 @@ public Envelope1D(double _vmin, double _vmax) { setCoords(_vmin, _vmax); } + public Envelope1D(Envelope1D other) { + setCoords(other); + } + public void setCoords(double _vmin, double _vmax) { vmin = _vmin; vmax = _vmax; normalize(); } + public void setCoords(Envelope1D other) { + setCoords(other.vmin, other.vmax); + } + public void normalize() { if (NumberUtils.isNaN(vmin)) return; @@ -71,7 +79,7 @@ public void setEmpty() { } public boolean isEmpty() { - return NumberUtils.isNaN(vmin); + return NumberUtils.isNaN(vmin) || NumberUtils.isNaN(vmax); } public void setInfinite() { diff --git a/src/main/java/com/esri/core/geometry/Envelope2D.java b/src/main/java/com/esri/core/geometry/Envelope2D.java index 3b9a02f7..172619dd 100644 --- a/src/main/java/com/esri/core/geometry/Envelope2D.java +++ b/src/main/java/com/esri/core/geometry/Envelope2D.java @@ -59,6 +59,12 @@ public static Envelope2D construct(double _xmin, double _ymin, return env; } + public static Envelope2D construct(Envelope2D other) { + Envelope2D env = new Envelope2D(); + env.setCoords(other); + return env; + } + public Envelope2D() { setEmpty(); } @@ -70,6 +76,10 @@ public Envelope2D(double _xmin, double _ymin, double _xmax, double _ymax) { ymax = _ymax; } + public Envelope2D(Envelope2D other) { + setCoords(other); + } + public void setCoords(double _x, double _y) { xmin = _x; ymin = _y; @@ -144,7 +154,7 @@ public void setInfinite() { } public boolean isEmpty() { - return NumberUtils.isNaN(xmin); + return NumberUtils.isNaN(xmin) || NumberUtils.isNaN(ymin) || NumberUtils.isNaN(xmax) || NumberUtils.isNaN(ymax); } public void setCoords(Envelope1D xinterval, Envelope1D yinterval) { @@ -240,14 +250,13 @@ public void zoom(double factorX, double factorY) { } /** - * Checks if this envelope intersects the other. + * Checks if this envelope intersects the other. * @return True if this envelope intersects the other. */ public boolean isIntersecting(Envelope2D other) { - return !isEmpty() - && !other.isEmpty() - && ((xmin <= other.xmin) ? xmax >= other.xmin - : other.xmax >= xmin) + // No need to check if empty, this will work for empty envelopes too + // (IEEE math) + return ((xmin <= other.xmin) ? xmax >= other.xmin : other.xmax >= xmin) && // check that x projections overlap ((ymin <= other.ymin) ? ymax >= other.ymin : other.ymax >= ymin); // check // that @@ -257,7 +266,7 @@ public boolean isIntersecting(Envelope2D other) { } /** - * Checks if this envelope intersects the other assuming neither one is empty. + * Checks if this envelope intersects the other assuming neither one is empty. * @return True if this envelope intersects the other. Assumes this and * other envelopes are not empty. */ @@ -271,6 +280,23 @@ public boolean isIntersectingNE(Envelope2D other) { // overlap } + /** + * Checks if this envelope intersects the other. + * @return True if this envelope intersects the other. + */ + public boolean isIntersecting(double xmin_, double ymin_, double xmax_, double ymax_) { + // No need to check if empty, this will work for empty geoms too (IEEE + // math) + return ((xmin <= xmin_) ? xmax >= xmin_ : xmax_ >= xmin) && // check + // that x + // projections + // overlap + ((ymin <= ymin_) ? ymax >= ymin_ : ymax_ >= ymin); // check that + // y + // projections + // overlap + } + /** * Intersects this envelope with the other and stores result in this * envelope. @@ -303,15 +329,20 @@ public boolean intersect(Envelope2D other) { } /** - * Queries a corner of the envelope. - * - * @param index Indicates a corner of the envelope. - *

0 => lower left or (xmin, ymin) - *

1 => upper left or (xmin, ymax) - *

2 => upper right or (xmax, ymax) - *

3 => lower right or (xmax, ymin) - * @return Point at a corner of the envelope. - * + * Queries a corner of the envelope. + * + * @param index + * Indicates a corner of the envelope. + *

+ * 0 means lower left or (xmin, ymin) + *

+ * 1 means upper left or (xmin, ymax) + *

+ * 2 means upper right or (xmax, ymax) + *

+ * 3 means lower right or (xmax, ymin) + * @return Point at a corner of the envelope. + * */ public Point2D queryCorner(int index) { switch (index) { @@ -1081,6 +1112,62 @@ public double sqrDistance(Envelope2D other) return dx * dx + dy * dy; } + /** + * Calculates minimum squared distance from this envelope to the other. + * Returns 0 for empty envelopes. + */ + public double sqrDistance(double xmin_, double ymin_, double xmax_, double ymax_) + { + double dx = 0; + double dy = 0; + double nn; + + nn = xmin - xmax_; + if (nn > dx) + dx = nn; + + nn = ymin - ymax_; + if (nn > dy) + dy = nn; + + nn = xmin_ - xmax; + if (nn > dx) + dx = nn; + + nn = ymin_ - ymax; + if (nn > dy) + dy = nn; + + return dx * dx + dy * dy; + } + + /** + *Returns squared max distance between two bounding boxes. This is furthest distance between points on the two envelopes. + * + *@param other The bounding box to calculate the max distance two. + *@return Squared distance value. + */ + public double sqrMaxDistance(Envelope2D other) { + if (isEmpty() || other.isEmpty()) + return NumberUtils.TheNaN; + + double dist = 0; + Point2D[] points = new Point2D[4]; + queryCorners(points); + Point2D[] points_o = new Point2D[4]; + other.queryCorners(points_o); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + double d = Point2D.sqrDistance(points[i], points_o[j]); + if (d > dist) { + dist = d; + } + } + } + + return dist; + } + /** * Calculates minimum squared distance from this envelope to the point. * Returns 0 for empty envelopes. diff --git a/src/main/java/com/esri/core/geometry/Envelope2DIntersectorImpl.java b/src/main/java/com/esri/core/geometry/Envelope2DIntersectorImpl.java index 98d7ad8c..35b83daa 100644 --- a/src/main/java/com/esri/core/geometry/Envelope2DIntersectorImpl.java +++ b/src/main/java/com/esri/core/geometry/Envelope2DIntersectorImpl.java @@ -43,7 +43,6 @@ void startConstruction() { m_elements_red = new AttributeStreamOfInt32(0); m_envelopes_red = new ArrayList(0); } else { - m_elements_red.resizePreserveCapacity(0); m_envelopes_red.clear(); } @@ -100,8 +99,7 @@ void endRedConstruction() { m_b_add_red = false; - if (m_envelopes_red != null && m_envelopes_red.size() > 0 - && m_envelopes_blue != null && m_envelopes_blue.size() > 0) { + if (m_envelopes_red != null && m_envelopes_red.size() > 0 && m_envelopes_blue != null && m_envelopes_blue.size() > 0) { if (m_function == -1) m_function = State.initializeRedBlue; else if (m_function == State.initializeBlue) @@ -142,8 +140,7 @@ void endBlueConstruction() { m_b_add_blue = false; - if (m_envelopes_red != null && m_envelopes_red.size() > 0 - && m_envelopes_blue != null && m_envelopes_blue.size() > 0) { + if (m_envelopes_red != null && m_envelopes_red.size() > 0 && m_envelopes_blue != null && m_envelopes_blue.size() > 0) { if (m_function == -1) m_function = State.initializeRedBlue; else if (m_function == State.initializeRed) @@ -255,6 +252,20 @@ void setTolerance(double tolerance) { m_tolerance = tolerance; } + /* + * Returns a reference to the envelope at the given handle. Use this for the red/red intersection case. + */ + Envelope2D getEnvelope(int handle) { + return m_envelopes_red.get(handle); + } + + /* + * Returns the user element associated with handle. Use this for the red/red intersection case. + */ + int getElement(int handle) { + return m_elements_red.read(handle); + } + /* * Returns a reference to the red envelope at handle_a. */ @@ -279,7 +290,6 @@ int getRedElement(int handle_a) { /* * Returns the user element associated with handle_b. */ - int getBlueElement(int handle_b) { return m_elements_blue.read(handle_b); } @@ -311,8 +321,7 @@ int getBlueElement(int handle_b) { private boolean m_b_add_red; private boolean m_b_add_blue; private boolean m_b_add_red_red; - - boolean m_b_done; + private boolean m_b_done; private static boolean isTop_(int y_end_point_handle) { return (y_end_point_handle & 0x1) == 1; @@ -345,16 +354,14 @@ private boolean initialize_() { if (m_interval_tree_red == null) { m_interval_tree_red = new IntervalTreeImpl(true); - m_iterator_red = m_interval_tree_red.getIterator(); m_sorted_end_indices_red = new AttributeStreamOfInt32(0); } - m_interval_tree_red.startConstruction(); - for (int i = 0; i < m_envelopes_red.size(); i++) { - Envelope2D env = m_envelopes_red.get(i); - m_interval_tree_red.addInterval(env.xmin, env.xmax); + m_interval_tree_red.addEnvelopesRef(m_envelopes_red); + + if (m_iterator_red == null) { + m_iterator_red = m_interval_tree_red.getIterator(); } - m_interval_tree_red.endConstruction(); m_sorted_end_indices_red.reserve(2 * m_envelopes_red.size()); m_sorted_end_indices_red.resize(0); @@ -362,8 +369,7 @@ private boolean initialize_() { for (int i = 0; i < 2 * m_envelopes_red.size(); i++) m_sorted_end_indices_red.add(i); - sortYEndIndices_(m_sorted_end_indices_red, 0, - 2 * m_envelopes_red.size(), true); + sortYEndIndices_(m_sorted_end_indices_red, 0, 2 * m_envelopes_red.size(), true); m_sweep_index_red = 2 * m_envelopes_red.size(); @@ -384,16 +390,14 @@ private boolean initializeRed_() { if (m_interval_tree_red == null) { m_interval_tree_red = new IntervalTreeImpl(true); - m_iterator_red = m_interval_tree_red.getIterator(); m_sorted_end_indices_red = new AttributeStreamOfInt32(0); } - m_interval_tree_red.startConstruction(); - for (int i = 0; i < m_envelopes_red.size(); i++) { - Envelope2D env = m_envelopes_red.get(i); - m_interval_tree_red.addInterval(env.xmin, env.xmax); + m_interval_tree_red.addEnvelopesRef(m_envelopes_red); + + if (m_iterator_red == null) { + m_iterator_red = m_interval_tree_red.getIterator(); } - m_interval_tree_red.endConstruction(); m_sorted_end_indices_red.reserve(2 * m_envelopes_red.size()); m_sorted_end_indices_red.resize(0); @@ -401,8 +405,7 @@ private boolean initializeRed_() { for (int i = 0; i < 2 * m_envelopes_red.size(); i++) m_sorted_end_indices_red.add(i); - sortYEndIndices_(m_sorted_end_indices_red, 0, - m_sorted_end_indices_red.size(), true); + sortYEndIndices_(m_sorted_end_indices_red, 0, m_sorted_end_indices_red.size(), true); m_sweep_index_red = m_sorted_end_indices_red.size(); if (m_queued_list_red != -1) { @@ -428,16 +431,14 @@ private boolean initializeBlue_() { if (m_interval_tree_blue == null) { m_interval_tree_blue = new IntervalTreeImpl(true); - m_iterator_blue = m_interval_tree_blue.getIterator(); m_sorted_end_indices_blue = new AttributeStreamOfInt32(0); } - m_interval_tree_blue.startConstruction(); - for (int i = 0; i < m_envelopes_blue.size(); i++) { - Envelope2D env = m_envelopes_blue.get(i); - m_interval_tree_blue.addInterval(env.xmin, env.xmax); + m_interval_tree_blue.addEnvelopesRef(m_envelopes_blue); + + if (m_iterator_blue == null) { + m_iterator_blue = m_interval_tree_blue.getIterator(); } - m_interval_tree_blue.endConstruction(); m_sorted_end_indices_blue.reserve(2 * m_envelopes_blue.size()); m_sorted_end_indices_blue.resize(0); @@ -445,8 +446,7 @@ private boolean initializeBlue_() { for (int i = 0; i < 2 * m_envelopes_blue.size(); i++) m_sorted_end_indices_blue.add(i); - sortYEndIndices_(m_sorted_end_indices_blue, 0, - m_sorted_end_indices_blue.size(), false); + sortYEndIndices_(m_sorted_end_indices_blue, 0, m_sorted_end_indices_blue.size(), false); m_sweep_index_blue = m_sorted_end_indices_blue.size(); if (m_queued_list_blue != -1) { @@ -472,29 +472,24 @@ private boolean initializeRedBlue_() { if (m_interval_tree_red == null) { m_interval_tree_red = new IntervalTreeImpl(true); - m_iterator_red = m_interval_tree_red.getIterator(); m_sorted_end_indices_red = new AttributeStreamOfInt32(0); } if (m_interval_tree_blue == null) { m_interval_tree_blue = new IntervalTreeImpl(true); - m_iterator_blue = m_interval_tree_blue.getIterator(); m_sorted_end_indices_blue = new AttributeStreamOfInt32(0); } - m_interval_tree_red.startConstruction(); - for (int i = 0; i < m_envelopes_red.size(); i++) { - Envelope2D env = m_envelopes_red.get(i); - m_interval_tree_red.addInterval(env.xmin, env.xmax); + m_interval_tree_red.addEnvelopesRef(m_envelopes_red); + m_interval_tree_blue.addEnvelopesRef(m_envelopes_blue); + + if (m_iterator_red == null) { + m_iterator_red = m_interval_tree_red.getIterator(); } - m_interval_tree_red.endConstruction(); - m_interval_tree_blue.startConstruction(); - for (int i = 0; i < m_envelopes_blue.size(); i++) { - Envelope2D env = m_envelopes_blue.get(i); - m_interval_tree_blue.addInterval(env.xmin, env.xmax); + if (m_iterator_blue == null) { + m_iterator_blue = m_interval_tree_blue.getIterator(); } - m_interval_tree_blue.endConstruction(); m_sorted_end_indices_red.reserve(2 * m_envelopes_red.size()); m_sorted_end_indices_blue.reserve(2 * m_envelopes_blue.size()); @@ -507,10 +502,8 @@ private boolean initializeRedBlue_() { for (int i = 0; i < 2 * m_envelopes_blue.size(); i++) m_sorted_end_indices_blue.add(i); - sortYEndIndices_(m_sorted_end_indices_red, 0, - m_sorted_end_indices_red.size(), true); - sortYEndIndices_(m_sorted_end_indices_blue, 0, - m_sorted_end_indices_blue.size(), false); + sortYEndIndices_(m_sorted_end_indices_red, 0, m_sorted_end_indices_red.size(), true); + sortYEndIndices_(m_sorted_end_indices_blue, 0, m_sorted_end_indices_blue.size(), false); m_sweep_index_red = m_sorted_end_indices_red.size(); m_sweep_index_blue = m_sorted_end_indices_blue.size(); @@ -533,8 +526,7 @@ private boolean initializeRedBlue_() { } private boolean sweep_() { - int y_end_point_handle = m_sorted_end_indices_red - .get(--m_sweep_index_red); + int y_end_point_handle = m_sorted_end_indices_red.get(--m_sweep_index_red); int envelope_handle = y_end_point_handle >> 1; if (isBottom_(y_end_point_handle)) { @@ -550,17 +542,14 @@ private boolean sweep_() { return true; } - m_iterator_red.resetIterator(m_envelopes_red.get(envelope_handle).xmin, - m_envelopes_red.get(envelope_handle).xmax, m_tolerance); + m_iterator_red.resetIterator(m_envelopes_red.get(envelope_handle).xmin, m_envelopes_red.get(envelope_handle).xmax, m_tolerance); m_envelope_handle_a = envelope_handle; m_function = State.iterate; return true; } - private boolean sweepBruteForce_() {// this isn't really a sweep, it just - // walks along the array of red - // envelopes backward. + private boolean sweepBruteForce_() {// this isn't really a sweep, it just walks along the array of red envelopes backward. if (--m_sweep_index_red == -1) { m_envelope_handle_a = -1; m_envelope_handle_b = -1; @@ -575,9 +564,7 @@ private boolean sweepBruteForce_() {// this isn't really a sweep, it just return true; } - private boolean sweepRedBlueBruteForce_() {// this isn't really a sweep, it - // just walks along the array of - // red envelopes backward. + private boolean sweepRedBlueBruteForce_() {// this isn't really a sweep, it just walks along the array of red envelopes backward. if (--m_sweep_index_red == -1) { m_envelope_handle_a = -1; m_envelope_handle_b = -1; @@ -592,13 +579,9 @@ private boolean sweepRedBlueBruteForce_() {// this isn't really a sweep, it return true; } - private boolean sweepRedBlue_() {// controls whether we want to sweep the - // red envelopes or sweep the blue - // envelopes - int y_end_point_handle_red = m_sorted_end_indices_red - .get(m_sweep_index_red - 1); - int y_end_point_handle_blue = m_sorted_end_indices_blue - .get(m_sweep_index_blue - 1); + private boolean sweepRedBlue_() {// controls whether we want to sweep the red envelopes or sweep the blue envelopes + int y_end_point_handle_red = m_sorted_end_indices_red.get(m_sweep_index_red - 1); + int y_end_point_handle_blue = m_sorted_end_indices_blue.get(m_sweep_index_blue - 1); double y_red = getAdjustedValue_(y_end_point_handle_red, true); double y_blue = getAdjustedValue_(y_end_point_handle_blue, false); @@ -613,20 +596,16 @@ private boolean sweepRedBlue_() {// controls whether we want to sweep the if (isTop_(y_end_point_handle_blue)) return sweepBlue_(); - return sweepRed_(); // arbitrary. can call sweep_blue_ instead and would - // also work correctly + return sweepRed_(); // arbitrary. can call sweep_blue_ instead and would also work correctly } private boolean sweepRed_() { - int y_end_point_handle_red = m_sorted_end_indices_red - .get(--m_sweep_index_red); + int y_end_point_handle_red = m_sorted_end_indices_red.get(--m_sweep_index_red); int envelope_handle_red = y_end_point_handle_red >> 1; if (isBottom_(y_end_point_handle_red)) { - if (m_queued_list_red != -1 - && m_queued_indices_red.get(envelope_handle_red) != -1) { - m_queued_envelopes.deleteElement(m_queued_list_red, - m_queued_indices_red.get(envelope_handle_red)); + if (m_queued_list_red != -1 && m_queued_indices_red.get(envelope_handle_red) != -1) { + m_queued_envelopes.deleteElement(m_queued_list_red, m_queued_indices_red.get(envelope_handle_red)); m_queued_indices_red.set(envelope_handle_red, -1); } else m_interval_tree_red.remove(envelope_handle_red); @@ -641,8 +620,7 @@ private boolean sweepRed_() { return true; } - if (m_queued_list_blue != -1 - && m_queued_envelopes.getListSize(m_queued_list_blue) > 0) { + if (m_queued_list_blue != -1 && m_queued_envelopes.getListSize(m_queued_list_blue) > 0) { int node = m_queued_envelopes.getFirst(m_queued_list_blue); while (node != -1) { int e = m_queued_envelopes.getData(node); @@ -655,9 +633,7 @@ private boolean sweepRed_() { } if (m_interval_tree_blue.size() > 0) { - m_iterator_blue.resetIterator( - m_envelopes_red.get(envelope_handle_red).xmin, - m_envelopes_red.get(envelope_handle_red).xmax, m_tolerance); + m_iterator_blue.resetIterator(m_envelopes_red.get(envelope_handle_red).xmin, m_envelopes_red.get(envelope_handle_red).xmax, m_tolerance); m_envelope_handle_a = envelope_handle_red; m_function = State.iterateBlue; } else { @@ -671,8 +647,7 @@ private boolean sweepRed_() { m_queued_list_red = m_queued_envelopes.createList(1); } - m_queued_indices_red.set(envelope_handle_red, m_queued_envelopes - .addElement(m_queued_list_red, envelope_handle_red)); + m_queued_indices_red.set(envelope_handle_red, m_queued_envelopes.addElement(m_queued_list_red, envelope_handle_red)); m_function = State.sweepRedBlue; } @@ -680,15 +655,12 @@ private boolean sweepRed_() { } private boolean sweepBlue_() { - int y_end_point_handle_blue = m_sorted_end_indices_blue - .get(--m_sweep_index_blue); + int y_end_point_handle_blue = m_sorted_end_indices_blue.get(--m_sweep_index_blue); int envelope_handle_blue = y_end_point_handle_blue >> 1; if (isBottom_(y_end_point_handle_blue)) { - if (m_queued_list_blue != -1 - && m_queued_indices_blue.get(envelope_handle_blue) != -1) { - m_queued_envelopes.deleteElement(m_queued_list_blue, - m_queued_indices_blue.get(envelope_handle_blue)); + if (m_queued_list_blue != -1 && m_queued_indices_blue.get(envelope_handle_blue) != -1) { + m_queued_envelopes.deleteElement(m_queued_list_blue, m_queued_indices_blue.get(envelope_handle_blue)); m_queued_indices_blue.set(envelope_handle_blue, -1); } else m_interval_tree_blue.remove(envelope_handle_blue); @@ -703,8 +675,7 @@ private boolean sweepBlue_() { return true; } - if (m_queued_list_red != -1 - && m_queued_envelopes.getListSize(m_queued_list_red) > 0) { + if (m_queued_list_red != -1 && m_queued_envelopes.getListSize(m_queued_list_red) > 0) { int node = m_queued_envelopes.getFirst(m_queued_list_red); while (node != -1) { int e = m_queued_envelopes.getData(node); @@ -717,10 +688,7 @@ private boolean sweepBlue_() { } if (m_interval_tree_red.size() > 0) { - m_iterator_red.resetIterator( - m_envelopes_blue.get(envelope_handle_blue).xmin, - m_envelopes_blue.get(envelope_handle_blue).xmax, - m_tolerance); + m_iterator_red.resetIterator(m_envelopes_blue.get(envelope_handle_blue).xmin, m_envelopes_blue.get(envelope_handle_blue).xmax, m_tolerance); m_envelope_handle_b = envelope_handle_blue; m_function = State.iterateRed; } else { @@ -734,8 +702,7 @@ private boolean sweepBlue_() { m_queued_list_blue = m_queued_envelopes.createList(0); } - m_queued_indices_blue.set(envelope_handle_blue, m_queued_envelopes - .addElement(m_queued_list_blue, envelope_handle_blue)); + m_queued_indices_blue.set(envelope_handle_blue, m_queued_envelopes.addElement(m_queued_list_blue, envelope_handle_blue)); m_function = State.sweepRedBlue; } @@ -762,8 +729,7 @@ private boolean iterateRed_() { m_envelope_handle_a = -1; m_envelope_handle_b = -1; - int envelope_handle_blue = m_sorted_end_indices_blue - .get(m_sweep_index_blue) >> 1; + int envelope_handle_blue = m_sorted_end_indices_blue.get(m_sweep_index_blue) >> 1; m_interval_tree_blue.insert(envelope_handle_blue); m_function = State.sweepRedBlue; @@ -775,8 +741,7 @@ private boolean iterateBlue_() { if (m_envelope_handle_b != -1) return false; - int envelope_handle_red = m_sorted_end_indices_red - .get(m_sweep_index_red) >> 1; + int envelope_handle_red = m_sorted_end_indices_red.get(m_sweep_index_red) >> 1; m_interval_tree_red.insert(envelope_handle_red); m_function = State.sweepRedBlue; @@ -886,18 +851,15 @@ private interface State { // *********** Helpers for Bucket sort************** private BucketSort m_bucket_sort; - private void sortYEndIndices_(AttributeStreamOfInt32 end_indices, - int begin_, int end_, boolean b_red) { + private void sortYEndIndices_(AttributeStreamOfInt32 end_indices, int begin_, int end_, boolean b_red) { if (m_bucket_sort == null) m_bucket_sort = new BucketSort(); - Envelope2DBucketSortHelper sorter = new Envelope2DBucketSortHelper( - this, b_red); + Envelope2DBucketSortHelper sorter = new Envelope2DBucketSortHelper(this, b_red); m_bucket_sort.sort(end_indices, begin_, end_, sorter); } - private void sortYEndIndicesHelper_(AttributeStreamOfInt32 end_indices, - int begin_, int end_, boolean b_red) { + private void sortYEndIndicesHelper_(AttributeStreamOfInt32 end_indices, int begin_, int end_, boolean b_red) { end_indices.Sort(begin_, end_, new EndPointsComparer(this, b_red)); } @@ -905,19 +867,17 @@ private double getAdjustedValue_(int e, boolean b_red) { double dy = 0.5 * m_tolerance; if (b_red) { Envelope2D envelope_red = m_envelopes_red.get(e >> 1); - double y = (isBottom_(e) ? envelope_red.ymin - dy - : envelope_red.ymax + dy); + double y = (isBottom_(e) ? envelope_red.ymin - dy : envelope_red.ymax + dy); return y; } Envelope2D envelope_blue = m_envelopes_blue.get(e >> 1); - double y = (isBottom_(e) ? envelope_blue.ymin - dy : envelope_blue.ymax - + dy); + double y = (isBottom_(e) ? envelope_blue.ymin - dy : envelope_blue.ymax + dy); return y; } - private static final class EndPointsComparer extends - AttributeStreamOfInt32.IntComparator {// For user sort + private static final class EndPointsComparer extends AttributeStreamOfInt32.IntComparator {// For user sort + EndPointsComparer(Envelope2DIntersectorImpl intersector, boolean b_red) { m_intersector = intersector; m_b_red = b_red; @@ -939,10 +899,10 @@ public int compare(int e_1, int e_2) { } private static final class Envelope2DBucketSortHelper extends ClassicSort {// For - // bucket - // sort - Envelope2DBucketSortHelper(Envelope2DIntersectorImpl intersector, - boolean b_red) { + + // bucket + // sort + Envelope2DBucketSortHelper(Envelope2DIntersectorImpl intersector, boolean b_red) { m_intersector = intersector; m_b_red = b_red; } diff --git a/src/main/java/com/esri/core/geometry/Envelope3D.java b/src/main/java/com/esri/core/geometry/Envelope3D.java index c1960fd6..3f64b053 100644 --- a/src/main/java/com/esri/core/geometry/Envelope3D.java +++ b/src/main/java/com/esri/core/geometry/Envelope3D.java @@ -47,20 +47,23 @@ public final class Envelope3D implements Serializable{ public static Envelope3D construct(double _xmin, double _ymin, double _zmin, double _xmax, double _ymax, double _zmax) { - Envelope3D env = new Envelope3D(); - env.xmin = _xmin; - env.ymin = _ymin; - env.zmin = _zmin; - env.xmax = _xmax; - env.ymax = _ymax; - env.zmax = _zmax; + Envelope3D env = new Envelope3D(_xmin, _ymin, _zmin, _xmax, _ymax, _zmax); return env; } + public Envelope3D(double _xmin, double _ymin, double _zmin, double _xmax, double _ymax, double _zmax) { + setCoords(_xmin, _ymin, _zmin, _xmax, _ymax, _zmax); + } + public Envelope3D() { } + public Envelope3D(Envelope3D other) { + setCoords(other); + } + + public void setInfinite() { xmin = NumberUtils.negativeInf(); xmax = NumberUtils.positiveInf(); @@ -72,7 +75,11 @@ public void setInfinite() { public void setEmpty() { xmin = NumberUtils.NaN(); + ymin = NumberUtils.NaN(); zmin = NumberUtils.NaN(); + xmax = 0; + ymax = 0; + zmax = 0; } public boolean isEmpty() { @@ -99,6 +106,7 @@ public void setCoords(double _xmin, double _ymin, double _zmin, xmax = _xmax; ymax = _ymax; zmax = _zmax; + normalize(); } public void setCoords(double _x, double _y, double _z) { @@ -118,6 +126,24 @@ public void setCoords(Point3D center, double width, double height, ymax = ymin + height; zmin = center.z - depth * 0.5; zmax = zmin + depth; + normalize(); + } + + public void setCoords(Envelope3D envSrc) { + + setCoords(envSrc.xmin, envSrc.ymin, envSrc.zmin, envSrc.xmax, envSrc.ymax, envSrc.zmax); + } + + public double getWidth() { + return xmax - xmin; + } + + public double getHeight() { + return ymax - ymin; + } + + public double getDepth() { + return zmax - zmin; } public void move(Point3D vector) { @@ -129,6 +155,24 @@ public void move(Point3D vector) { zmax += vector.z; } + public void normalize() { + if (isEmpty()) + return; + + double min = Math.min(xmin, xmax); + double max = Math.max(xmin, xmax); + xmin = min; + xmax = max; + min = Math.min(ymin, ymax); + max = Math.max(ymin, ymax); + ymin = min; + ymax = max; + min = Math.min(zmin, zmax); + max = Math.max(zmin, zmax); + zmin = min; + zmax = max; + } + public void copyTo(Envelope2D env) { env.xmin = xmin; env.ymin = ymin; @@ -189,6 +233,93 @@ public void merge(double x1, double y1, double z1, double x2, double y2, merge(x2, y2, z2); } + public void inflate(double dx, double dy, double dz) { + if (isEmpty()) + return; + xmin -= dx; + xmax += dx; + ymin -= dy; + ymax += dy; + zmin -= dz; + zmax += dz; + if (xmin > xmax || ymin > ymax || zmin > zmax) + setEmpty(); + } + + /** + * Checks if this envelope intersects the other. + * + * @return True if this envelope intersects the other. + */ + public boolean isIntersecting(Envelope3D other) { + return !isEmpty() && !other.isEmpty() && ((xmin <= other.xmin) ? xmax >= other.xmin : other.xmax >= xmin) && // check that x projections overlap + ((ymin <= other.ymin) ? ymax >= other.ymin : other.ymax >= ymin) && // check that y projections overlap + ((zmin <= other.zmin) ? zmax >= other.zmin : other.zmax >= zmin); // check that z projections overlap + } + + /** + * Intersects this envelope with the other and stores result in this + * envelope. + * + * @return True if this envelope intersects the other, otherwise sets this + * envelope to empty state and returns False. + */ + public boolean intersect(Envelope3D other) { + if (isEmpty() || other.isEmpty()) + return false; + + if (other.xmin > xmin) + xmin = other.xmin; + + if (other.xmax < xmax) + xmax = other.xmax; + + if (other.ymin > ymin) + ymin = other.ymin; + + if (other.ymax < ymax) + ymax = other.ymax; + + if (other.zmin > zmin) + zmin = other.zmin; + + if (other.zmax < zmax) + zmax = other.zmax; + + boolean bIntersecting = xmin <= xmax && ymin <= ymax && zmin <= zmax; + + if (!bIntersecting) + setEmpty(); + + return bIntersecting; + } + + /** + * Returns True if the envelope contains the other envelope (boundary + * inclusive). + */ + public boolean contains(Envelope3D other) {// Note: Will return False, if either envelope is empty. + return other.xmin >= xmin && other.xmax <= xmax && other.ymin >= ymin && other.ymax <= ymax && other.zmin >= zmin && other.zmax <= zmax; + } + + @Override + public boolean equals(Object _other) { + if (_other == this) + return true; + + if (!(_other instanceof Envelope3D)) + return false; + + Envelope3D other = (Envelope3D) _other; + if (isEmpty() && other.isEmpty()) + return true; + + if (xmin != other.xmin || ymin != other.ymin || zmin != other.zmin || xmax != other.xmax || ymax != other.ymax || zmax != other.zmax) + return false; + + return true; + } + public void construct(Envelope1D xinterval, Envelope1D yinterval, Envelope1D zinterval) { if (xinterval.isEmpty() || yinterval.isEmpty()) { diff --git a/src/main/java/com/esri/core/geometry/GenericGeometrySerializer.java b/src/main/java/com/esri/core/geometry/GenericGeometrySerializer.java new file mode 100644 index 00000000..c6bd2d09 --- /dev/null +++ b/src/main/java/com/esri/core/geometry/GenericGeometrySerializer.java @@ -0,0 +1,94 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +//This is a writeReplace class for MultiPoint, Polyline, and Polygon +public class GenericGeometrySerializer implements Serializable { + private static final long serialVersionUID = 1L; + int geometryType; + byte[] esriShape = null; + int simpleFlag = 0; + double tolerance = 0; + boolean[] ogcFlags = null; + + public Object readResolve() throws ObjectStreamException { + Geometry geometry = null; + try { + geometry = GeometryEngine.geometryFromEsriShape( + esriShape, Geometry.Type.intToType(geometryType)); + + if (Geometry.isMultiVertex(geometryType)) { + MultiVertexGeometryImpl mvImpl = (MultiVertexGeometryImpl) geometry + ._getImpl(); + if (!geometry.isEmpty() + && Geometry.isMultiPath(geometryType)) { + MultiPathImpl mpImpl = (MultiPathImpl) geometry._getImpl(); + AttributeStreamOfInt8 pathFlags = mpImpl + .getPathFlagsStreamRef(); + for (int i = 0, n = mpImpl.getPathCount(); i < n; i++) { + if (ogcFlags[i]) + pathFlags.setBits(i, + (byte) PathFlags.enumOGCStartPolygon); + } + } + mvImpl.setIsSimple(simpleFlag, tolerance, false); + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot read geometry from stream"); + } + return geometry; + } + + public void setGeometryByValue(Geometry geometry) + throws ObjectStreamException { + try { + esriShape = GeometryEngine + .geometryToEsriShape(geometry); + geometryType = geometry.getType().value(); + if (Geometry.isMultiVertex(geometryType)) { + MultiVertexGeometryImpl mvImpl = (MultiVertexGeometryImpl) geometry + ._getImpl(); + tolerance = mvImpl.m_simpleTolerance; + simpleFlag = mvImpl.getIsSimple(0); + if (!geometry.isEmpty() + && Geometry.isMultiPath(geometryType)) { + MultiPathImpl mpImpl = (MultiPathImpl) geometry._getImpl(); + ogcFlags = new boolean[mpImpl.getPathCount()]; + AttributeStreamOfInt8 pathFlags = mpImpl + .getPathFlagsStreamRef(); + for (int i = 0, n = mpImpl.getPathCount(); i < n; i++) { + ogcFlags[i] = (pathFlags.read(i) & (byte) PathFlags.enumOGCStartPolygon) != 0; + } + } + + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot serialize this geometry"); + } + } +} diff --git a/src/main/java/com/esri/core/geometry/GeoDist.java b/src/main/java/com/esri/core/geometry/GeoDist.java index 108e04e0..b7845151 100644 --- a/src/main/java/com/esri/core/geometry/GeoDist.java +++ b/src/main/java/com/esri/core/geometry/GeoDist.java @@ -257,7 +257,7 @@ static public void geodesic_distance_ngs(double a, double e2, double lam1, /* top of the long-line loop (kind = 1) */ q_continue_looping = true; - while (q_continue_looping == true) { + while (q_continue_looping && it < 100) { it = it + 1; if (kind == 1) { diff --git a/src/main/java/com/esri/core/geometry/GeoJsonCrsTables.java b/src/main/java/com/esri/core/geometry/GeoJsonCrsTables.java new file mode 100644 index 00000000..aa35f20d --- /dev/null +++ b/src/main/java/com/esri/core/geometry/GeoJsonCrsTables.java @@ -0,0 +1,183 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +import java.util.Arrays; + +class GeoJsonCrsTables { + static int getWkidFromCrsShortForm(String crs_identifier) { + int last_colon = crs_identifier.lastIndexOf((int) ':'); // skip version + + if (last_colon == -1) + return -1; + + int code_start = last_colon + 1; + int wkid = getWkidFromCrsCode_(crs_identifier, code_start); + return wkid; + } + + static int getWkidFromCrsName(String crs_identifier) { + int wkid = -1; + + int last_colon = crs_identifier.lastIndexOf((int) ':'); // skip + // authority, + // version, and + // other things. + // Just try to + // get a wkid. + // This works + // for + // short/long + // form. + + if (last_colon == -1) + return -1; + + int code_start = last_colon + 1; + wkid = getWkidFromCrsCode_(crs_identifier, code_start); + + if (wkid != -1) + return wkid; + + wkid = getWkidFromCrsOgcUrn(crs_identifier); // could be an OGC + // "preferred" urn + return wkid; + } + + static int getWkidFromCrsOgcUrn(String crs_identifier) { + int wkid = -1; + if (crs_identifier.regionMatches(0, "urn:ogc:def:crs:OGC", 0, 19)) + wkid = getWkidFromCrsOgcUrn_(crs_identifier); + + return wkid; + } + + private static int getWkidFromCrsCode_(String crs_identifier, int code_start) { + assert(code_start > 0); + + int wkid = -1; + int code_count = crs_identifier.length() - code_start; + + try { + wkid = Integer.parseInt(crs_identifier.substring(code_start, code_start + code_count)); + } catch (Exception e) { + } + + return (int) wkid; + } + + private static int getWkidFromCrsOgcUrn_(String crs_identifier) { + assert(crs_identifier.regionMatches(0, "urn:ogc:def:crs:OGC", 0, 19)); + + int last_colon = crs_identifier.lastIndexOf((int) ':'); // skip version + + if (last_colon == -1) + return -1; + + int ogc_code_start = last_colon + 1; + int ogc_code_count = crs_identifier.length() - ogc_code_start; + + if (crs_identifier.regionMatches(ogc_code_start, "CRS84", 0, ogc_code_count)) + return 4326; + + if (crs_identifier.regionMatches(ogc_code_start, "CRS83", 0, ogc_code_count)) + return 4269; + + if (crs_identifier.regionMatches(ogc_code_start, "CRS27", 0, ogc_code_count)) + return 4267; + + return -1; + } + + static int getWkidFromCrsHref(String crs_identifier) { + int sr_org_code_start = -1; + + if (crs_identifier.regionMatches(0, "http://spatialreference.org/ref/epsg/", 0, 37)) + sr_org_code_start = 37; + else if (crs_identifier.regionMatches(0, "www.spatialreference.org/ref/epsg/", 0, 34)) + sr_org_code_start = 34; + else if (crs_identifier.regionMatches(0, "http://www.spatialreference.org/ref/epsg/", 0, 41)) + sr_org_code_start = 41; + + if (sr_org_code_start != -1) { + int sr_org_code_end = crs_identifier.indexOf('/', sr_org_code_start); + + if (sr_org_code_end == -1) + return -1; + + int count = sr_org_code_end - sr_org_code_start; + int wkid = -1; + + try { + wkid = Integer.parseInt(crs_identifier.substring(sr_org_code_start, sr_org_code_start + count)); + } catch (Exception e) { + } + + return wkid; + } + + int open_gis_epsg_slash_end = -1; + + if (crs_identifier.regionMatches(0, "http://opengis.net/def/crs/EPSG/", 0, 32)) + open_gis_epsg_slash_end = 32; + else if (crs_identifier.regionMatches(0, "www.opengis.net/def/crs/EPSG/", 0, 29)) + open_gis_epsg_slash_end = 29; + else if (crs_identifier.regionMatches(0, "http://www.opengis.net/def/crs/EPSG/", 0, 36)) + open_gis_epsg_slash_end = 36; + + if (open_gis_epsg_slash_end != -1) { + int last_slash = crs_identifier.lastIndexOf('/'); // skip over the + // "0/" + + if (last_slash == -1) + return -1; + + int open_gis_code_start = last_slash + 1; + + int count = crs_identifier.length() - open_gis_code_start; + int wkid = -1; + + try { + wkid = Integer.parseInt(crs_identifier.substring(open_gis_code_start, open_gis_code_start + count)); + } catch (Exception e) { + } + + return wkid; + } + + if (crs_identifier.compareToIgnoreCase("http://spatialreference.org/ref/sr-org/6928/ogcwkt/") == 0) + return 3857; + + return -1; + } + + static String getWktFromCrsName(String crs_identifier) { + int last_colon = crs_identifier.lastIndexOf((int) ':'); // skip + // authority + int wkt_start = last_colon + 1; + int wkt_count = crs_identifier.length() - wkt_start; + String wkt = crs_identifier.substring(wkt_start, wkt_start + wkt_count); + return wkt; + } +} diff --git a/src/main/java/com/esri/core/geometry/GeoJsonExportFlags.java b/src/main/java/com/esri/core/geometry/GeoJsonExportFlags.java index 027bf2ac..6290a0f6 100644 --- a/src/main/java/com/esri/core/geometry/GeoJsonExportFlags.java +++ b/src/main/java/com/esri/core/geometry/GeoJsonExportFlags.java @@ -24,6 +24,31 @@ package com.esri.core.geometry; public interface GeoJsonExportFlags { - static final int geoJsonExportDefaults = 0; - static final int geoJsonExportPreferMultiGeometry = 1; + public static final int geoJsonExportDefaults = 0; + /** + * Export MultiXXX geometries every time, by default it will export the minimum required type. + */ + public static final int geoJsonExportPreferMultiGeometry = 1; + public static final int geoJsonExportStripZs = 2; + public static final int geoJsonExportStripMs = 4; + public static final int geoJsonExportSkipCRS = 8; + public static final int geoJsonExportFailIfNotSimple = 16; + public static final int geoJsonExportPrecision16 = 0x02000; + public static final int geoJsonExportPrecision15 = 0x04000; + public static final int geoJsonExportPrecision14 = 0x06000; + public static final int geoJsonExportPrecision13 = 0x08000; + public static final int geoJsonExportPrecision12 = 0x0a000; + public static final int geoJsonExportPrecision11 = 0x0c000; + public static final int geoJsonExportPrecision10 = 0x0e000; + public static final int geoJsonExportPrecision9 = 0x10000; + public static final int geoJsonExportPrecision8 = 0x12000; + public static final int geoJsonExportPrecision7 = 0x14000; + public static final int geoJsonExportPrecision6 = 0x16000; + public static final int geoJsonExportPrecision5 = 0x18000; + public static final int geoJsonExportPrecision4 = 0x1a000; + public static final int geoJsonExportPrecision3 = 0x1c000; + public static final int geoJsonExportPrecision2 = 0x1e000; + public static final int geoJsonExportPrecision1 = 0x20000; + public static final int geoJsonExportPrecision0 = 0x22000; + public static final int geoJsonExportPrecisionFixedPoint = 0x40000; } diff --git a/src/main/java/com/esri/core/geometry/GeoJsonImportFlags.java b/src/main/java/com/esri/core/geometry/GeoJsonImportFlags.java index a464d20b..5245bd96 100644 --- a/src/main/java/com/esri/core/geometry/GeoJsonImportFlags.java +++ b/src/main/java/com/esri/core/geometry/GeoJsonImportFlags.java @@ -24,6 +24,15 @@ package com.esri.core.geometry; public interface GeoJsonImportFlags { - static final int geoJsonImportDefaults = 0; - static final int geoJsonImportNonTrusted = 2; + public static final int geoJsonImportDefaults = 0; + @Deprecated static final int geoJsonImportNonTrusted = 2; + /** + * If set, the import will skip CRS. + */ + public static final int geoJsonImportSkipCRS = 8; + /** + * If set, and the geojson does not have a spatial reference, the result geometry will not have one too, otherwise + * it'll assume WGS84. + */ + public static final int geoJsonImportNoWGS84Default = 16; } diff --git a/src/main/java/com/esri/core/geometry/Geometry.java b/src/main/java/com/esri/core/geometry/Geometry.java index e5d08afa..68e93671 100644 --- a/src/main/java/com/esri/core/geometry/Geometry.java +++ b/src/main/java/com/esri/core/geometry/Geometry.java @@ -35,10 +35,6 @@ * objects that define a spatial location and and associated geometric shape. */ public abstract class Geometry implements Serializable { - // Note: We use writeReplace with GeometrySerializer. This field is - // irrelevant. Need to be removed after final. - private static final long serialVersionUID = 2L; - VertexDescription m_description; volatile int m_touchFlag; @@ -117,6 +113,18 @@ public int value() { Type(int val) { enumValue = val; } + + static public Geometry.Type intToType(int geometryType) + { + Geometry.Type[] v = Geometry.Type.values(); + for(int i = 0; i < v.length; i++) + { + if(v[i].value() == geometryType) + return v[i]; + } + + throw new IllegalArgumentException(); + } } /** @@ -153,7 +161,7 @@ public VertexDescription getDescription() { * Assigns the new VertexDescription by adding or dropping attributes. The * Geometry will have the src description as a result. */ - void assignVertexDescription(VertexDescription src) { + public void assignVertexDescription(VertexDescription src) { _touch(); if (src == m_description) return; @@ -161,14 +169,14 @@ void assignVertexDescription(VertexDescription src) { _assignVertexDescriptionImpl(src); } - protected abstract void _assignVertexDescriptionImpl(VertexDescription src); + protected abstract void _assignVertexDescriptionImpl(VertexDescription src); /** * Merges the new VertexDescription by adding missing attributes from the * src. The Geometry will have a union of the current and the src * descriptions. */ - void mergeVertexDescription(VertexDescription src) { + public void mergeVertexDescription(VertexDescription src) { _touch(); if (src == m_description) return; @@ -566,7 +574,27 @@ static public enum GeometryAccelerationDegree { } Object writeReplace() throws ObjectStreamException { - GeometrySerializer geomSerializer = new GeometrySerializer(); + Type gt = getType(); + if (gt == Geometry.Type.Point) + { + PtSrlzr pt = new PtSrlzr(); + pt.setGeometryByValue((Point)this); + return pt; + } + else if (gt == Geometry.Type.Envelope) + { + EnvSrlzr e = new EnvSrlzr(); + e.setGeometryByValue((Envelope)this); + return e; + } + else if (gt == Geometry.Type.Line) + { + LnSrlzr ln = new LnSrlzr(); + ln.setGeometryByValue((Line)this); + return ln; + } + + GenericGeometrySerializer geomSerializer = new GenericGeometrySerializer(); geomSerializer.setGeometryByValue(this); return geomSerializer; } diff --git a/src/main/java/com/esri/core/geometry/GeometryEngine.java b/src/main/java/com/esri/core/geometry/GeometryEngine.java index e027dd6c..7bed73ea 100644 --- a/src/main/java/com/esri/core/geometry/GeometryEngine.java +++ b/src/main/java/com/esri/core/geometry/GeometryEngine.java @@ -242,6 +242,7 @@ public static Geometry geometryFromWkt(String wkt, int importFlags, * @throws GeometryException when the geometryType is not Geometry.Type.Unknown and the geoJson contains a geometry that cannot be converted to the given geometryType. * @throws IllegalArgument exception if an error is found while parsing the geoJson string. */ + @Deprecated public static MapGeometry geometryFromGeoJson(String geoJson, int importFlags, Geometry.Type geometryType) throws JSONException { OperatorImportFromGeoJson op = (OperatorImportFromGeoJson) factory diff --git a/src/main/java/com/esri/core/geometry/GeometrySerializer.java b/src/main/java/com/esri/core/geometry/GeometrySerializer.java index b91c16f7..a2fefa1e 100644 --- a/src/main/java/com/esri/core/geometry/GeometrySerializer.java +++ b/src/main/java/com/esri/core/geometry/GeometrySerializer.java @@ -27,6 +27,8 @@ import java.io.ObjectStreamException; import java.io.Serializable; +//Left here for backward compatibility. Use GenericGeometrySerializer instead +@Deprecated final class GeometrySerializer implements Serializable { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/esri/core/geometry/IntervalTreeImpl.java b/src/main/java/com/esri/core/geometry/IntervalTreeImpl.java index acdc10d8..f7248c47 100644 --- a/src/main/java/com/esri/core/geometry/IntervalTreeImpl.java +++ b/src/main/java/com/esri/core/geometry/IntervalTreeImpl.java @@ -26,504 +26,452 @@ import java.util.ArrayList; final class IntervalTreeImpl { - static final class IntervalTreeIteratorImpl { - /** - * Resets the iterator to a starting state on the Interval_tree_impl - * using the input Envelope_1D interval as the query \param query The - * Envelope_1D interval used for the query. \param tolerance The - * tolerance used for the intersection tests. - */ - void resetIterator(Envelope1D query, double tolerance) { - m_query.vmin = query.vmin - tolerance; - m_query.vmax = query.vmax + tolerance; - m_tertiary_stack.resize(0); - m_function_index = 0; - m_function_stack[0] = State.initialize; - } + private void sortEndIndices_(AttributeStreamOfInt32 end_indices, int begin_, int end_) { + IntervalTreeBucketSortHelper sorter = new IntervalTreeBucketSortHelper(this); + BucketSort bucket_sort = new BucketSort(); + bucket_sort.sort(end_indices, begin_, end_, sorter); + } - /** - * Resets the iterator to a starting state on the Interval_tree_impl - * using the input Envelope_1D interval as the query \param query The - * Envelope_1D interval used for the query. \param tolerance The - * tolerance used for the intersection tests. - */ - void resetIterator(double query_min, double query_max, double tolerance) { - if (query_min > query_max) - throw new IllegalArgumentException(); + private void sortEndIndicesHelper_(AttributeStreamOfInt32 end_indices, int begin_, int end_) { + end_indices.Sort(begin_, end_, new EndPointsComparer(this)); + } - m_query.vmin = query_min - tolerance; - m_query.vmax = query_max + tolerance; - m_tertiary_stack.resize(0); - m_function_index = 0; - m_function_stack[0] = State.initialize; + private double getValue_(int e) { + if (!m_b_envelopes_ref) { + Envelope1D interval = m_intervals.get(e >> 1); + double v = (isLeft_(e) ? interval.vmin : interval.vmax); + return v; } - /** - * Resets the iterator to a starting state on the Interval_tree_impl - * using the input double as the stabbing query \param query The double - * used for the query. \param tolerance The tolerance used for the - * intersection tests. - */ - void resetIterator(double query, double tolerance) { - m_query.vmin = query - tolerance; - m_query.vmax = query + tolerance; - m_tertiary_stack.resize(0); - m_function_index = 0; - m_function_stack[0] = State.initialize; + Envelope2D interval = m_envelopes_ref.get(e >> 1); + double v = (isLeft_(e) ? interval.xmin : interval.xmax); + return v; + } + + private static final class EndPointsComparer extends AttributeStreamOfInt32.IntComparator { // For user sort + + EndPointsComparer(IntervalTreeImpl interval_tree) { + m_interval_tree = interval_tree; } - /** - * Iterates over all intervals which interset the query interval. - * Returns an index to an interval that intersects the query. - */ - int next() { - if (!m_interval_tree.m_b_construction_ended) - throw new GeometryException("invalid call"); + @Override + public int compare(int e_1, int e_2) { + double v_1 = m_interval_tree.getValue_(e_1); + double v_2 = m_interval_tree.getValue_(e_2); - if (m_function_index < 0) + if (v_1 < v_2 || (v_1 == v_2 && isLeft_(e_1) && isRight_(e_2))) return -1; - boolean b_searching = true; - - while (b_searching) { - switch (m_function_stack[m_function_index]) { - case State.pIn: - b_searching = pIn_(); - break; - case State.pL: - b_searching = pL_(); - break; - case State.pR: - b_searching = pR_(); - break; - case State.pT: - b_searching = pT_(); - break; - case State.right: - b_searching = right_(); - break; - case State.left: - b_searching = left_(); - break; - case State.all: - b_searching = all_(); - break; - case State.initialize: - b_searching = initialize_(); - break; - default: - throw GeometryException.GeometryInternalError(); - } - } + return 1; + } - if (m_current_end_handle != -1) - return getCurrentEndIndex_() >> 1; + private IntervalTreeImpl m_interval_tree; + } - return -1; - } + private class IntervalTreeBucketSortHelper extends ClassicSort { // For bucket sort - // Creates an iterator on the input Interval_tree using the input - // Envelope_1D interval as the query. - IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree, - Envelope1D query, double tolerance) { + IntervalTreeBucketSortHelper(IntervalTreeImpl interval_tree) { m_interval_tree = interval_tree; - m_tertiary_stack.reserve(20); - resetIterator(query, tolerance); } - // Creates an iterator on the input Interval_tree using the input double - // as the stabbing query. - IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree, double query, - double tolerance) { - m_interval_tree = interval_tree; - m_tertiary_stack.reserve(20); - resetIterator(query, tolerance); + @Override + public void userSort(int begin, int end, AttributeStreamOfInt32 indices) { + m_interval_tree.sortEndIndicesHelper_(indices, begin, end); } - // Creates an iterator on the input Interval_tree. - IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree) { - m_interval_tree = interval_tree; - m_tertiary_stack.reserve(20); - m_function_index = -1; + @Override + public double getValue(int e) { + return m_interval_tree.getValue_(e); } private IntervalTreeImpl m_interval_tree; - private Envelope1D m_query = new Envelope1D(); - private int m_primary_handle; - private int m_next_primary_handle; - private int m_forked_handle; - private int m_current_end_handle; - private int m_next_end_handle; - private AttributeStreamOfInt32 m_tertiary_stack = new AttributeStreamOfInt32( - 0); - private int m_function_index; - private int[] m_function_stack = new int[2]; + } - private interface State { - static final int initialize = 0; - static final int pIn = 1; - static final int pL = 2; - static final int pR = 3; - static final int pT = 4; - static final int right = 5; - static final int left = 6; - static final int all = 7; - } + IntervalTreeImpl(boolean b_offline_dynamic) { + m_b_envelopes_ref = false; + m_b_offline_dynamic = b_offline_dynamic; + m_b_constructing = false; + m_b_construction_ended = false; + } - private boolean initialize_() { - m_primary_handle = -1; - m_next_primary_handle = -1; - m_forked_handle = -1; - m_current_end_handle = -1; + void addEnvelopesRef(ArrayList envelopes) { + reset_(true, true); + m_b_envelopes_ref = true; + m_envelopes_ref = envelopes; - if (m_interval_tree.m_primary_nodes != null - && m_interval_tree.m_primary_nodes.size() > 0) { - m_function_stack[0] = State.pIn; // overwrite initialize - m_next_primary_handle = m_interval_tree.m_root; - return true; - } + m_b_constructing = false; + m_b_construction_ended = true; - m_function_index = -1; - return false; + if (!m_b_offline_dynamic) { + insertIntervalsStatic_(); + m_c_count = m_envelopes_ref.size(); } + } - private boolean pIn_() { - m_primary_handle = m_next_primary_handle; + void startConstruction() { + reset_(true, false); + } - if (m_primary_handle == -1) { - m_function_index = -1; - m_current_end_handle = -1; - return false; - } + void addInterval(Envelope1D interval) { + if (!m_b_constructing) + throw new GeometryException("invalid call"); - double discriminant = m_interval_tree - .getDiscriminant_(m_primary_handle); + m_intervals.add(interval); + } - if (m_query.vmax < discriminant) { - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree - .getLPTR_(m_primary_handle); + void addInterval(double min, double max) { + if (!m_b_constructing) + throw new GeometryException("invald call"); - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree - .getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.left; - } + m_intervals.add(new Envelope1D(min, max)); + } - return true; - } + void endConstruction() { + if (!m_b_constructing) + throw new GeometryException("invalid call"); - if (discriminant < m_query.vmin) { - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree - .getRPTR_(m_primary_handle); + m_b_constructing = false; + m_b_construction_ended = true; - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree - .getLast_(secondary_handle); - m_function_stack[++m_function_index] = State.right; - } + if (!m_b_offline_dynamic) { + insertIntervalsStatic_(); + m_c_count = m_intervals.size(); + } + } - return true; - } + /* + * Resets the Interval_tree_impl to an empty state, but maintains a handle + * on the current intervals. + */ + void reset() { + if (!m_b_offline_dynamic || !m_b_construction_ended) + throw new IllegalArgumentException("invalid call"); - assert (m_query.contains(discriminant)); + reset_(false, m_b_envelopes_ref); + } - m_function_stack[m_function_index] = State.pL; // overwrite pIn - m_forked_handle = m_primary_handle; - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree.getLPTR_(m_primary_handle); + /** + * Returns the number of intervals stored in the Interval_tree_impl + */ + int size() { + return m_c_count; + } - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.all; - } + /** + * Gets an iterator on the Interval_tree_impl using the input Envelope_1D + * interval as the query. To reuse the existing iterator on the same + * Interval_tree_impl but with a new query, use the reset_iterator function + * on the Interval_tree_iterator_impl. \param query The Envelope_1D interval + * used for the query. \param tolerance The tolerance used for the + * intersection tests. + */ + IntervalTreeIteratorImpl getIterator(Envelope1D query, double tolerance) { + return new IntervalTreeImpl.IntervalTreeIteratorImpl(this, query, tolerance); + } - return true; - } + /** + * Gets an iterator on the Interval_tree_impl using the input double as the + * stabbing query. To reuse the existing iterator on the same + * Interval_tree_impl but with a new query, use the reset_iterator function + * on the Interval_tree_iterator_impl. \param query The double used for the + * stabbing query. \param tolerance The tolerance used for the intersection + * tests. + */ + IntervalTreeIteratorImpl getIterator(double query, double tolerance) { + return new IntervalTreeImpl.IntervalTreeIteratorImpl(this, query, tolerance); + } - private boolean pL_() { - m_primary_handle = m_next_primary_handle; + /** + * Gets an iterator on the Interval_tree_impl. + */ + IntervalTreeIteratorImpl getIterator() { + return new IntervalTreeImpl.IntervalTreeIteratorImpl(this); + } - if (m_primary_handle == -1) { - m_function_stack[m_function_index] = State.pR; // overwrite pL - m_next_primary_handle = m_interval_tree - .getRPTR_(m_forked_handle); - return true; - } + private boolean m_b_envelopes_ref; + private boolean m_b_offline_dynamic; + private ArrayList m_intervals; + private ArrayList m_envelopes_ref; + private StridedIndexTypeCollection m_tertiary_nodes; // 5 elements for offline dynamic case, 4 elements for static case + private StridedIndexTypeCollection m_interval_nodes; // 3 elements + private AttributeStreamOfInt32 m_interval_handles; // for offline dynamic// case + private IndexMultiDCList m_secondary_lists; // for static case + private Treap m_secondary_treaps; // for off-line dynamic case + private AttributeStreamOfInt32 m_end_indices_unique; // for both offline dynamic and static cases + private int m_c_count; + private int m_root; + private boolean m_b_sort_intervals; + private boolean m_b_constructing; + private boolean m_b_construction_ended; - double discriminant = m_interval_tree - .getDiscriminant_(m_primary_handle); + /* m_tertiary_nodes + * 0: m_discriminant_index_1 + * 1: m_secondary + * 2: m_lptr + * 3: m_rptr + * 4: m_pptr + */ - if (discriminant < m_query.vmin) { - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree - .getRPTR_(m_primary_handle); + private void querySortedEndPointIndices_(AttributeStreamOfInt32 end_indices) { + int size = (!m_b_envelopes_ref ? m_intervals.size() : m_envelopes_ref.size()); - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree - .getLast_(secondary_handle); - m_function_stack[++m_function_index] = State.right; - } + for (int i = 0; i < 2 * size; i++) + end_indices.add(i); - return true; - } + sortEndIndices_(end_indices, 0, 2 * size); + } - assert (m_query.contains(discriminant)); + private void querySortedDuplicatesRemoved_(AttributeStreamOfInt32 end_indices_sorted) { + // remove duplicates - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree.getLPTR_(m_primary_handle); + double prev = NumberUtils.TheNaN; + for (int i = 0; i < end_indices_sorted.size(); i++) { + int e = end_indices_sorted.get(i); + double v = getValue_(e); - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.all; + if (v != prev) { + m_end_indices_unique.add(e); + prev = v; } + } + } - int rptr = m_interval_tree.getRPTR_(m_primary_handle); + void insert(int index) { + if (!m_b_offline_dynamic || !m_b_construction_ended) + throw new IllegalArgumentException("invalid call"); - if (rptr != -1) { - m_tertiary_stack.add(rptr); // we'll search this in the pT state - } + if (m_root == -1) { - return true; - } + int size = (!m_b_envelopes_ref ? m_intervals.size() : m_envelopes_ref.size()); - private boolean pR_() { - m_primary_handle = m_next_primary_handle; + if (m_b_sort_intervals) { + // sort + AttributeStreamOfInt32 end_point_indices_sorted = new AttributeStreamOfInt32(0); + end_point_indices_sorted.reserve(2 * size); + querySortedEndPointIndices_(end_point_indices_sorted); - if (m_primary_handle == -1) { - m_function_stack[m_function_index] = State.pT; // overwrite pR - return true; + // remove duplicates + m_end_indices_unique.resize(0); + querySortedDuplicatesRemoved_(end_point_indices_sorted); + m_interval_handles.resize(size, -1); + m_interval_handles.setRange(-1, 0, size); + m_b_sort_intervals = false; + } else { + m_interval_handles.setRange(-1, 0, size); } - double discriminant = m_interval_tree - .getDiscriminant_(m_primary_handle); + m_root = createRoot_(); + } - if (m_query.vmax < discriminant) { - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); - m_next_primary_handle = m_interval_tree - .getLPTR_(m_primary_handle); + int interval_handle = insertIntervalEnd_(index << 1, m_root); + int secondary_handle = getSecondaryFromInterval_(interval_handle); + int right_end_handle = m_secondary_treaps.addElement((index << 1) + 1, secondary_handle); + setRightEnd_(interval_handle, right_end_handle); + m_interval_handles.set(index, interval_handle); + m_c_count++; + // assert(check_validation_()); + } - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree - .getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.left; - } + private void insertIntervalsStatic_() { + int size = (!m_b_envelopes_ref ? m_intervals.size() : m_envelopes_ref.size()); - return true; - } + assert (m_b_sort_intervals); - assert (m_query.contains(discriminant)); + // sort + AttributeStreamOfInt32 end_indices_sorted = new AttributeStreamOfInt32(0); + end_indices_sorted.reserve(2 * size); + querySortedEndPointIndices_(end_indices_sorted); - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); + // remove duplicates + m_end_indices_unique.resize(0); + querySortedDuplicatesRemoved_(end_indices_sorted); - m_next_primary_handle = m_interval_tree.getRPTR_(m_primary_handle); + assert (m_tertiary_nodes.size() == 0); + m_interval_nodes.setCapacity(size); // one for each interval being inserted. each element contains a tertiary node, a left secondary node, and a right secondary node. + m_secondary_lists.reserveNodes(2 * size); // one for each end point of the original interval set (not the unique set) - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.all; - } + AttributeStreamOfInt32 interval_handles = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream(size); + interval_handles.setRange(-1, 0, size); - int lptr = m_interval_tree.getLPTR_(m_primary_handle); + m_root = createRoot_(); - if (lptr != -1) { - m_tertiary_stack.add(lptr); // we'll search this in the pT state - } + for (int i = 0; i < end_indices_sorted.size(); i++) { + int e = end_indices_sorted.get(i); + int interval_handle = interval_handles.get(e >> 1); - return true; + if (interval_handle != -1) {// insert the right end point + assert (isRight_(e)); + int secondary_handle = getSecondaryFromInterval_(interval_handle); + setRightEnd_(interval_handle, m_secondary_lists.addElement(secondary_handle, e)); + } else {// insert the left end point + assert (isLeft_(e)); + interval_handle = insertIntervalEnd_(e, m_root); + interval_handles.set(e >> 1, interval_handle); + } } - private boolean pT_() { - if (m_tertiary_stack.size() == 0) { - m_function_index = -1; - m_current_end_handle = -1; - return false; - } + assert (m_secondary_lists.getNodeCount() == 2 * size); + } - m_primary_handle = m_tertiary_stack - .get(m_tertiary_stack.size() - 1); - m_tertiary_stack.resize(m_tertiary_stack.size() - 1); + private int createRoot_() { + int discriminant_index_1 = calculateDiscriminantIndex1_(0, m_end_indices_unique.size() - 1); + return createTertiaryNode_(discriminant_index_1); + } - int secondary_handle = m_interval_tree - .getSecondaryFromPrimary(m_primary_handle); + private int insertIntervalEnd_(int end_index, int root) { + assert (isLeft_(end_index)); + int pptr = -1; + int ptr = root; + int secondary_handle = -1; + int interval_handle = -1; + int il = 0, ir = m_end_indices_unique.size() - 1, im = 0; + int index = end_index >> 1; + double discriminant_pptr = NumberUtils.NaN(); + double discriminant_ptr = NumberUtils.NaN(); + boolean bSearching = true; - if (secondary_handle != -1) { - m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); - m_function_stack[++m_function_index] = State.all; - } + double min = getMin_(index); + double max = getMax_(index); - if (m_interval_tree.getLPTR_(m_primary_handle) != -1) - m_tertiary_stack - .add(m_interval_tree.getLPTR_(m_primary_handle)); + int discriminant_index_1 = -1; - if (m_interval_tree.getRPTR_(m_primary_handle) != -1) - m_tertiary_stack - .add(m_interval_tree.getRPTR_(m_primary_handle)); + while (bSearching) { + im = il + (ir - il) / 2; + assert (il != ir || min == max); + discriminant_index_1 = calculateDiscriminantIndex1_(il, ir); + double discriminant = getDiscriminantFromIndex1_(discriminant_index_1); + assert (!NumberUtils.isNaN(discriminant)); - return true; - } + if (max < discriminant) { + if (ptr != -1) { + if (discriminant_index_1 == getDiscriminantIndex1_(ptr)) { + assert (getDiscriminantFromIndex1_(discriminant_index_1) == getDiscriminant_(ptr)); - private boolean left_() { - m_current_end_handle = m_next_end_handle; + pptr = ptr; + discriminant_pptr = discriminant; + ptr = getLPTR_(ptr); - if (m_current_end_handle != -1 - && IntervalTreeImpl.isLeft_(getCurrentEndIndex_()) - && m_interval_tree.getValue_(getCurrentEndIndex_()) <= m_query.vmax) { - m_next_end_handle = getNext_(); - return false; - } + if (ptr != -1) + discriminant_ptr = getDiscriminant_(ptr); + else + discriminant_ptr = NumberUtils.NaN(); + } else if (discriminant_ptr > discriminant) { + int tertiary_handle = createTertiaryNode_(discriminant_index_1); - m_function_index--; - return true; - } + if (discriminant < discriminant_pptr) + setLPTR_(pptr, tertiary_handle); + else + setRPTR_(pptr, tertiary_handle); - private boolean right_() { - m_current_end_handle = m_next_end_handle; + setRPTR_(tertiary_handle, ptr); - if (m_current_end_handle != -1 - && IntervalTreeImpl.isRight_(getCurrentEndIndex_()) - && m_interval_tree.getValue_(getCurrentEndIndex_()) >= m_query.vmin) { - m_next_end_handle = getPrev_(); - return false; - } + if (m_b_offline_dynamic) { + setPPTR_(tertiary_handle, pptr); + setPPTR_(ptr, tertiary_handle); + } - m_function_index--; - return true; - } + pptr = tertiary_handle; + discriminant_pptr = discriminant; + ptr = -1; + discriminant_ptr = NumberUtils.NaN(); + } + } - private boolean all_() { - m_current_end_handle = m_next_end_handle; + ir = im; - if (m_current_end_handle != -1 - && IntervalTreeImpl.isLeft_(getCurrentEndIndex_())) { - m_next_end_handle = getNext_(); - return false; + continue; } - m_function_index--; - return true; - } - - private int getNext_() { - if (!m_interval_tree.m_b_offline_dynamic) - return m_interval_tree.m_secondary_lists - .getNext(m_current_end_handle); - - return m_interval_tree.m_secondary_treaps - .getNext(m_current_end_handle); - } + if (min > discriminant) { + if (ptr != -1) { + if (discriminant_index_1 == getDiscriminantIndex1_(ptr)) { + assert (getDiscriminantFromIndex1_(discriminant_index_1) == getDiscriminant_(ptr)); - private int getPrev_() { - if (!m_interval_tree.m_b_offline_dynamic) - return m_interval_tree.m_secondary_lists - .getPrev(m_current_end_handle); + pptr = ptr; + discriminant_pptr = discriminant; + ptr = getRPTR_(ptr); - return m_interval_tree.m_secondary_treaps - .getPrev(m_current_end_handle); - } + if (ptr != -1) + discriminant_ptr = getDiscriminant_(ptr); + else + discriminant_ptr = NumberUtils.NaN(); + } else if (discriminant_ptr < discriminant) { + int tertiary_handle = createTertiaryNode_(discriminant_index_1); - private int getCurrentEndIndex_() { - if (!m_interval_tree.m_b_offline_dynamic) - return m_interval_tree.m_secondary_lists - .getData(m_current_end_handle); + if (discriminant < discriminant_pptr) + setLPTR_(pptr, tertiary_handle); + else + setRPTR_(pptr, tertiary_handle); - return m_interval_tree.m_secondary_treaps - .getElement(m_current_end_handle); - } - } + setLPTR_(tertiary_handle, ptr); - IntervalTreeImpl(boolean b_offline_dynamic) { - m_b_offline_dynamic = b_offline_dynamic; - m_b_constructing = false; - m_b_construction_ended = false; - } + if (m_b_offline_dynamic) { + setPPTR_(tertiary_handle, pptr); + setPPTR_(ptr, tertiary_handle); + } - void startConstruction() { - reset_(true); - } + pptr = tertiary_handle; + discriminant_pptr = discriminant; + ptr = -1; + discriminant_ptr = NumberUtils.NaN(); + } + } - void addInterval(Envelope1D interval) { - if (!m_b_constructing) - throw new GeometryException("invalid call"); + il = im + 1; - m_intervals.add(interval); - } + continue; + } - void addInterval(double min, double max) { - if (!m_b_constructing) - throw new GeometryException("invald call"); + int tertiary_handle = -1; - m_intervals.add(new Envelope1D(min, max)); - } + if (ptr == -1 || discriminant_index_1 != getDiscriminantIndex1_(ptr)) { + tertiary_handle = createTertiaryNode_(discriminant_index_1); + } else { + tertiary_handle = ptr; + } - void endConstruction() { - if (!m_b_constructing) - throw new GeometryException("invalid call"); + secondary_handle = getSecondaryFromTertiary_(tertiary_handle); - m_b_constructing = false; - m_b_construction_ended = true; + if (secondary_handle == -1) { + secondary_handle = createSecondary_(tertiary_handle); + setSecondaryToTertiary_(tertiary_handle, secondary_handle); + } - if (!m_b_offline_dynamic) { - insertIntervalsStatic_(); - m_c_count = m_intervals.size(); - } - } + int left_end_handle = addEndIndex_(secondary_handle, end_index); + interval_handle = createIntervalNode_(); + setSecondaryToInterval_(interval_handle, secondary_handle); + setLeftEnd_(interval_handle, left_end_handle); - /** - * Inserts the interval from the given index into the Interval_tree_impl. - * This operation can only be performed in the offline dynamic case. \param - * index The index containing the interval to be inserted. - */ - void insert(int index) { - if (!m_b_offline_dynamic || !m_b_construction_ended) - throw new IllegalArgumentException("invalid call"); + if (ptr == -1 || discriminant_index_1 != getDiscriminantIndex1_(ptr)) { + assert (tertiary_handle != -1); + assert (getLPTR_(tertiary_handle) == -1 && getRPTR_(tertiary_handle) == -1 && (!m_b_offline_dynamic || getPPTR_(tertiary_handle) == -1)); - if (m_root == -1) { + if (discriminant < discriminant_pptr) + setLPTR_(pptr, tertiary_handle); + else + setRPTR_(pptr, tertiary_handle); - int size = m_intervals.size(); + if (m_b_offline_dynamic) + setPPTR_(tertiary_handle, pptr); - if (m_b_sort_intervals) { - // sort - AttributeStreamOfInt32 end_point_indices_sorted = new AttributeStreamOfInt32( - 0); - end_point_indices_sorted.reserve(2 * size); - querySortedEndPointIndices_(end_point_indices_sorted); + if (ptr != -1) { + if (discriminant_ptr < discriminant) + setLPTR_(tertiary_handle, ptr); + else + setRPTR_(tertiary_handle, ptr); - // remove duplicates - m_end_indices_unique.reserve(2 * size); - m_end_indices_unique.resize(0); - querySortedDuplicatesRemoved_(end_point_indices_sorted); - m_interval_handles.resize(size, -1); - m_interval_handles.setRange(-1, 0, size); - m_b_sort_intervals = false; - } else { - m_interval_handles.setRange(-1, 0, size); + if (m_b_offline_dynamic) + setPPTR_(ptr, tertiary_handle); + } } - m_root = createPrimaryNode_(); + bSearching = false; + break; } - int interval_handle = insertIntervalEnd_(index << 1, m_root); - int secondary_handle = getSecondaryFromInterval_(interval_handle); - int right_end_handle = m_secondary_treaps.addElement((index << 1) + 1, - secondary_handle); - setRightEnd_(interval_handle, right_end_handle); - m_interval_handles.set(index, interval_handle); - m_c_count++; - // assert(check_validation_()); + return interval_handle; } - /** - * Deletes the interval from the Interval_tree_impl. \param index The index - * containing the interval to be deleted from the Interval_tree_impl. - */ void remove(int index) { if (!m_b_offline_dynamic || !m_b_construction_ended) throw new GeometryException("invalid call"); @@ -531,8 +479,7 @@ void remove(int index) { int interval_handle = m_interval_handles.get(index); if (interval_handle == -1) - throw new IllegalArgumentException( - "the interval does not exist in the interval tree"); + throw new GeometryException("the interval does not exist in the interval tree"); m_interval_handles.set(index, -1); @@ -544,574 +491,595 @@ void remove(int index) { int size; int secondary_handle = getSecondaryFromInterval_(interval_handle); - int primary_handle; + int tertiary_handle = -1; - primary_handle = m_secondary_treaps.getTreapData(secondary_handle); - m_secondary_treaps.deleteNode(getLeftEnd_(interval_handle), - secondary_handle); - m_secondary_treaps.deleteNode(getRightEnd_(interval_handle), - secondary_handle); + tertiary_handle = m_secondary_treaps.getTreapData(secondary_handle); + m_secondary_treaps.deleteNode(getLeftEnd_(interval_handle), secondary_handle); + m_secondary_treaps.deleteNode(getRightEnd_(interval_handle), secondary_handle); size = m_secondary_treaps.size(secondary_handle); if (size == 0) { m_secondary_treaps.deleteTreap(secondary_handle); - setSecondaryToPrimary_(primary_handle, -1); + setSecondaryToTertiary_(tertiary_handle, -1); } m_interval_nodes.deleteElement(interval_handle); - int tertiary_handle = getPPTR_(primary_handle); - int lptr = getLPTR_(primary_handle); - int rptr = getRPTR_(primary_handle); + int pptr = getPPTR_(tertiary_handle); + int lptr = getLPTR_(tertiary_handle); + int rptr = getRPTR_(tertiary_handle); int iterations = 0; - while (!(size > 0 || primary_handle == m_root || (lptr != -1 && rptr != -1))) { + while (!(size > 0 || tertiary_handle == m_root || (lptr != -1 && rptr != -1))) { assert (size == 0); assert (lptr == -1 || rptr == -1); - assert (primary_handle != 0); + assert (tertiary_handle != 0); - if (primary_handle == getLPTR_(tertiary_handle)) { + if (tertiary_handle == getLPTR_(pptr)) { if (lptr != -1) { - setLPTR_(tertiary_handle, lptr); - setPPTR_(lptr, tertiary_handle); - setLPTR_(primary_handle, -1); - setPPTR_(primary_handle, -1); + setLPTR_(pptr, lptr); + setPPTR_(lptr, pptr); + setLPTR_(tertiary_handle, -1); + setPPTR_(tertiary_handle, -1); } else if (rptr != -1) { - setLPTR_(tertiary_handle, rptr); - setPPTR_(rptr, tertiary_handle); - setRPTR_(primary_handle, -1); - setPPTR_(primary_handle, -1); + setLPTR_(pptr, rptr); + setPPTR_(rptr, pptr); + setRPTR_(tertiary_handle, -1); + setPPTR_(tertiary_handle, -1); } else { - setLPTR_(tertiary_handle, -1); - setPPTR_(primary_handle, -1); + setLPTR_(pptr, -1); + setPPTR_(tertiary_handle, -1); } } else { if (lptr != -1) { - setRPTR_(tertiary_handle, lptr); - setPPTR_(lptr, tertiary_handle); - setLPTR_(primary_handle, -1); - setPPTR_(primary_handle, -1); + setRPTR_(pptr, lptr); + setPPTR_(lptr, pptr); + setLPTR_(tertiary_handle, -1); + setPPTR_(tertiary_handle, -1); } else if (rptr != -1) { - setRPTR_(tertiary_handle, rptr); - setPPTR_(rptr, tertiary_handle); - setRPTR_(primary_handle, -1); - setPPTR_(primary_handle, -1); - } else { + setRPTR_(pptr, rptr); + setPPTR_(rptr, pptr); setRPTR_(tertiary_handle, -1); - setPPTR_(primary_handle, -1); + setPPTR_(tertiary_handle, -1); + } else { + setRPTR_(pptr, -1); + setPPTR_(tertiary_handle, -1); } } + m_tertiary_nodes.deleteElement(tertiary_handle); + iterations++; - primary_handle = tertiary_handle; - secondary_handle = getSecondaryFromPrimary(primary_handle); - size = (secondary_handle != -1 ? m_secondary_treaps - .size(secondary_handle) : 0); - lptr = getLPTR_(primary_handle); - rptr = getRPTR_(primary_handle); - tertiary_handle = getPPTR_(primary_handle); + tertiary_handle = pptr; + secondary_handle = getSecondaryFromTertiary_(tertiary_handle); + size = (secondary_handle != -1 ? m_secondary_treaps.size(secondary_handle) : 0); + lptr = getLPTR_(tertiary_handle); + rptr = getRPTR_(tertiary_handle); + pptr = getPPTR_(tertiary_handle); } assert (iterations <= 2); - // assert(check_validation_()); + //assert(check_validation_()); } - /* - * Resets the Interval_tree_impl to an empty state, but maintains a handle - * on the current intervals. - */ - void reset() { - if (!m_b_offline_dynamic || !m_b_construction_ended) - throw new IllegalArgumentException("invalid call"); + private void reset_(boolean b_new_intervals, boolean b_envelopes_ref) { + if (b_new_intervals) { + m_b_envelopes_ref = false; + m_envelopes_ref = null; + + m_b_sort_intervals = true; + m_b_constructing = true; + m_b_construction_ended = false; + + if (m_end_indices_unique == null) + m_end_indices_unique = (AttributeStreamOfInt32) (AttributeStreamBase.createIndexStream(0)); + else + m_end_indices_unique.resize(0); + + if (!b_envelopes_ref) { + if (m_intervals == null) + m_intervals = new ArrayList(0); + else + m_intervals.clear(); + } else { + if (m_intervals != null) + m_intervals.clear(); + + m_b_envelopes_ref = true; + } + } else { + assert (m_b_offline_dynamic && m_b_construction_ended); + m_b_sort_intervals = false; + } + + if (m_b_offline_dynamic) { + if (m_interval_handles == null) { + m_interval_handles = (AttributeStreamOfInt32) (AttributeStreamBase.createIndexStream(0)); + m_secondary_treaps = new Treap(); + m_secondary_treaps.setComparator(new SecondaryComparator(this)); + } else { + m_secondary_treaps.clear(); + } + } else { + if (m_secondary_lists == null) + m_secondary_lists = new IndexMultiDCList(); + else + m_secondary_lists.clear(); + } + + if (m_tertiary_nodes == null) { + m_interval_nodes = new StridedIndexTypeCollection(3); + m_tertiary_nodes = new StridedIndexTypeCollection(m_b_offline_dynamic ? 5 : 4); + } else { + m_interval_nodes.deleteAll(false); + m_tertiary_nodes.deleteAll(false); + } - reset_(false); + m_root = -1; + m_c_count = 0; } - /** - * Returns the number of intervals stored in the Interval_tree_impl - */ - int size() { - return m_c_count; + private double getDiscriminant_(int tertiary_handle) { + int discriminant_index_1 = getDiscriminantIndex1_(tertiary_handle); + return getDiscriminantFromIndex1_(discriminant_index_1); } - /** - * Gets an iterator on the Interval_tree_impl using the input Envelope_1D - * interval as the query. To reuse the existing iterator on the same - * Interval_tree_impl but with a new query, use the reset_iterator function - * on the Interval_tree_iterator_impl. \param query The Envelope_1D interval - * used for the query. \param tolerance The tolerance used for the - * intersection tests. - */ - IntervalTreeIteratorImpl getIterator(Envelope1D query, double tolerance) { - return new IntervalTreeImpl.IntervalTreeIteratorImpl(this, query, - tolerance); - } + private double getDiscriminantFromIndex1_(int discriminant_index_1) { + if (discriminant_index_1 == -1) + return NumberUtils.NaN(); - /** - * Gets an iterator on the Interval_tree_impl using the input double as the - * stabbing query. To reuse the existing iterator on the same - * Interval_tree_impl but with a new query, use the reset_iterator function - * on the Interval_tree_iterator_impl. \param query The double used for the - * stabbing query. \param tolerance The tolerance used for the intersection - * tests. - */ - IntervalTreeIteratorImpl getIterator(double query, double tolerance) { - return new IntervalTreeImpl.IntervalTreeIteratorImpl(this, query, - tolerance); - } + if (discriminant_index_1 > 0) { + int j = discriminant_index_1 - 2; + int e_1 = m_end_indices_unique.get(j); + int e_2 = m_end_indices_unique.get(j + 1); - /** - * Gets an iterator on the Interval_tree_impl. - */ - IntervalTreeIteratorImpl getIterator() { - return new IntervalTreeImpl.IntervalTreeIteratorImpl(this); - } + double v_1 = getValue_(e_1); + double v_2 = getValue_(e_2); + assert (v_1 < v_2); - private static final class SecondaryComparator extends Treap.Comparator { - SecondaryComparator(IntervalTreeImpl interval_tree) { - m_interval_tree = interval_tree; + return 0.5 * (v_1 + v_2); } - @Override - public int compare(Treap treap, int e_1, int node) { - int e_2 = treap.getElement(node); - double v_1 = m_interval_tree.getValue_(e_1); - double v_2 = m_interval_tree.getValue_(e_2); + int j = -discriminant_index_1 - 2; + assert (j >= 0); + int e = m_end_indices_unique.get(j); + double v = getValue_(e); - if (v_1 < v_2) - return -1; - if (v_1 == v_2) { - if (isLeft_(e_1) && isRight_(e_2)) - return -1; - if (isLeft_(e_2) && isRight_(e_1)) - return 1; - return 0; - } - return 1; + return v; + } + + private int calculateDiscriminantIndex1_(int il, int ir) { + int discriminant_index_1; + + if (il < ir) { + int im = il + (ir - il) / 2; + discriminant_index_1 = im + 2; // positive discriminant means use average of im and im + 1 + } else { + discriminant_index_1 = -(il + 2); // negative discriminant just means use il (-(il + 2) will never be -1) } + return discriminant_index_1; + } + + static final class IntervalTreeIteratorImpl { + private IntervalTreeImpl m_interval_tree; - }; + private Envelope1D m_query = new Envelope1D(); + private int m_tertiary_handle; + private int m_next_tertiary_handle; + private int m_forked_handle; + private int m_current_end_handle; + private int m_next_end_handle; + private AttributeStreamOfInt32 m_tertiary_stack = new AttributeStreamOfInt32(0); + private int m_function_index; + private int[] m_function_stack = new int[2]; - private boolean m_b_offline_dynamic; - private ArrayList m_intervals; - private StridedIndexTypeCollection m_primary_nodes; // 8 elements for - // offline dynamic case, - // 7 elements for static - // case - private StridedIndexTypeCollection m_interval_nodes; // 3 elements - private AttributeStreamOfInt32 m_interval_handles; // for offline dynamic - // case - private IndexMultiDCList m_secondary_lists; // for static case - private Treap m_secondary_treaps; // for off-line dynamic case - private AttributeStreamOfInt32 m_end_indices_unique; // for both offline - // dynamic and - // static cases - private int m_c_count; - private int m_root; - private boolean m_b_sort_intervals; - private boolean m_b_constructing; - private boolean m_b_construction_ended; + private interface State { + static final int initialize = 0; + static final int pIn = 1; + static final int pL = 2; + static final int pR = 3; + static final int pT = 4; + static final int right = 5; + static final int left = 6; + static final int all = 7; + } - private void querySortedEndPointIndices_(AttributeStreamOfInt32 end_indices) { - int size = m_intervals.size(); + private int getNext_() { + if (!m_interval_tree.m_b_offline_dynamic) + return m_interval_tree.m_secondary_lists.getNext(m_current_end_handle); - for (int i = 0; i < 2 * size; i++) - end_indices.add(i); + return m_interval_tree.m_secondary_treaps.getNext(m_current_end_handle); + } - sortEndIndices_(end_indices, 0, 2 * size); - } + private int getPrev_() { + if (!m_interval_tree.m_b_offline_dynamic) + return m_interval_tree.m_secondary_lists.getPrev(m_current_end_handle); - private void querySortedDuplicatesRemoved_( - AttributeStreamOfInt32 end_indices_sorted) { - // remove duplicates + return m_interval_tree.m_secondary_treaps.getPrev(m_current_end_handle); + } - double prev = NumberUtils.TheNaN; - for (int i = 0; i < end_indices_sorted.size(); i++) { - int e = end_indices_sorted.get(i); - double v = getValue_(e); + private int getCurrentEndIndex_() { + if (!m_interval_tree.m_b_offline_dynamic) + return m_interval_tree.m_secondary_lists.getData(m_current_end_handle); - if (v != prev) { - m_end_indices_unique.add(e); - prev = v; - } + return m_interval_tree.m_secondary_treaps.getElement(m_current_end_handle); } - } - private void insertIntervalsStatic_() { - int size = m_intervals.size(); + int next() { + if (!m_interval_tree.m_b_construction_ended) + throw new GeometryException("invalid call"); - assert (m_b_sort_intervals); + if (m_function_index < 0) + return -1; - // sort - AttributeStreamOfInt32 end_indices_sorted = new AttributeStreamOfInt32( - 0); - end_indices_sorted.reserve(2 * size); - querySortedEndPointIndices_(end_indices_sorted); + boolean b_searching = true; - // remove duplicates - m_end_indices_unique.reserve(2 * size); - m_end_indices_unique.resize(0); - querySortedDuplicatesRemoved_(end_indices_sorted); + while (b_searching) { + switch (m_function_stack[m_function_index]) { + case State.pIn: + b_searching = pIn_(); + break; + case State.pL: + b_searching = pL_(); + break; + case State.pR: + b_searching = pR_(); + break; + case State.pT: + b_searching = pT_(); + break; + case State.right: + b_searching = right_(); + break; + case State.left: + b_searching = left_(); + break; + case State.all: + b_searching = all_(); + break; + case State.initialize: + b_searching = initialize_(); + break; + default: + throw GeometryException.GeometryInternalError(); + } + } - assert (m_primary_nodes.size() == 0); - m_interval_nodes.setCapacity(size); // one for each interval being - // inserted. each element contains a - // primary node, a left secondary - // node, and a right secondary node. - m_secondary_lists.reserveNodes(2 * size); // one for each end point of - // the original interval set - // (not the unique set) - - AttributeStreamOfInt32 interval_handles = (AttributeStreamOfInt32) AttributeStreamBase - .createIndexStream(size); - interval_handles.setRange(-1, 0, size); + if (m_current_end_handle != -1) + return getCurrentEndIndex_() >> 1; - m_root = createPrimaryNode_(); + return -1; + } - for (int i = 0; i < end_indices_sorted.size(); i++) { - int e = end_indices_sorted.get(i); - int interval_handle = interval_handles.get(e >> 1); + private boolean initialize_() { + m_tertiary_handle = -1; + m_next_tertiary_handle = -1; + m_forked_handle = -1; + m_current_end_handle = -1; - if (interval_handle != -1) {// insert the right end point - assert (isRight_(e)); - int secondary_handle = getSecondaryFromInterval_(interval_handle); - setRightEnd_(interval_handle, - m_secondary_lists.addElement(secondary_handle, e)); - } else {// insert the left end point - assert (isLeft_(e)); - interval_handle = insertIntervalEnd_(e, m_root); - interval_handles.set(e >> 1, interval_handle); + if (m_interval_tree.m_tertiary_nodes != null && m_interval_tree.m_tertiary_nodes.size() > 0) { + m_function_stack[0] = State.pIn; // overwrite initialize + m_next_tertiary_handle = m_interval_tree.m_root; + return true; } + + m_function_index = -1; + return false; } - assert (m_secondary_lists.getNodeCount() == 2 * size); - } + private boolean pIn_() { + m_tertiary_handle = m_next_tertiary_handle; - private int insertIntervalEnd_(int end_index, int root) { - assert (isLeft_(end_index)); - int primary_handle = root; - int tertiary_handle = root; - int ptr = root; - int secondary_handle; - int interval_handle = -1; - int il = 0, ir = m_end_indices_unique.size() - 1, im = 0; - int index = end_index >> 1; - double discriminant_tertiary = NumberUtils.TheNaN; - double discriminant_ptr = NumberUtils.TheNaN; - boolean bSearching = true; + if (m_tertiary_handle == -1) { + m_function_index = -1; + m_current_end_handle = -1; + return false; + } - double min = getMin_(index); - double max = getMax_(index); + double discriminant = m_interval_tree.getDiscriminant_(m_tertiary_handle); - while (bSearching) { - if (il < ir) { - im = il + (ir - il) / 2; + if (m_query.vmax < discriminant) { + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getLPTR_(m_tertiary_handle); - if (getDiscriminantIndex1_(primary_handle) == -1) - setDiscriminantIndices_(primary_handle, - m_end_indices_unique.get(im), - m_end_indices_unique.get(im + 1)); - } else { - assert (il == ir); - assert (min == max); + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.left; + } - if (getDiscriminantIndex1_(primary_handle) == -1) - setDiscriminantIndices_(primary_handle, - m_end_indices_unique.get(il), - m_end_indices_unique.get(il)); + return true; } - double discriminant = getDiscriminant_(primary_handle); - assert (!NumberUtils.isNaN(discriminant)); + if (discriminant < m_query.vmin) { + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getRPTR_(m_tertiary_handle); - if (max < discriminant) { - if (ptr != -1) { - if (ptr == primary_handle) { - tertiary_handle = primary_handle; - discriminant_tertiary = discriminant; - ptr = getLPTR_(primary_handle); + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getLast_(secondary_handle); + m_function_stack[++m_function_index] = State.right; + } - if (ptr != -1) - discriminant_ptr = getDiscriminant_(ptr); - else - discriminant_ptr = NumberUtils.TheNaN; - } else if (discriminant_ptr > discriminant) { - if (discriminant < discriminant_tertiary) - setLPTR_(tertiary_handle, primary_handle); - else - setRPTR_(tertiary_handle, primary_handle); + return true; + } - setRPTR_(primary_handle, ptr); + assert (m_query.contains(discriminant)); - if (m_b_offline_dynamic) { - setPPTR_(primary_handle, tertiary_handle); - setPPTR_(ptr, primary_handle); - } + m_function_stack[m_function_index] = State.pL; // overwrite pIn + m_forked_handle = m_tertiary_handle; + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getLPTR_(m_tertiary_handle); - tertiary_handle = primary_handle; - discriminant_tertiary = discriminant; - ptr = -1; - discriminant_ptr = NumberUtils.TheNaN; + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.all; + } - assert (getLPTR_(primary_handle) == -1); - assert (getRightPrimary_(primary_handle) != -1); - } - } + return true; + } + + private boolean pL_() { + m_tertiary_handle = m_next_tertiary_handle; + + if (m_tertiary_handle == -1) { + m_function_stack[m_function_index] = State.pR; // overwrite pL + m_next_tertiary_handle = m_interval_tree.getRPTR_(m_forked_handle); + return true; + } + + double discriminant = m_interval_tree.getDiscriminant_(m_tertiary_handle); - int left_handle = getLeftPrimary_(primary_handle); + if (discriminant < m_query.vmin) { + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getRPTR_(m_tertiary_handle); - if (left_handle == -1) { - left_handle = createPrimaryNode_(); - setLeftPrimary_(primary_handle, left_handle); + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getLast_(secondary_handle); + m_function_stack[++m_function_index] = State.right; } - primary_handle = left_handle; - ir = im; + return true; + } - continue; + assert (m_query.contains(discriminant)); + + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getLPTR_(m_tertiary_handle); + + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.all; } - if (min > discriminant) { - if (ptr != -1) { - if (ptr == primary_handle) { - tertiary_handle = primary_handle; - discriminant_tertiary = discriminant; - ptr = getRPTR_(primary_handle); + int rptr = m_interval_tree.getRPTR_(m_tertiary_handle); - if (ptr != -1) - discriminant_ptr = getDiscriminant_(ptr); - else - discriminant_ptr = NumberUtils.TheNaN; - } else if (discriminant_ptr < discriminant) { - if (discriminant < discriminant_tertiary) - setLPTR_(tertiary_handle, primary_handle); - else - setRPTR_(tertiary_handle, primary_handle); + if (rptr != -1) { + m_tertiary_stack.add(rptr); // we'll search this in the pT state + } - setLPTR_(primary_handle, ptr); + return true; + } - if (m_b_offline_dynamic) { - setPPTR_(primary_handle, tertiary_handle); - setPPTR_(ptr, primary_handle); - } + private boolean pR_() { + m_tertiary_handle = m_next_tertiary_handle; - tertiary_handle = primary_handle; - discriminant_tertiary = discriminant; - ptr = -1; - discriminant_ptr = NumberUtils.TheNaN; + if (m_tertiary_handle == -1) { + m_function_stack[m_function_index] = State.pT; // overwrite pR + return true; + } - assert (getRPTR_(primary_handle) == -1); - assert (getLeftPrimary_(primary_handle) != -1); - } - } + double discriminant = m_interval_tree.getDiscriminant_(m_tertiary_handle); - int right_handle = getRightPrimary_(primary_handle); + if (m_query.vmax < discriminant) { + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); + m_next_tertiary_handle = m_interval_tree.getLPTR_(m_tertiary_handle); - if (right_handle == -1) { - right_handle = createPrimaryNode_(); - setRightPrimary_(primary_handle, right_handle); + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.left; } - primary_handle = right_handle; - il = im + 1; - - continue; + return true; } - secondary_handle = getSecondaryFromPrimary(primary_handle); + assert (m_query.contains(discriminant)); - if (secondary_handle == -1) { - secondary_handle = createSecondary_(primary_handle); - setSecondaryToPrimary_(primary_handle, secondary_handle); - } + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); - int left_end_handle = addEndIndex(secondary_handle, end_index); - interval_handle = createIntervalNode_(); - setSecondaryToInterval_(interval_handle, secondary_handle); - setLeftEnd_(interval_handle, left_end_handle); + m_next_tertiary_handle = m_interval_tree.getRPTR_(m_tertiary_handle); - if (primary_handle != ptr) { - assert (primary_handle != -1); - assert (getLPTR_(primary_handle) == -1 - && getRPTR_(primary_handle) == -1 && (!m_b_offline_dynamic || getPPTR_(primary_handle) == -1)); + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.all; + } - if (discriminant < discriminant_tertiary) - setLPTR_(tertiary_handle, primary_handle); - else - setRPTR_(tertiary_handle, primary_handle); + int lptr = m_interval_tree.getLPTR_(m_tertiary_handle); - if (m_b_offline_dynamic) - setPPTR_(primary_handle, tertiary_handle); + if (lptr != -1) { + m_tertiary_stack.add(lptr); // we'll search this in the pT state + } - if (ptr != -1) { - if (discriminant_ptr < discriminant) - setLPTR_(primary_handle, ptr); - else - setRPTR_(primary_handle, ptr); + return true; + } - if (m_b_offline_dynamic) - setPPTR_(ptr, primary_handle); - } + private boolean pT_() { + if (m_tertiary_stack.size() == 0) { + m_function_index = -1; + m_current_end_handle = -1; + return false; } - bSearching = false; - } + m_tertiary_handle = m_tertiary_stack.get(m_tertiary_stack.size() - 1); + m_tertiary_stack.resize(m_tertiary_stack.size() - 1); - return interval_handle; - } + int secondary_handle = m_interval_tree.getSecondaryFromTertiary_(m_tertiary_handle); - private int createPrimaryNode_() { - return m_primary_nodes.newElement(); - } + if (secondary_handle != -1) { + m_next_end_handle = m_interval_tree.getFirst_(secondary_handle); + m_function_stack[++m_function_index] = State.all; + } - private int createSecondary_(int primary_handle) { - if (!m_b_offline_dynamic) - return m_secondary_lists.createList(primary_handle); + if (m_interval_tree.getLPTR_(m_tertiary_handle) != -1) + m_tertiary_stack.add(m_interval_tree.getLPTR_(m_tertiary_handle)); - return m_secondary_treaps.createTreap(primary_handle); - } + if (m_interval_tree.getRPTR_(m_tertiary_handle) != -1) + m_tertiary_stack.add(m_interval_tree.getRPTR_(m_tertiary_handle)); - private int createIntervalNode_() { - return m_interval_nodes.newElement(); - } + return true; + } - private void reset_(boolean b_new_intervals) { - if (b_new_intervals) { - m_b_sort_intervals = true; - m_b_constructing = true; - m_b_construction_ended = false; + private boolean left_() { + m_current_end_handle = m_next_end_handle; - if (m_end_indices_unique == null) - m_end_indices_unique = (AttributeStreamOfInt32) (AttributeStreamBase - .createIndexStream(0)); - else - m_end_indices_unique.resize(0); + if (m_current_end_handle != -1 && IntervalTreeImpl.isLeft_(getCurrentEndIndex_()) && m_interval_tree.getValue_(getCurrentEndIndex_()) <= m_query.vmax) { + m_next_end_handle = getNext_(); + return false; + } - if (m_intervals == null) - m_intervals = new ArrayList(0); - else - m_intervals.clear(); - } else { - assert (m_b_offline_dynamic && m_b_construction_ended); - m_b_sort_intervals = false; + m_function_index--; + return true; } - if (m_b_offline_dynamic) { - if (m_interval_handles == null) { - m_interval_handles = (AttributeStreamOfInt32) (AttributeStreamBase - .createIndexStream(0)); - m_secondary_treaps = new Treap(); - m_secondary_treaps.setComparator(new SecondaryComparator(this)); - } else { - m_secondary_treaps.clear(); + private boolean right_() { + m_current_end_handle = m_next_end_handle; + + if (m_current_end_handle != -1 && IntervalTreeImpl.isRight_(getCurrentEndIndex_()) && m_interval_tree.getValue_(getCurrentEndIndex_()) >= m_query.vmin) { + m_next_end_handle = getPrev_(); + return false; } - } else { - if (m_secondary_lists == null) - m_secondary_lists = new IndexMultiDCList(); - else - m_secondary_lists.clear(); - } - if (m_primary_nodes == null) { - m_interval_nodes = new StridedIndexTypeCollection(3); - m_primary_nodes = new StridedIndexTypeCollection( - m_b_offline_dynamic ? 8 : 7); - } else { - m_interval_nodes.deleteAll(false); - m_primary_nodes.deleteAll(false); + m_function_index--; + return true; } - m_root = -1; - m_c_count = 0; - } - - private void setDiscriminantIndices_(int primary_handle, int e_1, int e_2) { - setDiscriminantIndex1_(primary_handle, e_1); - setDiscriminantIndex2_(primary_handle, e_2); - } + private boolean all_() { + m_current_end_handle = m_next_end_handle; - private double getDiscriminant_(int primary_handle) { - int e_1 = getDiscriminantIndex1_(primary_handle); - if (e_1 == -1) - return NumberUtils.TheNaN; + if (m_current_end_handle != -1 && IntervalTreeImpl.isLeft_(getCurrentEndIndex_())) { + m_next_end_handle = getNext_(); + return false; + } - int e_2 = getDiscriminantIndex2_(primary_handle); - assert (e_2 != -1); + m_function_index--; + return true; + } - double v_1 = getValue_(e_1); - double v_2 = getValue_(e_2); + IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree, Envelope1D query, double tolerance) { + m_interval_tree = interval_tree; + m_tertiary_stack.reserve(20); + resetIterator(query, tolerance); + } - if (v_1 == v_2) - return v_1; + IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree, double query, double tolerance) { + m_interval_tree = interval_tree; + m_tertiary_stack.reserve(20); + resetIterator(query, tolerance); + } - return 0.5 * (v_1 + v_2); - } + IntervalTreeIteratorImpl(IntervalTreeImpl interval_tree) { + m_interval_tree = interval_tree; + m_tertiary_stack.reserve(20); + m_function_index = -1; + } - private boolean isActive_(int primary_handle) { - int secondary_handle = getSecondaryFromPrimary(primary_handle); + void resetIterator(Envelope1D query, double tolerance) { + m_query.vmin = query.vmin - tolerance; + m_query.vmax = query.vmax + tolerance; + m_tertiary_stack.resize(0); + m_function_index = 0; + m_function_stack[0] = State.initialize; + } - if (secondary_handle != -1) - return true; + void resetIterator(double query_min, double query_max, double tolerance) { + m_query.vmin = query_min - tolerance; + m_query.vmax = query_max + tolerance; + m_tertiary_stack.resize(0); + m_function_index = 0; + m_function_stack[0] = State.initialize; + } - int left_handle = getLeftPrimary_(primary_handle); + void resetIterator(double query, double tolerance) { + m_query.vmin = query - tolerance; + m_query.vmax = query + tolerance; + m_tertiary_stack.resize(0); + m_function_index = 0; + m_function_stack[0] = State.initialize; + } + } - if (left_handle == -1) - return false; + private static final class SecondaryComparator extends Treap.Comparator { + SecondaryComparator(IntervalTreeImpl interval_tree) { + m_interval_tree = interval_tree; + } - int right_handle = getRightPrimary_(primary_handle); + @Override + public int compare(Treap treap, int e_1, int node) { + int e_2 = treap.getElement(node); + double v_1 = m_interval_tree.getValue_(e_1); + double v_2 = m_interval_tree.getValue_(e_2); - if (right_handle == -1) - return false; + if (v_1 < v_2) + return -1; + if (v_1 == v_2) { + if (isLeft_(e_1) && isRight_(e_2)) + return -1; + if (isLeft_(e_2) && isRight_(e_1)) + return 1; + return 0; + } + return 1; + } - return true; + private IntervalTreeImpl m_interval_tree; } - private void setDiscriminantIndex1_(int primary_handle, int end_index) { - m_primary_nodes.setField(primary_handle, 0, end_index); + private int createTertiaryNode_(int discriminant_index_1) { + int tertiary_handle = m_tertiary_nodes.newElement(); + setDiscriminantIndex1_(tertiary_handle, discriminant_index_1); + return tertiary_handle; } - private void setDiscriminantIndex2_(int primary_handle, int end_index) { - m_primary_nodes.setField(primary_handle, 1, end_index); + private int createSecondary_(int tertiary_handle) { + if (!m_b_offline_dynamic) + return m_secondary_lists.createList(tertiary_handle); + + return m_secondary_treaps.createTreap(tertiary_handle); } - private void setLeftPrimary_(int primary_handle, int left_handle) { - m_primary_nodes.setField(primary_handle, 3, left_handle); + private int createIntervalNode_() { + return m_interval_nodes.newElement(); } - private void setRightPrimary_(int primary_handle, int right_handle) { - m_primary_nodes.setField(primary_handle, 4, right_handle); + private void setDiscriminantIndex1_(int tertiary_handle, int end_index) { + m_tertiary_nodes.setField(tertiary_handle, 0, end_index); } - private void setSecondaryToPrimary_(int primary_handle, int secondary_handle) { - m_primary_nodes.setField(primary_handle, 2, secondary_handle); + private void setSecondaryToTertiary_(int tertiary_handle, int secondary_handle) { + m_tertiary_nodes.setField(tertiary_handle, 1, secondary_handle); } - private void setLPTR_(int primary_handle, int lptr) { - m_primary_nodes.setField(primary_handle, 5, lptr); + private void setLPTR_(int tertiary_handle, int lptr) { + m_tertiary_nodes.setField(tertiary_handle, 2, lptr); } - private void setRPTR_(int primary_handle, int rptr) { - m_primary_nodes.setField(primary_handle, 6, rptr); + private void setRPTR_(int tertiary_handle, int rptr) { + m_tertiary_nodes.setField(tertiary_handle, 3, rptr); } - private void setPPTR_(int primary_handle, int pptr) { - m_primary_nodes.setField(primary_handle, 7, pptr); + private void setPPTR_(int tertiary_handle, int pptr) { + m_tertiary_nodes.setField(tertiary_handle, 4, pptr); } - private void setSecondaryToInterval_(int interval_handle, - int secondary_handle) { + private void setSecondaryToInterval_(int interval_handle, int secondary_handle) { m_interval_nodes.setField(interval_handle, 0, secondary_handle); } - private int addEndIndex(int secondary_handle, int end_index) { + private int addEndIndex_(int secondary_handle, int end_index) { int end_index_handle; if (!m_b_offline_dynamic) - end_index_handle = m_secondary_lists.addElement(secondary_handle, - end_index); + end_index_handle = m_secondary_lists.addElement(secondary_handle, end_index); else - end_index_handle = m_secondary_treaps.addElement(end_index, - secondary_handle); + end_index_handle = m_secondary_treaps.addElement(end_index, secondary_handle); return end_index_handle; } @@ -1124,58 +1092,24 @@ private void setRightEnd_(int interval_handle, int right_end_handle) { m_interval_nodes.setField(interval_handle, 2, right_end_handle); } - private int getFirst_(int secondary_handle) { - if (!m_b_offline_dynamic) - return m_secondary_lists.getFirst(secondary_handle); - - return m_secondary_treaps.getFirst(secondary_handle); - } - - private int getLast_(int secondary_handle) { - if (!m_b_offline_dynamic) - return m_secondary_lists.getLast(secondary_handle); - - return m_secondary_treaps.getLast(secondary_handle); - } - - private static boolean isLeft_(int end_index) { - return (end_index & 0x1) == 0; - } - - private static boolean isRight_(int end_index) { - return (end_index & 0x1) == 1; - } - - private int getDiscriminantIndex1_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 0); - } - - private int getDiscriminantIndex2_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 1); - } - - private int getSecondaryFromPrimary(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 2); - } - - private int getLeftPrimary_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 3); + private int getDiscriminantIndex1_(int tertiary_handle) { + return m_tertiary_nodes.getField(tertiary_handle, 0); } - private int getRightPrimary_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 4); + private int getSecondaryFromTertiary_(int tertiary_handle) { + return m_tertiary_nodes.getField(tertiary_handle, 1); } - private int getLPTR_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 5); + private int getLPTR_(int tertiary_handle) { + return m_tertiary_nodes.getField(tertiary_handle, 2); } - private int getRPTR_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 6); + private int getRPTR_(int tertiary_handle) { + return m_tertiary_nodes.getField(tertiary_handle, 3); } - private int getPPTR_(int primary_handle) { - return m_primary_nodes.getField(primary_handle, 7); + private int getPPTR_(int tertiary_handle) { + return m_tertiary_nodes.getField(tertiary_handle, 4); } private int getSecondaryFromInterval_(int interval_handle) { @@ -1191,76 +1125,32 @@ private int getRightEnd_(int interval_handle) { } private double getMin_(int i) { - Envelope1D interval = m_intervals.get(i); - return interval.vmin; + return (!m_b_envelopes_ref ? m_intervals.get(i).vmin : m_envelopes_ref.get(i).xmin); } private double getMax_(int i) { - Envelope1D interval = m_intervals.get(i); - return interval.vmax; + return (!m_b_envelopes_ref ? m_intervals.get(i).vmax : m_envelopes_ref.get(i).xmax); } - // *********** Helpers for Bucket sort************** - private BucketSort m_bucket_sort; - - private void sortEndIndices_(AttributeStreamOfInt32 end_indices, - int begin_, int end_) { - if (m_bucket_sort == null) - m_bucket_sort = new BucketSort(); + private int getFirst_(int secondary_handle) { + if (!m_b_offline_dynamic) + return m_secondary_lists.getFirst(secondary_handle); - IntervalTreeBucketSortHelper sorter = new IntervalTreeBucketSortHelper( - this); - m_bucket_sort.sort(end_indices, begin_, end_, sorter); + return m_secondary_treaps.getFirst(secondary_handle); } - private void sortEndIndicesHelper_(AttributeStreamOfInt32 end_indices, - int begin_, int end_) { - end_indices.Sort(begin_, end_, new EndPointsComparer(this)); - } + private int getLast_(int secondary_handle) { + if (!m_b_offline_dynamic) + return m_secondary_lists.getLast(secondary_handle); - private double getValue_(int e) { - Envelope1D interval = m_intervals.get(e >> 1); - double v = (isLeft_(e) ? interval.vmin : interval.vmax); - return v; + return m_secondary_treaps.getLast(secondary_handle); } - private static final class EndPointsComparer extends - AttributeStreamOfInt32.IntComparator { // For user sort - EndPointsComparer(IntervalTreeImpl interval_tree) { - m_interval_tree = interval_tree; - } - - @Override - public int compare(int e_1, int e_2) { - double v_1 = m_interval_tree.getValue_(e_1); - double v_2 = m_interval_tree.getValue_(e_2); - - if (v_1 < v_2 || (v_1 == v_2 && isLeft_(e_1) && isRight_(e_2))) - return -1; - - return 1; - } - - private IntervalTreeImpl m_interval_tree; + private static boolean isLeft_(int end_index) { + return (end_index & 0x1) == 0; } - private class IntervalTreeBucketSortHelper extends ClassicSort { // For - // bucket - // sort - IntervalTreeBucketSortHelper(IntervalTreeImpl interval_tree) { - m_interval_tree = interval_tree; - } - - @Override - public void userSort(int begin, int end, AttributeStreamOfInt32 indices) { - m_interval_tree.sortEndIndicesHelper_(indices, begin, end); - } - - @Override - public double getValue(int e) { - return m_interval_tree.getValue_(e); - } - - private IntervalTreeImpl m_interval_tree; + private static boolean isRight_(int end_index) { + return (end_index & 0x1) == 1; } } diff --git a/src/main/java/com/esri/core/geometry/JsonGeometryException.java b/src/main/java/com/esri/core/geometry/JsonGeometryException.java new file mode 100644 index 00000000..a5552901 --- /dev/null +++ b/src/main/java/com/esri/core/geometry/JsonGeometryException.java @@ -0,0 +1,40 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +/** + * A runtime exception raised when a JSON related exception occurs. + */ +public class JsonGeometryException extends GeometryException { + + /** + * Constructs a Json Geometry Exception with the given error string/message. + * + * @param str + * - The error string. + */ + public JsonGeometryException(String str) { + super(str); + } +} diff --git a/src/main/java/com/esri/core/geometry/JsonParserReader.java b/src/main/java/com/esri/core/geometry/JsonParserReader.java index 80666579..bf382dd9 100644 --- a/src/main/java/com/esri/core/geometry/JsonParserReader.java +++ b/src/main/java/com/esri/core/geometry/JsonParserReader.java @@ -24,10 +24,15 @@ package com.esri.core.geometry; import java.util.ArrayList; + import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.json.JSONArray; import org.json.JSONObject; +import org.json.JSONException; +import org.codehaus.jackson.JsonParseException; + +import java.io.IOException; final class JsonParserReader extends JsonReader { @@ -38,33 +43,33 @@ final class JsonParserReader extends JsonReader { } @Override - JsonToken nextToken() throws Exception { + JsonToken nextToken() throws JSONException, JsonParseException, IOException { JsonToken token = m_jsonParser.nextToken(); return token; } @Override - JsonToken currentToken() throws Exception { + JsonToken currentToken() throws JSONException, JsonParseException, IOException { return m_jsonParser.getCurrentToken(); } @Override - void skipChildren() throws Exception { + void skipChildren() throws JSONException, JsonParseException, IOException { m_jsonParser.skipChildren(); } @Override - String currentString() throws Exception { + String currentString() throws JSONException, JsonParseException, IOException { return m_jsonParser.getText(); } @Override - double currentDoubleValue() throws Exception { + double currentDoubleValue() throws JSONException, JsonParseException, IOException { return m_jsonParser.getValueAsDouble(); } @Override - int currentIntValue() throws Exception { + int currentIntValue() throws JSONException, JsonParseException, IOException { return m_jsonParser.getValueAsInt(); } } diff --git a/src/main/java/com/esri/core/geometry/JsonReader.java b/src/main/java/com/esri/core/geometry/JsonReader.java index ba3b8cca..b1f69d95 100644 --- a/src/main/java/com/esri/core/geometry/JsonReader.java +++ b/src/main/java/com/esri/core/geometry/JsonReader.java @@ -24,24 +24,28 @@ package com.esri.core.geometry; import java.util.ArrayList; + import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.json.JSONArray; import org.json.JSONObject; +import org.json.JSONException; +import org.codehaus.jackson.JsonParseException; +import java.io.IOException; abstract class JsonReader { - abstract JsonToken nextToken() throws Exception; + abstract JsonToken nextToken() throws JSONException, JsonParseException, IOException; - abstract JsonToken currentToken() throws Exception; + abstract JsonToken currentToken() throws JSONException, JsonParseException, IOException; - abstract void skipChildren() throws Exception; + abstract void skipChildren() throws JSONException, JsonParseException, IOException; - abstract String currentString() throws Exception; + abstract String currentString() throws JSONException, JsonParseException, IOException; - abstract double currentDoubleValue() throws Exception; + abstract double currentDoubleValue() throws JSONException, JsonParseException, IOException; - abstract int currentIntValue() throws Exception; + abstract int currentIntValue() throws JSONException, JsonParseException, IOException; } diff --git a/src/main/java/com/esri/core/geometry/JsonStringWriter.java b/src/main/java/com/esri/core/geometry/JsonStringWriter.java index f324a197..a8df0930 100644 --- a/src/main/java/com/esri/core/geometry/JsonStringWriter.java +++ b/src/main/java/com/esri/core/geometry/JsonStringWriter.java @@ -33,14 +33,14 @@ Object getJson() { @Override void startObject() { - next_(Action.addContainer); + next_(Action.addObject); m_jsonString.append('{'); m_functionStack.add(State.objectStart); } @Override void startArray() { - next_(Action.addContainer); + next_(Action.addArray); m_jsonString.append('['); m_functionStack.add(State.arrayStart); } @@ -57,6 +57,12 @@ void endArray() { m_jsonString.append(']'); } + @Override + void addFieldName(String fieldName) { + next_(Action.addKey); + appendQuote_(fieldName); + } + @Override void addPairObject(String fieldName) { next_(Action.addPair); @@ -90,11 +96,11 @@ void addPairDouble(String fieldName, double v) { } @Override - void addPairDoubleF(String fieldName, double v, int decimals) { + void addPairDouble(String fieldName, double v, int precision, boolean bFixedPoint) { next_(Action.addPair); appendQuote_(fieldName); m_jsonString.append(":"); - addValueDoubleF_(v, decimals); + addValueDouble_(v, precision, bFixedPoint); } @Override @@ -123,13 +129,13 @@ void addPairNull(String fieldName) { @Override void addValueObject() { - next_(Action.addContainer); + next_(Action.addObject); addValueObject_(); } @Override void addValueArray() { - next_(Action.addContainer); + next_(Action.addArray); addValueArray_(); } @@ -146,9 +152,9 @@ void addValueDouble(double v) { } @Override - void addValueDoubleF(double v, int decimals) { + void addValueDouble(double v, int precision, boolean bFixedPoint) { next_(Action.addTerminal); - addValueDoubleF_(v, decimals); + addValueDouble_(v, precision, bFixedPoint); } @Override @@ -202,13 +208,16 @@ private void addValueDouble_(double v) { StringUtils.appendDouble(v, 17, m_jsonString); } - private void addValueDoubleF_(double v, int decimals) { + private void addValueDouble_(double v, int precision, boolean bFixedPoint) { if (NumberUtils.isNaN(v)) { addValueNull_(); return; } - StringUtils.appendDoubleF(v, decimals, m_jsonString); + if (bFixedPoint) + StringUtils.appendDoubleF(v, precision, m_jsonString); + else + StringUtils.appendDouble(v, precision, m_jsonString); } private void addValueInt_(int v) { @@ -247,6 +256,9 @@ private void next_(int action) { case State.elementEnd: elementEnd_(action); break; + case State.fieldNameEnd: + fieldNameEnd_(action); + break; default: throw new GeometryException("internal error"); } @@ -259,7 +271,7 @@ private void accept_(int action) { } private void start_(int action) { - if (action == Action.addContainer) { + if ((action & Action.addContainer) != 0) { m_functionStack.removeLast(); } else { throw new GeometryException("invalid call"); @@ -267,18 +279,25 @@ private void start_(int action) { } private void objectStart_(int action) { + if (action != Action.popObject && action != Action.addPair && action != Action.addKey) + throw new GeometryException("invalid call"); + m_functionStack.removeLast(); if (action == Action.addPair) { m_functionStack.add(State.pairEnd); - } else if (action != Action.popObject) { - throw new GeometryException("invalid call"); + } else if (action == Action.addKey) { + m_functionStack.add(State.pairEnd); + m_functionStack.add(State.fieldNameEnd); } } private void pairEnd_(int action) { if (action == Action.addPair) { m_jsonString.append(','); + } else if (action == Action.addKey) { + m_jsonString.append(','); + m_functionStack.add(State.fieldNameEnd); } else if (action == Action.popObject) { m_functionStack.removeLast(); } else { @@ -287,12 +306,13 @@ private void pairEnd_(int action) { } private void arrayStart_(int action) { + if ((action & Action.addValue) == 0 && action != Action.popArray) + throw new GeometryException("invalid call"); + m_functionStack.removeLast(); if ((action & Action.addValue) != 0) { m_functionStack.add(State.elementEnd); - } else if (action != Action.popArray) { - throw new GeometryException("invalid call"); } } @@ -306,6 +326,14 @@ private void elementEnd_(int action) { } } + private void fieldNameEnd_(int action) { + if ((action & Action.addValue) == 0) + throw new GeometryException("invalid call"); + + m_functionStack.removeLast(); + m_jsonString.append(':'); + } + private void appendQuote_(String string) { int count = 0; int start = 0; diff --git a/src/main/java/com/esri/core/geometry/JsonValueReader.java b/src/main/java/com/esri/core/geometry/JsonValueReader.java index d7c4b639..91028624 100644 --- a/src/main/java/com/esri/core/geometry/JsonValueReader.java +++ b/src/main/java/com/esri/core/geometry/JsonValueReader.java @@ -24,10 +24,15 @@ package com.esri.core.geometry; import java.util.ArrayList; + import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.json.JSONArray; import org.json.JSONObject; +import org.json.JSONException; +import org.codehaus.jackson.JsonParseException; + +import java.io.IOException; final class JsonValueReader extends JsonReader { @@ -108,7 +113,7 @@ Object currentObject_() { } @Override - JsonToken nextToken() throws Exception { + JsonToken nextToken() throws JSONException, JsonParseException { if (m_parentStack.isEmpty()) { m_currentToken = JsonToken.NOT_AVAILABLE; return m_currentToken; @@ -170,12 +175,12 @@ JsonToken nextToken() throws Exception { } @Override - JsonToken currentToken() throws Exception { + JsonToken currentToken() throws JSONException, JsonParseException, IOException { return m_currentToken; } @Override - void skipChildren() throws Exception { + void skipChildren() throws JSONException, JsonParseException, IOException { assert (!m_parentStack.isEmpty()); if (m_currentToken != JsonToken.START_OBJECT && m_currentToken != JsonToken.START_ARRAY) { @@ -196,7 +201,7 @@ void skipChildren() throws Exception { } @Override - String currentString() throws Exception { + String currentString() throws JSONException, JsonParseException, IOException { if (m_currentToken == JsonToken.FIELD_NAME) { return m_objIters.get(m_objIters.size() - 1).getCurrentKey(); } @@ -209,7 +214,7 @@ String currentString() throws Exception { } @Override - double currentDoubleValue() throws Exception { + double currentDoubleValue() throws JSONException, JsonParseException, IOException { if (m_currentToken != JsonToken.VALUE_NUMBER_FLOAT && m_currentToken != JsonToken.VALUE_NUMBER_INT) { throw new GeometryException("invalid call"); } @@ -218,7 +223,7 @@ String currentString() throws Exception { } @Override - int currentIntValue() throws Exception { + int currentIntValue() throws JSONException, JsonParseException, IOException { if (m_currentToken != JsonToken.VALUE_NUMBER_INT) { throw new GeometryException("invalid call"); } diff --git a/src/main/java/com/esri/core/geometry/JsonWriter.java b/src/main/java/com/esri/core/geometry/JsonWriter.java index 0baa0f2b..095f50e1 100644 --- a/src/main/java/com/esri/core/geometry/JsonWriter.java +++ b/src/main/java/com/esri/core/geometry/JsonWriter.java @@ -35,6 +35,8 @@ abstract class JsonWriter { abstract void endArray(); + abstract void addFieldName(String fieldName); + abstract void addPairObject(String fieldName); abstract void addPairArray(String fieldName); @@ -43,7 +45,7 @@ abstract class JsonWriter { abstract void addPairDouble(String fieldName, double v); - abstract void addPairDoubleF(String fieldName, double v, int decimals); + abstract void addPairDouble(String fieldName, double v, int precision, boolean bFixedPoint); abstract void addPairInt(String fieldName, int v); @@ -59,7 +61,7 @@ abstract class JsonWriter { abstract void addValueDouble(double v); - abstract void addValueDoubleF(double v, int decimals); + abstract void addValueDouble(double v, int precision, boolean bFixedPoint); abstract void addValueInt(int v); @@ -70,11 +72,14 @@ abstract class JsonWriter { protected interface Action { static final int accept = 0; - static final int addContainer = 1; + static final int addObject = 1; + static final int addArray = 2; static final int popObject = 4; static final int popArray = 8; - static final int addPair = 16; + static final int addKey = 16; static final int addTerminal = 32; + static final int addPair = 64; + static final int addContainer = addObject | addArray; static final int addValue = addContainer | addTerminal; } @@ -85,6 +90,7 @@ protected interface State { static final int objectStart = 2; static final int arrayStart = 3; static final int pairEnd = 4; - static final int elementEnd = 6; + static final int elementEnd = 5; + static final int fieldNameEnd = 6; } } diff --git a/src/main/java/com/esri/core/geometry/Line.java b/src/main/java/com/esri/core/geometry/Line.java index a3c5e570..4eccd513 100644 --- a/src/main/java/com/esri/core/geometry/Line.java +++ b/src/main/java/com/esri/core/geometry/Line.java @@ -35,10 +35,6 @@ */ public final class Line extends Segment implements Serializable { - private static final long serialVersionUID = 2L;// TODO:remove as we use - // writeReplace and - // GeometrySerializer - @Override public Geometry.Type getType() { return Type.Line; @@ -92,7 +88,7 @@ public Line() { m_description = vd; } - Line(double x1, double y1, double x2, double y2) { + public Line(double x1, double y1, double x2, double y2) { m_description = VertexDescriptionDesignerImpl.getDefaultDescriptor2D(); setStartXY(x1, y1); setEndXY(x2, y2); @@ -199,7 +195,7 @@ public Geometry createInstance() { } @Override - void getCoord2D(double t, Point2D pt) { + public void getCoord2D(double t, Point2D pt) { // We want: // 1. When t == 0, get exactly Start // 2. When t == 1, get exactly End @@ -209,7 +205,7 @@ void getCoord2D(double t, Point2D pt) { } @Override - Segment cut(double t1, double t2) { + public Segment cut(double t1, double t2) { SegmentBuffer segmentBuffer = new SegmentBuffer(); cut(t1, t2, segmentBuffer); return segmentBuffer.get(); @@ -270,7 +266,7 @@ public double getAttributeAsDbl(double t, int semantics, int ordinate) { } @Override - double getClosestCoordinate(Point2D inputPt, boolean bExtrapolate) { + public double getClosestCoordinate(Point2D inputPt, boolean bExtrapolate) { double vx = m_xEnd - m_xStart; double vy = m_yEnd - m_yStart; double v2 = vx * vx + vy * vy; @@ -390,7 +386,7 @@ boolean _isIntersectingPoint(Point2D pt, double tolerance, * given tolerance. */ @Override - boolean isIntersecting(Point2D pt, double tolerance) { + public boolean isIntersecting(Point2D pt, double tolerance) { return _isIntersectingPoint(pt, tolerance, false); } diff --git a/src/main/java/com/esri/core/geometry/LnSrlzr.java b/src/main/java/com/esri/core/geometry/LnSrlzr.java new file mode 100644 index 00000000..b1bd001f --- /dev/null +++ b/src/main/java/com/esri/core/geometry/LnSrlzr.java @@ -0,0 +1,93 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +//This is a writeReplace class for Lin +public class LnSrlzr implements Serializable { + private static final long serialVersionUID = 1L; + double[] attribs; + int descriptionBitMask; + + public Object readResolve() throws ObjectStreamException { + Line ln = null; + if (descriptionBitMask == -1) + return null; + + try { + VertexDescription vd = VertexDescriptionDesignerImpl + .getVertexDescription(descriptionBitMask); + ln = new Line(vd); + if (attribs != null) { + ln.setStartXY(attribs[0], attribs[1]); + ln.setEndXY(attribs[2], attribs[3]); + int index = 4; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + ln.setStartAttribute(semantics, ord, attribs[index++]); + ln.setEndAttribute(semantics, ord, attribs[index++]); + } + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot read geometry from stream"); + } + + return ln; + } + + public void setGeometryByValue(Line ln) throws ObjectStreamException { + try { + attribs = null; + if (ln == null) { + descriptionBitMask = -1; + } + + VertexDescription vd = ln.getDescription(); + descriptionBitMask = vd.m_semanticsBitArray; + + attribs = new double[vd.getTotalComponentCount() * 2]; + attribs[0] = ln.getStartX(); + attribs[1] = ln.getStartY(); + attribs[2] = ln.getEndX(); + attribs[3] = ln.getEndY(); + int index = 4; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + attribs[index++] = ln.getStartAttributeAsDbl(semantics, ord); + attribs[index++] = ln.getEndAttributeAsDbl(semantics, ord); + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot serialize this geometry"); + } + } +} diff --git a/src/main/java/com/esri/core/geometry/MathUtils.java b/src/main/java/com/esri/core/geometry/MathUtils.java index 208fe4ba..92d5d8dd 100644 --- a/src/main/java/com/esri/core/geometry/MathUtils.java +++ b/src/main/java/com/esri/core/geometry/MathUtils.java @@ -135,11 +135,21 @@ static int sign(double value) { return value < 0 ? -1 : (value > 0) ? 1 : 0; } + /** + * Rounds towards zero. + */ + static double truncate(double v) { + if (v >= 0) + return Math.floor(v); + else + return -Math.floor(-v); + } + /** * C fmod function. */ static double FMod(double x, double y) { - return x - Math.floor(x / y) * y; + return x - truncate(x / y) * y; } @@ -184,22 +194,26 @@ static double lerp(double start_, double end_, double t) { *It also guarantees that for 0 <= t <= 1, the interpolated value v is between start and end. */ static void lerp(Point2D start_, Point2D end_, double t, Point2D result) { + assert(start_ != result); // When end == start, we want result to be equal to start, for all t // values. At the same time, when end != start, we want the result to be // equal to start for t==0 and end for t == 1.0 // The regular formula end_ * t + (1.0 - t) * start_, when end_ == // start_, and t at 1/3, produces value different from start + double rx, ry; if (t <= 0.5) { - result.x = start_.x + (end_.x - start_.x) * t; - result.y = start_.y + (end_.y - start_.y) * t; + rx = start_.x + (end_.x - start_.x) * t; + ry = start_.y + (end_.y - start_.y) * t; } else { - result.x = end_.x - (end_.x - start_.x) * (1.0 - t); - result.y = end_.y - (end_.y - start_.y) * (1.0 - t); + rx = end_.x - (end_.x - start_.x) * (1.0 - t); + ry = end_.y - (end_.y - start_.y) * (1.0 - t); } - assert (t < 0 || t > 1.0 || (result.x >= start_.x && result.x <= end_.x) || (result.x <= start_.x && result.x >= end_.x)); - assert (t < 0 || t > 1.0 || (result.y >= start_.y && result.y <= end_.y) || (result.y <= start_.y && result.y >= end_.y)); + assert (t < 0 || t > 1.0 || (rx >= start_.x && rx <= end_.x) || (rx <= start_.x && rx >= end_.x)); + assert (t < 0 || t > 1.0 || (ry >= start_.y && ry <= end_.y) || (ry <= start_.y && ry >= end_.y)); + result.x = rx; + result.y = ry; } static void lerp(double start_x, double start_y, double end_x, double end_y, double t, Point2D result) { diff --git a/src/main/java/com/esri/core/geometry/MultiPath.java b/src/main/java/com/esri/core/geometry/MultiPath.java index 8e778725..48cf8bd9 100644 --- a/src/main/java/com/esri/core/geometry/MultiPath.java +++ b/src/main/java/com/esri/core/geometry/MultiPath.java @@ -39,12 +39,12 @@ public VertexDescription getDescription() { } @Override - void assignVertexDescription(VertexDescription src) { + public void assignVertexDescription(VertexDescription src) { m_impl.assignVertexDescription(src); } @Override - void mergeVertexDescription(VertexDescription src) { + public void mergeVertexDescription(VertexDescription src) { m_impl.mergeVertexDescription(src); } @@ -277,6 +277,41 @@ void addPath(Point2D[] points, int count, boolean bForward) { m_impl.addPath(points, count, bForward); } + /** + * Adds segments from a source multipath to this MultiPath. + * + * @param src + * The source MultiPath to add segments from. + * @param srcPathIndex + * The index of the path in the the source MultiPath. + * @param srcSegmentFrom + * The index of first segment in the path to start adding from. + * The value has to be between 0 and + * src.getSegmentCount(srcPathIndex) - 1. + * @param srcSegmentCount + * The number of segments to add. If 0, the function does + * nothing. + * @param bStartNewPath + * When true, a new path is added and segments are added to it. + * Otherwise the segments are added to the last path of this + * MultiPath. + * + * If bStartNewPath false, the first point of the first source + * segment is not added. This is done to ensure proper connection + * to existing segments. When the source path is closed, and the + * closing segment is among those to be added, it is added also + * as a closing segment, not as a real segment. Use add_segment + * instead if you do not like that behavior. + * + * This MultiPath obtains all missing attributes from the src + * MultiPath. + */ + public void addSegmentsFromPath(MultiPath src, int srcPathIndex, + int srcSegmentFrom, int srcSegmentCount, boolean bStartNewPath) { + m_impl.addSegmentsFromPath((MultiPathImpl) src._getImpl(), + srcPathIndex, srcSegmentFrom, srcSegmentCount, bStartNewPath); + } + /** * Adds a new segment to this multipath. * @@ -724,12 +759,12 @@ public int hashCode() { } @Override - void getPointByVal(int index, Point outPoint) { + public void getPointByVal(int index, Point outPoint) { m_impl.getPointByVal(index, outPoint); } @Override - void setPointByVal(int index, Point point) { + public void setPointByVal(int index, Point point) { m_impl.setPointByVal(index, point); } diff --git a/src/main/java/com/esri/core/geometry/MultiPathImpl.java b/src/main/java/com/esri/core/geometry/MultiPathImpl.java index 43ed98a5..f6d606bb 100644 --- a/src/main/java/com/esri/core/geometry/MultiPathImpl.java +++ b/src/main/java/com/esri/core/geometry/MultiPathImpl.java @@ -1923,18 +1923,27 @@ public boolean equals(Object other) { if (pathCount != pathCountOther) return false; - if (m_paths != null + if (pathCount > 0 && m_paths != null && !m_paths.equals(otherMultiPath.m_paths, 0, pathCount + 1)) return false; - - if (m_fill_rule != otherMultiPath.m_fill_rule) - return false; - if (m_pathFlags != null - && !m_pathFlags - .equals(otherMultiPath.m_pathFlags, 0, pathCount)) + if (m_fill_rule != otherMultiPath.m_fill_rule) return false; + { + // Note: OGC flags do not participate in the equals operation by + // design. + // Because for the polygon pathFlags will have all enum_closed set, + // we do not need to compare this stream. Only for polyline. + // Polyline does not have OGC flags set. + if (!m_bPolygon) { + if (m_pathFlags != null + && !m_pathFlags.equals(otherMultiPath.m_pathFlags, 0, + pathCount)) + return false; + } + } + return super.equals(other); } @@ -2101,7 +2110,7 @@ protected void _updateOGCFlags() { _updateRingAreas2D(); int pathCount = getPathCount(); - if (m_pathFlags == null || m_pathFlags.size() < pathCount) + if (pathCount > 0 && (m_pathFlags == null || m_pathFlags.size() < pathCount)) m_pathFlags = (AttributeStreamOfInt8) AttributeStreamBase .createByteStream(pathCount + 1); diff --git a/src/main/java/com/esri/core/geometry/MultiPoint.java b/src/main/java/com/esri/core/geometry/MultiPoint.java index 2d2cbf28..8a3f6f6a 100644 --- a/src/main/java/com/esri/core/geometry/MultiPoint.java +++ b/src/main/java/com/esri/core/geometry/MultiPoint.java @@ -32,7 +32,7 @@ * information where the order and individual identity of each point is not an * essential characteristic of the point set. */ -public final class MultiPoint extends MultiVertexGeometry implements +public class MultiPoint extends MultiVertexGeometry implements Serializable { private static final long serialVersionUID = 2L; @@ -122,6 +122,15 @@ public void add(double x, double y) { m_impl.add(x, y); } + /** + * Adds a point with the specified X, Y coordinates to this multipoint. + * + * @param pt the point to add + */ + public void add(Point2D pt) { + m_impl.add(pt.x, pt.y); + } + /** * Adds a 3DPoint with the specified X, Y, Z coordinates to this multipoint. * @@ -265,7 +274,7 @@ public void addAttribute(int semantics) { } @Override - void assignVertexDescription(VertexDescription src) { + public void assignVertexDescription(VertexDescription src) { m_impl.assignVertexDescription(src); } @@ -280,7 +289,7 @@ public void dropAttribute(int semantics) { } @Override - void mergeVertexDescription(VertexDescription src) { + public void mergeVertexDescription(VertexDescription src) { m_impl.mergeVertexDescription(src); } @@ -346,12 +355,12 @@ int queryCoordinates(Point2D[] dst, int dstSize, int beginIndex, } @Override - void getPointByVal(int index, Point outPoint) { + public void getPointByVal(int index, Point outPoint) { m_impl.getPointByVal(index, outPoint); } @Override - void setPointByVal(int index, Point pointSrc) { + public void setPointByVal(int index, Point pointSrc) { m_impl.setPointByVal(index, pointSrc); } diff --git a/src/main/java/com/esri/core/geometry/MultiVertexGeometry.java b/src/main/java/com/esri/core/geometry/MultiVertexGeometry.java index 668d6b33..c164b48c 100644 --- a/src/main/java/com/esri/core/geometry/MultiVertexGeometry.java +++ b/src/main/java/com/esri/core/geometry/MultiVertexGeometry.java @@ -197,7 +197,7 @@ abstract void setAttribute(int semantics, int index, int ordinate, * Returns given vertex of the Geometry. The outPoint will have same * VertexDescription as this Geometry. */ - abstract void getPointByVal(int index, Point outPoint); + public abstract void getPointByVal(int index, Point outPoint); /** * Sets the vertex at given index of the Geometry. @@ -214,6 +214,6 @@ abstract void setAttribute(int semantics, int index, int ordinate, * the Geometry will be set to the default values (see * VertexDescription::GetDefaultValue). */ - abstract void setPointByVal(int index, Point pointSrc); + public abstract void setPointByVal(int index, Point pointSrc); } diff --git a/src/main/java/com/esri/core/geometry/MultiVertexGeometryImpl.java b/src/main/java/com/esri/core/geometry/MultiVertexGeometryImpl.java index 11ef7ed4..6e2694c7 100644 --- a/src/main/java/com/esri/core/geometry/MultiVertexGeometryImpl.java +++ b/src/main/java/com/esri/core/geometry/MultiVertexGeometryImpl.java @@ -180,9 +180,8 @@ public MultiVertexGeometryImpl() { m_accelerators = null; } - // Checked vs. Jan 11, 2011 @Override - void getPointByVal(int index, Point dst) { + public void getPointByVal(int index, Point dst) { if (index < 0 || index >= m_pointCount) // TODO throw new GeometryException("index out of bounds"); @@ -212,9 +211,8 @@ void getPointByVal(int index, Point dst) { } } - // Checked vs. Jan 11, 2011 @Override - protected void setPointByVal(int index, Point src) { + public void setPointByVal(int index, Point src) { if (index < 0 || index >= m_pointCount) throw new GeometryException("index out of bounds"); @@ -1110,6 +1108,9 @@ public abstract boolean _buildRasterizedGeometryAccelerator( public abstract boolean _buildQuadTreeAccelerator( GeometryAccelerationDegree d); - // //////////////////METHODS To REMOVE /////////////////////// + @Override + public String toString() { + return "MultiVertexGeometryImpl"; + } } diff --git a/src/main/java/com/esri/core/geometry/NumberUtils.java b/src/main/java/com/esri/core/geometry/NumberUtils.java index 209df086..4ee00a1e 100644 --- a/src/main/java/com/esri/core/geometry/NumberUtils.java +++ b/src/main/java/com/esri/core/geometry/NumberUtils.java @@ -68,6 +68,12 @@ static double NaN() { return Double.NaN; } + //combines two hash values + public static int hashCombine(int hash1, int hash2) { + return (hash1 * 31 + hash2) & 0x7FFFFFFF; + } + + //makes a hash out of an int static int hash(int n) { int hash = 5381; hash = ((hash << 5) + hash) + (n & 0xFF); /* hash * 33 + c */ @@ -78,12 +84,14 @@ static int hash(int n) { return hash; } + // //makes a hash out of an double static int hash(double d) { long bits = Double.doubleToLongBits(d); int hc = (int) (bits ^ (bits >>> 32)); return hash(hc); } + //adds an int to a hash value static int hash(int hashIn, int n) { int hash = ((hashIn << 5) + hashIn) + (n & 0xFF); /* hash * 33 + c */ hash = ((hash << 5) + hash) + ((n >> 8) & 0xFF); @@ -93,6 +101,7 @@ static int hash(int hashIn, int n) { return hash; } + //adds a double to a hash value static int hash(int hash, double d) { long bits = Double.doubleToLongBits(d); int hc = (int) (bits ^ (bits >>> 32)); diff --git a/src/main/java/com/esri/core/geometry/OperatorBuffer.java b/src/main/java/com/esri/core/geometry/OperatorBuffer.java index cca44f54..93c71b02 100644 --- a/src/main/java/com/esri/core/geometry/OperatorBuffer.java +++ b/src/main/java/com/esri/core/geometry/OperatorBuffer.java @@ -58,6 +58,31 @@ public abstract Geometry execute(Geometry inputGeometry, SpatialReference sr, double distance, ProgressTracker progressTracker); + /** + *Creates a buffer around the input geometries + * + *@param input_geometries The geometries to buffer. + *@param sr The Spatial_reference of the Geometries. It is used to obtain the tolerance. Can be null. + *@param distances The buffer distances for the Geometries. If the size of the distances array is less than the number of geometries in the input_geometries, the last distance value is used for the rest of geometries. + *@param max_deviation The max deviation of the result buffer from the true buffer in the units of the sr. + *When max_deviation is NaN or 0, it is replaced with 1e-5 * abs(distance). + *When max_deviation is larger than MIN = 0.5 * abs(distance), it is replaced with MIN. See below for more information. + *@param max_vertices_in_full_circle The maximum number of vertices in polygon produced from a buffered point. A value of 96 is used in methods that do not accept max_vertices_in_full_circle. + *If the value is less than MIN=12, it is set to MIN. See below for more information. + *@param b_union If True, the buffered geometries will be unioned, otherwise they wont be unioned. + *@param progress_tracker The progress tracker that allows to cancel the operation. Pass null if not needed. + * + *The max_deviation and max_vertices_in_full_circle control the quality of round joins in the buffer. That is, the precision of the buffer is max_deviation unless + *the number of required vertices is too large. + *The max_vertices_in_full_circle controls how many vertices can be in each round join in the buffer. It is approximately equal to the number of vertices in the polygon around a + *buffered point. It has a priority over max_deviation. The max deviation is the distance from the result polygon to a true buffer. + *The real deviation is calculated as the max(max_deviation, abs(distance) * (1 - cos(PI / max_vertex_in_complete_circle))). + * + *Note that max_deviation can be exceeded because geometry is generalized with 0.25 * real_deviation, also input segments closer than 0.25 * real_deviation are + *snapped to a point. + */ + abstract GeometryCursor execute(GeometryCursor input_geometries, SpatialReference sr, double[] distances, double max_deviation, int max_vertices_in_full_circle, boolean b_union, ProgressTracker progress_tracker); + public static OperatorBuffer local() { return (OperatorBuffer) OperatorFactoryLocal.getInstance().getOperator( Type.Buffer); diff --git a/src/main/java/com/esri/core/geometry/OperatorBufferCursor.java b/src/main/java/com/esri/core/geometry/OperatorBufferCursor.java index 397ec89c..dd5716ca 100644 --- a/src/main/java/com/esri/core/geometry/OperatorBufferCursor.java +++ b/src/main/java/com/esri/core/geometry/OperatorBufferCursor.java @@ -25,25 +25,29 @@ package com.esri.core.geometry; class OperatorBufferCursor extends GeometryCursor { - + Bufferer m_bufferer = new Bufferer(); private GeometryCursor m_inputGeoms; private SpatialReferenceImpl m_Spatial_reference; private ProgressTracker m_progress_tracker; private double[] m_distances; private Envelope2D m_currentUnionEnvelope2D; - private boolean m_bUnion; - + double m_max_deviation; + int m_max_vertices_in_full_circle; private int m_index; private int m_dindex; OperatorBufferCursor(GeometryCursor inputGeoms, SpatialReference sr, - double[] distances, boolean b_union, + double[] distances, + double max_deviation, + int max_vertices, + boolean b_union, ProgressTracker progress_tracker) { m_index = -1; m_inputGeoms = inputGeoms; + m_max_deviation = max_deviation; + m_max_vertices_in_full_circle = max_vertices; m_Spatial_reference = (SpatialReferenceImpl) (sr); m_distances = distances; - m_bUnion = b_union; m_currentUnionEnvelope2D = new Envelope2D(); m_currentUnionEnvelope2D.setEmpty(); m_dindex = -1; @@ -72,7 +76,7 @@ public int getGeometryID() { // virtual bool IsRecycling() OVERRIDE { return false; } Geometry buffer(Geometry geom, double distance) { - return Bufferer.buffer(geom, distance, m_Spatial_reference, - NumberUtils.TheNaN, 96, m_progress_tracker); + return m_bufferer.buffer(geom, distance, m_Spatial_reference, + m_max_deviation, m_max_vertices_in_full_circle, m_progress_tracker); } } diff --git a/src/main/java/com/esri/core/geometry/OperatorBufferLocal.java b/src/main/java/com/esri/core/geometry/OperatorBufferLocal.java index 5e195d57..8c44225f 100644 --- a/src/main/java/com/esri/core/geometry/OperatorBufferLocal.java +++ b/src/main/java/com/esri/core/geometry/OperatorBufferLocal.java @@ -30,15 +30,8 @@ class OperatorBufferLocal extends OperatorBuffer { public GeometryCursor execute(GeometryCursor inputGeometries, SpatialReference sr, double[] distances, boolean bUnion, ProgressTracker progressTracker) { - if (bUnion) { - OperatorBufferCursor cursor = new OperatorBufferCursor( - inputGeometries, sr, distances, false, progressTracker); - return ((OperatorUnion) OperatorFactoryLocal.getInstance() - .getOperator(Operator.Type.Union)).execute(cursor, sr, - progressTracker); - } else - return new OperatorBufferCursor(inputGeometries, sr, distances, - false, progressTracker); + return execute(inputGeometries, sr, distances, NumberUtils.NaN(), 96, + bUnion, progressTracker); } @Override @@ -54,4 +47,20 @@ public Geometry execute(Geometry inputGeometry, SpatialReference sr, return outputCursor.next(); } + @Override + public GeometryCursor execute(GeometryCursor inputGeometries, + SpatialReference sr, double[] distances, double max_deviation, + int max_vertices_in_full_circle, boolean b_union, + ProgressTracker progressTracker) { + if (b_union) { + OperatorBufferCursor cursor = new OperatorBufferCursor( + inputGeometries, sr, distances, max_deviation, + max_vertices_in_full_circle, false, progressTracker); + return OperatorUnion.local().execute(cursor, sr, progressTracker);// (int)Operator_union::Options::enum_disable_edge_dissolver + } else { + return new OperatorBufferCursor(inputGeometries, sr, distances, + max_deviation, max_vertices_in_full_circle, false, + progressTracker); + } + } } diff --git a/src/main/java/com/esri/core/geometry/OperatorConvexHullCursor.java b/src/main/java/com/esri/core/geometry/OperatorConvexHullCursor.java index 11b32738..c90c9d72 100644 --- a/src/main/java/com/esri/core/geometry/OperatorConvexHullCursor.java +++ b/src/main/java/com/esri/core/geometry/OperatorConvexHullCursor.java @@ -30,9 +30,8 @@ class OperatorConvexHullCursor extends GeometryCursor { private GeometryCursor m_inputGeometryCursor; private int m_index; ConvexHull m_hull = new ConvexHull(); - - OperatorConvexHullCursor(boolean b_merge, GeometryCursor geoms, - ProgressTracker progress_tracker) { + + OperatorConvexHullCursor(boolean b_merge, GeometryCursor geoms, ProgressTracker progress_tracker) { m_index = -1; if (geoms == null) throw new IllegalArgumentException(); @@ -47,8 +46,7 @@ class OperatorConvexHullCursor extends GeometryCursor { public Geometry next() { if (m_b_merge) { if (!m_b_done) { - Geometry result = calculateConvexHullMerging_( - m_inputGeometryCursor, m_progress_tracker); + Geometry result = calculateConvexHullMerging_(m_inputGeometryCursor, m_progress_tracker); m_b_done = true; return result; } @@ -74,8 +72,7 @@ public int getGeometryID() { return m_index; } - private Geometry calculateConvexHullMerging_(GeometryCursor geoms, - ProgressTracker progress_tracker) { + private Geometry calculateConvexHullMerging_(GeometryCursor geoms, ProgressTracker progress_tracker) { Geometry geometry; while ((geometry = geoms.next()) != null) @@ -83,20 +80,19 @@ private Geometry calculateConvexHullMerging_(GeometryCursor geoms, return m_hull.getBoundingGeometry(); } - + @Override public boolean tock() { if (m_b_done) return true; - - if (!m_b_merge) - { + + if (!m_b_merge) { //Do not use tick/tock with the non-merging convex hull. //Call tick/next instead, //because tick pushes geometry into the cursor, and next performs a single convex hull on it. throw new GeometryException("Invalid call for non merging convex hull."); } - + Geometry geometry = m_inputGeometryCursor.next(); if (geometry != null) { m_hull.addGeometry(geometry); @@ -106,33 +102,64 @@ public boolean tock() { } } - static Geometry calculateConvexHull_(Geometry geom, - ProgressTracker progress_tracker) { - if (isConvex_(geom, progress_tracker)) - return geom; - - int type = geom.getType().value(); + static Geometry calculateConvexHull_(Geometry geom, ProgressTracker progress_tracker) { + if (geom.isEmpty()) + return geom.createInstance(); - if (MultiPath.isSegment(type)) { - Polyline polyline = new Polyline(geom.getDescription()); - polyline.addSegment((Segment) geom, true); - return polyline; - } + Geometry.Type type = geom.getType(); - if (type == Geometry.GeometryType.MultiPoint) { - MultiPoint multi_point = (MultiPoint) geom; - if (multi_point.getPointCount() == 2) { + if (Geometry.isSegment(type.value())) {// Segments are always returned either as a Point or Polyline + Segment segment = (Segment) geom; + if (segment.getStartXY().equals(segment.getEndXY())) { + Point point = new Point(); + segment.queryStart(point); + return point; + } else { + Point pt = new Point(); + Polyline polyline = new Polyline(geom.getDescription()); + segment.queryStart(pt); + polyline.startPath(pt); + segment.queryEnd(pt); + polyline.lineTo(pt); + return polyline; + } + } else if (type == Geometry.Type.Envelope) { + Envelope envelope = (Envelope) geom; + Envelope2D env = new Envelope2D(); + envelope.queryEnvelope2D(env); + if (env.xmin == env.xmax && env.ymin == env.ymax) { + Point point = new Point(); + envelope.queryCornerByVal(0, point); + return point; + } else if (env.xmin == env.xmax || env.ymin == env.ymax) { Point pt = new Point(); Polyline polyline = new Polyline(geom.getDescription()); - multi_point.getPointByVal(0, pt); + envelope.queryCornerByVal(0, pt); polyline.startPath(pt); - multi_point.getPointByVal(1, pt); + envelope.queryCornerByVal(1, pt); polyline.lineTo(pt); return polyline; + } else { + Polygon polygon = new Polygon(geom.getDescription()); + polygon.addEnvelope(envelope, false); + return polygon; } } - Polygon convex_hull = ConvexHull.construct((MultiVertexGeometry) geom); + if (isConvex_(geom, progress_tracker)) { + if (type == Geometry.Type.MultiPoint) {// Downgrade to a Point for simplistic output + MultiPoint multi_point = (MultiPoint) geom; + Point point = new Point(); + multi_point.getPointByVal(0, point); + return point; + } + + return geom; + } + + assert (Geometry.isMultiVertex(type.value())); + + Geometry convex_hull = ConvexHull.construct((MultiVertexGeometry) geom); return convex_hull; } @@ -140,44 +167,52 @@ static boolean isConvex_(Geometry geom, ProgressTracker progress_tracker) { if (geom.isEmpty()) return true; // vacuously true - int type = geom.getType().value(); + Geometry.Type type = geom.getType(); - if (type == Geometry.GeometryType.Point) + if (type == Geometry.Type.Point) return true; // vacuously true - if (type == Geometry.GeometryType.Envelope) - return true; // always convex + if (type == Geometry.Type.Envelope) { + Envelope envelope = (Envelope) geom; + if (envelope.getXMin() == envelope.getXMax() || envelope.getYMin() == envelope.getYMax()) + return false; - if (MultiPath.isSegment(type)) - return false; // upgrade to polyline + return true; + } - if (type == Geometry.GeometryType.MultiPoint) { + if (MultiPath.isSegment(type.value())) { + Segment segment = (Segment) geom; + if (segment.getStartXY().equals(segment.getEndXY())) + return false; + + return true; // true, but we will upgrade to a Polyline for the ConvexHull operation + } + + if (type == Geometry.Type.MultiPoint) { MultiPoint multi_point = (MultiPoint) geom; if (multi_point.getPointCount() == 1) - return true; // vacuously true + return true; // vacuously true, but we will downgrade to a Point for the ConvexHull operation - return false; // upgrade to polyline if point count is 2, otherwise - // create convex hull + return false; } - if (type == Geometry.GeometryType.Polyline) { + if (type == Geometry.Type.Polyline) { Polyline polyline = (Polyline) geom; - if (polyline.getPathCount() == 1 && polyline.getPointCount() <= 2) - return true; // vacuously true + if (polyline.getPathCount() == 1 && polyline.getPointCount() == 2) { + if (!polyline.getXY(0).equals(polyline.getXY(1))) + return true; // vacuously true + } return false; // create convex hull } Polygon polygon = (Polygon) geom; - if (polygon.getPathCount() != 1) + if (polygon.getPathCount() != 1 || polygon.getPointCount() < 3) return false; - if (polygon.getPointCount() <= 2) - return true; // vacuously true - return ConvexHull.isPathConvex(polygon, 0, progress_tracker); } } diff --git a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJson.java b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJson.java index 9a1ac765..6f9d506c 100644 --- a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJson.java +++ b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJson.java @@ -3,7 +3,7 @@ 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 + 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, @@ -28,21 +28,53 @@ *Export to GeoJson format. */ public abstract class OperatorExportToGeoJson extends Operator { - @Override - public Type getType() { - return Type.ExportToGeoJson; - } + @Override + public Type getType() { + return Type.ExportToGeoJson; + } - public abstract JsonCursor execute(SpatialReference spatialReference, GeometryCursor geometryCursor); + /** + * Performs the ExportToGeoJson operation + * @param spatialReference The SpatialReference of the Geometry. Will be written as "crs":null if the spatialReference is null. + * @param geometryCursor The cursor of geometries to write as GeoJson. + * @return Returns a JsonCursor. + */ + public abstract JsonCursor execute(SpatialReference spatialReference, GeometryCursor geometryCursor); - public abstract String execute(SpatialReference spatialReference, Geometry geometry); + /** + * Performs the ExportToGeoJson operation + * @param spatialReference The SpatialReference of the Geometry. Will be written as "crs":null if the spatialReference is null. + * @param geometry The Geometry to write as GeoJson. + * @return Returns a string in GeoJson format. + */ + public abstract String execute(SpatialReference spatialReference, Geometry geometry); - public abstract String execute(int exportFlags, SpatialReference spatialReference, Geometry geometry); - - public abstract String execute(Geometry geometry); + /** + * Performs the ExportToGeoJson operation + * @param exportFlags Use the {@link GeoJsonExportFlags} interface. + * @param spatialReference The SpatialReference of the Geometry. Will be written as "crs":null if the spatialReference is null. + * @param geometry The Geometry to write as GeoJson. + * @return Returns a string in GeoJson format. + */ + public abstract String execute(int exportFlags, SpatialReference spatialReference, Geometry geometry); - public static OperatorExportToGeoJson local() { - return (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance() - .getOperator(Type.ExportToGeoJson); - } + /** + * Performs the ExportToGeoJson operation. Will not write out a spatial reference or crs tag. Assumes the geometry is in wgs84. + * @param geometry The Geometry to write as GeoJson. + * @return Returns a string in GeoJson format. + */ + public abstract String execute(Geometry geometry); + + /** + * Performs the ExportToGeoJson operation on a spatial reference. + * + * @param export_flags The flags used for the export. + * @param spatial_reference The spatial reference being exported. Cannot be null. + * @return Returns the crs value object. + */ + public abstract String exportSpatialReference(int export_flags, SpatialReference spatial_reference); + + public static OperatorExportToGeoJson local() { + return (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Type.ExportToGeoJson); + } } diff --git a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonCursor.java b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonCursor.java index ddad1662..0678cef4 100644 --- a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonCursor.java +++ b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonCursor.java @@ -1,9 +1,32 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ /* 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 + 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, @@ -23,312 +46,757 @@ package com.esri.core.geometry; import com.esri.core.geometry.VertexDescription.Semantics; -import java.io.IOException; -import java.io.StringWriter; -import org.codehaus.jackson.JsonFactory; -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.JsonGenerator; class OperatorExportToGeoJsonCursor extends JsonCursor { - GeometryCursor m_inputGeometryCursor; - int m_index; - int m_wkid = -1; - int m_latest_wkid = -1; - String m_wkt = null; - boolean m_preferMulti = false; - - private static JsonFactory factory = new JsonFactory(); - - public OperatorExportToGeoJsonCursor(boolean preferMulti, SpatialReference spatialReference, GeometryCursor geometryCursor) { - m_index = -1; - if (geometryCursor == null) - throw new IllegalArgumentException(); - if (spatialReference != null && !spatialReference.isLocal()) { - m_wkid = spatialReference.getOldID(); - m_wkt = spatialReference.getText(); - m_latest_wkid = spatialReference.getLatestID(); - } - m_inputGeometryCursor = geometryCursor; - m_preferMulti = preferMulti; - } - - public OperatorExportToGeoJsonCursor(GeometryCursor geometryCursor) { - m_index = -1; - - if (geometryCursor == null) - throw new IllegalArgumentException(); - - m_inputGeometryCursor = geometryCursor; - } - - @Override - public int getID() { - return m_index; - } - - @Override - public String next() { - Geometry geometry; - if ((geometry = m_inputGeometryCursor.next()) != null) { - m_index = m_inputGeometryCursor.getGeometryID(); - return exportToGeoJson(geometry); - } - return null; - } - - private String exportToGeoJson(Geometry geometry) { - StringWriter sw = new StringWriter(); - - try { - JsonGenerator g = factory.createJsonGenerator(sw); - - int type = geometry.getType().value(); - - switch (type) { - case Geometry.GeometryType.Point: - exportPointToGeoJson(g, (Point) geometry); - break; - case Geometry.GeometryType.MultiPoint: - exportMultiPointToGeoJson(g, (MultiPoint) geometry); - break; - case Geometry.GeometryType.Polyline: - exportMultiPathToGeoJson(g, (Polyline) geometry); - break; - case Geometry.GeometryType.Polygon: - exportMultiPathToGeoJson(g, (Polygon) geometry); - break; - case Geometry.GeometryType.Envelope: - exportEnvelopeToGeoJson(g, (Envelope) geometry); - break; - default: - throw new RuntimeException("not implemented for this geometry type"); - } - - return sw.getBuffer().toString(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - private void exportPointToGeoJson(JsonGenerator g, Point p) throws JsonGenerationException, IOException { - if (m_preferMulti) { - MultiPoint mp = new MultiPoint(); - mp.add(p); - exportMultiPointToGeoJson(g, mp); - return; - } - - g.writeStartObject(); - - g.writeFieldName("type"); - g.writeString("Point"); - - g.writeFieldName("coordinates"); - - if (p.isEmpty()) { - g.writeNull(); - } else { - g.writeStartArray(); - - writeDouble(p.getX(), g); - writeDouble(p.getY(), g); - - if (p.hasAttribute(Semantics.Z)) - writeDouble(p.getZ(), g); - - g.writeEndArray(); - } - - g.writeEndObject(); - g.close(); - } - - private void exportMultiPointToGeoJson(JsonGenerator g, MultiPoint mp) throws JsonGenerationException, IOException { - g.writeStartObject(); - - g.writeFieldName("type"); - g.writeString("MultiPoint"); - - g.writeFieldName("coordinates"); - - if (mp.isEmpty()) { - g.writeNull(); - } else { - g.writeStartArray(); - - MultiPointImpl mpImpl = (MultiPointImpl) mp._getImpl(); - AttributeStreamOfDbl zs = mp.hasAttribute(Semantics.Z) ? (AttributeStreamOfDbl) mpImpl.getAttributeStreamRef(Semantics.Z) : null; - - Point2D p = new Point2D(); - - int n = mp.getPointCount(); - - for(int i = 0; i < n; i++) { - mp.getXY(i, p); - - g.writeStartArray(); - - writeDouble(p.x, g); - writeDouble(p.y, g); - - if (zs != null) - writeDouble(zs.get(i), g); - - g.writeEndArray(); - } - - g.writeEndArray(); - } - - g.writeEndObject(); - g.close(); - } - - private void exportMultiPathToGeoJson(JsonGenerator g, MultiPath p) throws JsonGenerationException, IOException { - MultiPathImpl pImpl = (MultiPathImpl) p._getImpl(); - - boolean isPolygon = pImpl.m_bPolygon; - int polyCount = isPolygon ? pImpl.getOGCPolygonCount() : 0; - - // check yo' polys playa - - g.writeStartObject(); - - g.writeFieldName("type"); - - boolean bCollection = false; - if (isPolygon) { - if (polyCount >= 2 || m_preferMulti) { // single polys seem to have a polyCount of 0, multi polys seem to be >= 2 - g.writeString("MultiPolygon"); - bCollection = true; - } else { - g.writeString("Polygon"); - } - } - else { - if (p.getPathCount() > 1 || m_preferMulti) { // single polys seem to have a polyCount of 0, multi polys seem to be >= 2 - g.writeString("MultiLineString"); - bCollection = true; - } else { - g.writeString("LineString"); - } - } - - g.writeFieldName("coordinates"); - - if (p.isEmpty()) { - g.writeNull(); - } else { - exportMultiPathToGeoJson(g, pImpl, bCollection); - } + GeometryCursor m_inputGeometryCursor; + SpatialReference m_spatialReference; + int m_index; + int m_export_flags; + + public OperatorExportToGeoJsonCursor(int export_flags, SpatialReference spatialReference, + GeometryCursor geometryCursor) { + m_index = -1; + if (geometryCursor == null) + throw new IllegalArgumentException(); + + m_export_flags = export_flags; + m_spatialReference = spatialReference; + m_inputGeometryCursor = geometryCursor; + } + + @Override + public int getID() { + return m_index; + } + + @Override + public String next() { + Geometry geometry; + if ((geometry = m_inputGeometryCursor.next()) != null) { + m_index = m_inputGeometryCursor.getGeometryID(); + return exportToGeoJson(m_export_flags, geometry, m_spatialReference); + } + return null; + } + + // Mirrors wkt + static String exportToGeoJson(int export_flags, Geometry geometry, SpatialReference spatial_reference) { + + if (geometry == null) + throw new IllegalArgumentException(""); + + JsonWriter json_writer = new JsonStringWriter(); + + json_writer.startObject(); + + exportGeometryToGeoJson_(export_flags, geometry, json_writer); + + if ((export_flags & GeoJsonExportFlags.geoJsonExportSkipCRS) == 0) { + json_writer.addFieldName("crs"); + exportSpatialReference(export_flags, spatial_reference, json_writer); + } + + json_writer.endObject(); + + return (String) json_writer.getJson(); + } + + static String exportSpatialReference(int export_flags, SpatialReference spatial_reference) { + if (spatial_reference == null || (export_flags & GeoJsonExportFlags.geoJsonExportSkipCRS) != 0) + throw new IllegalArgumentException(""); + + JsonWriter json_writer = new JsonStringWriter(); + exportSpatialReference(export_flags, spatial_reference, json_writer); + + return (String) json_writer.getJson(); + } + + private static void exportGeometryToGeoJson_(int export_flags, Geometry geometry, JsonWriter json_writer) { + int type = geometry.getType().value(); + switch (type) { + case Geometry.GeometryType.Polygon: + exportPolygonToGeoJson_(export_flags, (Polygon) geometry, json_writer); + return; + + case Geometry.GeometryType.Polyline: + exportPolylineToGeoJson_(export_flags, (Polyline) geometry, json_writer); + return; + + case Geometry.GeometryType.MultiPoint: + exportMultiPointToGeoJson_(export_flags, (MultiPoint) geometry, json_writer); + return; + + case Geometry.GeometryType.Point: + exportPointToGeoJson_(export_flags, (Point) geometry, json_writer); + return; + + case Geometry.GeometryType.Envelope: + exportEnvelopeToGeoJson_(export_flags, (Envelope) geometry, + json_writer); + return; + + default: + throw new RuntimeException("not implemented for this geometry type"); + } + } + + private static void exportSpatialReference(int export_flags, SpatialReference spatial_reference, + JsonWriter json_writer) { + if (spatial_reference != null) { + int wkid = spatial_reference.getLatestID(); + + if (wkid <= 0) + throw new GeometryException("invalid call"); + + json_writer.startObject(); + + json_writer.addFieldName("type"); + + json_writer.addValueString("name"); + + json_writer.addFieldName("properties"); + json_writer.startObject(); + + json_writer.addFieldName("name"); + + String authority = ((SpatialReferenceImpl) spatial_reference).getAuthority(); + authority = authority.toUpperCase(); + StringBuilder crs_identifier = new StringBuilder(authority); + crs_identifier.append(':'); + crs_identifier.append(wkid); + json_writer.addValueString(crs_identifier.toString()); + + json_writer.endObject(); + + json_writer.endObject(); + } else { + json_writer.addValueNull(); + } + } + + // Mirrors wkt + private static void exportPolygonToGeoJson_(int export_flags, Polygon polygon, JsonWriter json_writer) { + MultiPathImpl polygon_impl = (MultiPathImpl) (polygon._getImpl()); + + if ((export_flags & GeoJsonExportFlags.geoJsonExportFailIfNotSimple) != 0) { + int simple = polygon_impl.getIsSimple(0.0); + + if (simple != MultiPathImpl.GeometryXSimple.Strong) + throw new GeometryException("corrupted geometry"); + } + + int point_count = polygon.getPointCount(); + int polygon_count = polygon_impl.getOGCPolygonCount(); + + if (point_count > 0 && polygon_count == 0) + throw new GeometryException("corrupted geometry"); + + int precision = 17 - (31 & (export_flags >> 13)); + boolean bFixedPoint = (GeoJsonExportFlags.geoJsonExportPrecisionFixedPoint & export_flags) != 0; + boolean b_export_zs = polygon_impl.hasAttribute(VertexDescription.Semantics.Z) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripZs) == 0; + boolean b_export_ms = polygon_impl.hasAttribute(VertexDescription.Semantics.M) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripMs) == 0; + + if (!b_export_zs && b_export_ms) + throw new IllegalArgumentException("invalid argument"); + + int path_count = 0; + AttributeStreamOfDbl position = null; + AttributeStreamOfDbl zs = null; + AttributeStreamOfDbl ms = null; + AttributeStreamOfInt8 path_flags = null; + AttributeStreamOfInt32 paths = null; + + if (point_count > 0) { + position = (AttributeStreamOfDbl) polygon_impl.getAttributeStreamRef(Semantics.POSITION); + path_flags = polygon_impl.getPathFlagsStreamRef(); + paths = polygon_impl.getPathStreamRef(); + path_count = polygon_impl.getPathCount(); + + if (b_export_zs) { + if (polygon_impl._attributeStreamIsAllocated(Semantics.Z)) + zs = (AttributeStreamOfDbl) polygon_impl.getAttributeStreamRef(Semantics.Z); + } + + if (b_export_ms) { + if (polygon_impl._attributeStreamIsAllocated(Semantics.M)) + ms = (AttributeStreamOfDbl) polygon_impl.getAttributeStreamRef(Semantics.M); + } + } + + if ((export_flags & GeoJsonExportFlags.geoJsonExportPreferMultiGeometry) == 0 && polygon_count <= 1) + polygonTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, paths, path_count, + json_writer); + else + multiPolygonTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, path_flags, + paths, polygon_count, path_count, json_writer); + } + + // Mirrors wkt + private static void exportPolylineToGeoJson_(int export_flags, Polyline polyline, JsonWriter json_writer) { + MultiPathImpl polyline_impl = (MultiPathImpl) polyline._getImpl(); + + int point_count = polyline_impl.getPointCount(); + int path_count = polyline_impl.getPathCount(); + + if (point_count > 0 && path_count == 0) + throw new GeometryException("corrupted geometry"); + + int precision = 17 - (31 & (export_flags >> 13)); + boolean bFixedPoint = (GeoJsonExportFlags.geoJsonExportPrecisionFixedPoint & export_flags) != 0; + boolean b_export_zs = polyline_impl.hasAttribute(VertexDescription.Semantics.Z) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripZs) == 0; + boolean b_export_ms = polyline_impl.hasAttribute(VertexDescription.Semantics.M) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripMs) == 0; + + if (!b_export_zs && b_export_ms) + throw new IllegalArgumentException("invalid argument"); + + AttributeStreamOfDbl position = null; + AttributeStreamOfDbl zs = null; + AttributeStreamOfDbl ms = null; + AttributeStreamOfInt8 path_flags = null; + AttributeStreamOfInt32 paths = null; + + if (point_count > 0) { + position = (AttributeStreamOfDbl) polyline_impl.getAttributeStreamRef(Semantics.POSITION); + path_flags = polyline_impl.getPathFlagsStreamRef(); + paths = polyline_impl.getPathStreamRef(); + + if (b_export_zs) { + if (polyline_impl._attributeStreamIsAllocated(Semantics.Z)) + zs = (AttributeStreamOfDbl) polyline_impl.getAttributeStreamRef(Semantics.Z); + } + + if (b_export_ms) { + if (polyline_impl._attributeStreamIsAllocated(Semantics.M)) + ms = (AttributeStreamOfDbl) polyline_impl.getAttributeStreamRef(Semantics.M); + } + } + + if ((export_flags & GeoJsonExportFlags.geoJsonExportPreferMultiGeometry) == 0 && path_count <= 1) + lineStringTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, path_flags, paths, + json_writer); + else + multiLineStringTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, path_flags, + paths, path_count, json_writer); + } + + // Mirrors wkt + private static void exportMultiPointToGeoJson_(int export_flags, MultiPoint multipoint, JsonWriter json_writer) { + MultiPointImpl multipoint_impl = (MultiPointImpl) multipoint._getImpl(); + + int point_count = multipoint_impl.getPointCount(); + + int precision = 17 - (31 & (export_flags >> 13)); + boolean bFixedPoint = (GeoJsonExportFlags.geoJsonExportPrecisionFixedPoint & export_flags) != 0; + boolean b_export_zs = multipoint_impl.hasAttribute(VertexDescription.Semantics.Z) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripZs) == 0; + boolean b_export_ms = multipoint_impl.hasAttribute(VertexDescription.Semantics.M) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripMs) == 0; + + if (!b_export_zs && b_export_ms) + throw new IllegalArgumentException("invalid argument"); + + AttributeStreamOfDbl position = null; + AttributeStreamOfDbl zs = null; + AttributeStreamOfDbl ms = null; + + if (point_count > 0) { + position = (AttributeStreamOfDbl) multipoint_impl.getAttributeStreamRef(Semantics.POSITION); + + if (b_export_zs) { + if (multipoint_impl._attributeStreamIsAllocated(Semantics.Z)) + zs = (AttributeStreamOfDbl) multipoint_impl.getAttributeStreamRef(Semantics.Z); + } + + if (b_export_ms) { + if (multipoint_impl._attributeStreamIsAllocated(Semantics.M)) + ms = (AttributeStreamOfDbl) multipoint_impl.getAttributeStreamRef(Semantics.M); + } + } + + multiPointTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, point_count, + json_writer); + } + + // Mirrors wkt + private static void exportPointToGeoJson_(int export_flags, Point point, JsonWriter json_writer) { + int precision = 17 - (31 & (export_flags >> 13)); + boolean bFixedPoint = (GeoJsonExportFlags.geoJsonExportPrecisionFixedPoint & export_flags) != 0; + boolean b_export_zs = point.hasAttribute(VertexDescription.Semantics.Z) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripZs) == 0; + boolean b_export_ms = point.hasAttribute(VertexDescription.Semantics.M) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripMs) == 0; + + if (!b_export_zs && b_export_ms) + throw new IllegalArgumentException("invalid argument"); + + double x = NumberUtils.NaN(); + double y = NumberUtils.NaN(); + double z = NumberUtils.NaN(); + double m = NumberUtils.NaN(); + + if (!point.isEmpty()) { + x = point.getX(); + y = point.getY(); + + if (b_export_zs) + z = point.getZ(); + + if (b_export_ms) + m = point.getM(); + } + + if ((export_flags & GeoJsonExportFlags.geoJsonExportPreferMultiGeometry) == 0) + pointTaggedText_(precision, bFixedPoint, b_export_zs, b_export_ms, x, y, z, m, json_writer); + else + multiPointTaggedTextFromPoint_(precision, bFixedPoint, b_export_zs, b_export_ms, x, y, z, m, json_writer); + } + + // Mirrors wkt + private static void exportEnvelopeToGeoJson_(int export_flags, Envelope envelope, JsonWriter json_writer) { + int precision = 17 - (31 & (export_flags >> 13)); + boolean bFixedPoint = (GeoJsonExportFlags.geoJsonExportPrecisionFixedPoint & export_flags) != 0; + boolean b_export_zs = envelope.hasAttribute(VertexDescription.Semantics.Z) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripZs) == 0; + boolean b_export_ms = envelope.hasAttribute(VertexDescription.Semantics.M) + && (export_flags & GeoJsonExportFlags.geoJsonExportStripMs) == 0; + + if (!b_export_zs && b_export_ms) + throw new IllegalArgumentException("invalid argument"); + + double xmin = NumberUtils.NaN(); + double ymin = NumberUtils.NaN(); + double xmax = NumberUtils.NaN(); + double ymax = NumberUtils.NaN(); + double zmin = NumberUtils.NaN(); + double zmax = NumberUtils.NaN(); + double mmin = NumberUtils.NaN(); + double mmax = NumberUtils.NaN(); + + if (!envelope.isEmpty()) { + xmin = envelope.getXMin(); + ymin = envelope.getYMin(); + xmax = envelope.getXMax(); + ymax = envelope.getYMax(); + + Envelope1D interval; + + if (b_export_zs) { + interval = envelope.queryInterval(Semantics.Z, 0); + zmin = interval.vmin; + zmax = interval.vmax; + } + + if (b_export_ms) { + interval = envelope.queryInterval(Semantics.M, 0); + mmin = interval.vmin; + mmax = interval.vmax; + } + } + + if ((export_flags & GeoJsonExportFlags.geoJsonExportPreferMultiGeometry) == 0) + polygonTaggedTextFromEnvelope_(precision, bFixedPoint, b_export_zs, b_export_ms, xmin, ymin, xmax, ymax, + zmin, zmax, mmin, mmax, json_writer); + else + multiPolygonTaggedTextFromEnvelope_(precision, bFixedPoint, b_export_zs, b_export_ms, xmin, ymin, xmax, + ymax, zmin, zmax, mmin, mmax, json_writer); + } + + // Mirrors wkt + private static void multiPolygonTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt8 path_flags, AttributeStreamOfInt32 paths, int polygon_count, int path_count, + JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("MultiPolygon"); + + json_writer.addFieldName("coordinates"); + + if (position == null) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + json_writer.startArray(); + + multiPolygonText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, path_flags, paths, + polygon_count, path_count, json_writer); + + json_writer.endArray(); + } + + // Mirrors wkt + private static void multiPolygonTaggedTextFromEnvelope_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, double xmin, double ymin, double xmax, double ymax, double zmin, double zmax, + double mmin, double mmax, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("MultiPolygon"); + + json_writer.addFieldName("coordinates"); + + if (NumberUtils.isNaN(xmin)) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + json_writer.startArray(); + + writeEnvelopeAsGeoJsonPolygon_(precision, bFixedPoint, b_export_zs, b_export_ms, xmin, ymin, xmax, ymax, zmin, + zmax, mmin, mmax, json_writer); + + json_writer.endArray(); + } + + // Mirrors wkt + private static void multiLineStringTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt8 path_flags, AttributeStreamOfInt32 paths, int path_count, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("MultiLineString"); + + json_writer.addFieldName("coordinates"); + + if (position == null) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + json_writer.startArray(); + + multiLineStringText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, path_flags, paths, + path_count, json_writer); + + json_writer.endArray(); + } + + // Mirrors wkt + private static void multiPointTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + int point_count, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("MultiPoint"); + + json_writer.addFieldName("coordinates"); + + if (position == null) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + lineStringText_(false, false, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, 0, + point_count, json_writer); + } + + // Mirrors wkt + private static void multiPointTaggedTextFromPoint_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, double x, double y, double z, double m, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("MultiPoint"); + + json_writer.addFieldName("coordinates"); + + if (NumberUtils.isNaN(x)) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + json_writer.startArray(); + + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, x, y, z, m, json_writer); + + json_writer.endArray(); + } + + // Mirrors wkt + private static void polygonTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt32 paths, int path_count, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("Polygon"); + + json_writer.addFieldName("coordinates"); + + if (position == null) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + polygonText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, paths, 0, path_count, + json_writer); + } + + // Mirrors wkt + private static void polygonTaggedTextFromEnvelope_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, double xmin, double ymin, double xmax, double ymax, double zmin, double zmax, + double mmin, double mmax, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("Polygon"); + + json_writer.addFieldName("coordinates"); + + if (NumberUtils.isNaN(xmin)) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + writeEnvelopeAsGeoJsonPolygon_(precision, bFixedPoint, b_export_zs, b_export_ms, xmin, ymin, xmax, ymax, zmin, + zmax, mmin, mmax, json_writer); + } + + // Mirrors wkt + private static void lineStringTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt8 path_flags, AttributeStreamOfInt32 paths, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("LineString"); + + json_writer.addFieldName("coordinates"); + + if (position == null) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + boolean b_closed = ((path_flags.read(0) & PathFlags.enumClosed) != 0); + + lineStringText_(false, b_closed, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, 0, + paths.read(1), json_writer); + } + + // Mirrors wkt + private static void pointTaggedText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + double x, double y, double z, double m, JsonWriter json_writer) { + json_writer.addFieldName("type"); + json_writer.addValueString("Point"); + + json_writer.addFieldName("coordinates"); + + if (NumberUtils.isNaN(x)) { + json_writer.startArray(); + json_writer.endArray(); + + return; + } + + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, x, y, z, m, json_writer); + } + + // Mirrors wkt + private static void multiPolygonText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt8 path_flags, AttributeStreamOfInt32 paths, int polygon_count, int path_count, + JsonWriter json_writer) { + int polygon_start = 0; + int polygon_end = 1; + + while (polygon_end < path_count && ((int) path_flags.read(polygon_end) & PathFlags.enumOGCStartPolygon) == 0) + polygon_end++; + + polygonText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, paths, polygon_start, + polygon_end, json_writer); + + for (int ipolygon = 1; ipolygon < polygon_count; ipolygon++) { + polygon_start = polygon_end; + polygon_end++; + + while (polygon_end < path_count + && ((int) path_flags.read(polygon_end) & PathFlags.enumOGCStartPolygon) == 0) + polygon_end++; + + polygonText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, paths, polygon_start, + polygon_end, json_writer); + } + } + + // Mirrors wkt + private static void multiLineStringText_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt8 path_flags, AttributeStreamOfInt32 paths, int path_count, JsonWriter json_writer) { + boolean b_closed = ((path_flags.read(0) & PathFlags.enumClosed) != 0); + + lineStringText_(false, b_closed, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, 0, + paths.read(1), json_writer); + + for (int path = 1; path < path_count; path++) { + b_closed = ((path_flags.read(path) & PathFlags.enumClosed) != 0); + + int istart = paths.read(path); + int iend = paths.read(path + 1); + lineStringText_(false, b_closed, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, + iend, json_writer); + } + } + + // Mirrors wkt + private static void polygonText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt32 paths, int polygon_start, int polygon_end, JsonWriter json_writer) { + json_writer.startArray(); + + int istart = paths.read(polygon_start); + int iend = paths.read(polygon_start + 1); + lineStringText_(true, true, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, iend, + json_writer); + + for (int path = polygon_start + 1; path < polygon_end; path++) { + istart = paths.read(path); + iend = paths.read(path + 1); + lineStringText_(true, true, precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, + iend, json_writer); + } + + json_writer.endArray(); + } + + // Mirrors wkt + private static void lineStringText_(boolean bRing, boolean b_closed, int precision, boolean bFixedPoint, + boolean b_export_zs, boolean b_export_ms, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, + AttributeStreamOfDbl position, int istart, int iend, JsonWriter json_writer) { + if (istart == iend) { + json_writer.startArray(); + json_writer.endArray(); + return; + } + + json_writer.startArray(); + + if (bRing) { + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, json_writer); + + for (int point = iend - 1; point >= istart + 1; point--) + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, point, json_writer); + + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, json_writer); + } else { + for (int point = istart; point < iend - 1; point++) + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, point, json_writer); + + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, iend - 1, json_writer); + + if (b_closed) + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, zs, ms, position, istart, json_writer); + } + + json_writer.endArray(); + } + + // Mirrors wkt + private static int pointText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + double x, double y, double z, double m, JsonWriter json_writer) { + + json_writer.startArray(); + + json_writer.addValueDouble(x, precision, bFixedPoint); + json_writer.addValueDouble(y, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(z, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(m, precision, bFixedPoint); + + json_writer.endArray(); + + return 1; + } + + // Mirrors wkt + private static void pointText_(int precision, boolean bFixedPoint, boolean b_export_zs, boolean b_export_ms, + AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, int point, + JsonWriter json_writer) { + double x = position.readAsDbl(2 * point); + double y = position.readAsDbl(2 * point + 1); + double z = NumberUtils.NaN(); + double m = NumberUtils.NaN(); + + if (b_export_zs) + z = (zs != null ? zs.readAsDbl(point) : VertexDescription.getDefaultValue(Semantics.Z)); + + if (b_export_ms) + m = (ms != null ? ms.readAsDbl(point) : VertexDescription.getDefaultValue(Semantics.M)); + + pointText_(precision, bFixedPoint, b_export_zs, b_export_ms, x, y, z, m, json_writer); + } + + // Mirrors wkt + private static void writeEnvelopeAsGeoJsonPolygon_(int precision, boolean bFixedPoint, boolean b_export_zs, + boolean b_export_ms, double xmin, double ymin, double xmax, double ymax, double zmin, double zmax, + double mmin, double mmax, JsonWriter json_writer) { + json_writer.startArray(); + json_writer.startArray(); + + json_writer.startArray(); + json_writer.addValueDouble(xmin, precision, bFixedPoint); + json_writer.addValueDouble(ymin, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(zmin, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(mmin, precision, bFixedPoint); + + json_writer.endArray(); + + json_writer.startArray(); + json_writer.addValueDouble(xmax, precision, bFixedPoint); + json_writer.addValueDouble(ymin, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(zmax, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(mmax, precision, bFixedPoint); + + json_writer.endArray(); + + json_writer.startArray(); + json_writer.addValueDouble(xmax, precision, bFixedPoint); + json_writer.addValueDouble(ymax, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(zmin, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(mmin, precision, bFixedPoint); + + json_writer.endArray(); + + json_writer.startArray(); + json_writer.addValueDouble(xmin, precision, bFixedPoint); + json_writer.addValueDouble(ymax, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(zmax, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(mmax, precision, bFixedPoint); + + json_writer.endArray(); + + json_writer.startArray(); + json_writer.addValueDouble(xmin, precision, bFixedPoint); + json_writer.addValueDouble(ymin, precision, bFixedPoint); + + if (b_export_zs) + json_writer.addValueDouble(zmin, precision, bFixedPoint); + + if (b_export_ms) + json_writer.addValueDouble(mmin, precision, bFixedPoint); + + json_writer.endArray(); - g.writeEndObject(); - g.close(); - } - - private void exportMultiPathToGeoJson(JsonGenerator g, MultiPathImpl pImpl, boolean bCollection) throws IOException { - int startIndex; - int vertices; - - if (bCollection) - g.writeStartArray(); - - //AttributeStreamOfDbl position = (AttributeStreamOfDbl) pImpl.getAttributeStreamRef(VertexDescription.Semantics.POSITION); - //AttributeStreamOfInt8 pathFlags = pImpl.getPathFlagsStreamRef(); - //AttributeStreamOfInt32 paths = pImpl.getPathStreamRef(); - int pathCount = pImpl.getPathCount(); - boolean isPolygon = pImpl.m_bPolygon; - AttributeStreamOfDbl zs = pImpl.hasAttribute(Semantics.Z) ? (AttributeStreamOfDbl) pImpl.getAttributeStreamRef(Semantics.Z) : null; - - for (int path = 0; path < pathCount; path++) { - startIndex = pImpl.getPathStart(path); - vertices = pImpl.getPathSize(path); - - boolean isExtRing = isPolygon && pImpl.isExteriorRing(path); - if (isExtRing) {//only for polygons - if (path > 0) - g.writeEndArray();//end of OGC polygon - - g.writeStartArray();//start of next OGC polygon - } - - writePath(pImpl, g, path, startIndex, vertices, zs); - } - - if (isPolygon) - g.writeEndArray();//end of last OGC polygon - - if (bCollection) - g.writeEndArray(); - } - - private void closePath(MultiPathImpl mp, JsonGenerator g, int startIndex, AttributeStreamOfDbl zs) throws IOException { - Point2D pt = new Point2D(); - - // close ring - mp.getXY(startIndex, pt); - g.writeStartArray(); - writeDouble(pt.x, g); - writeDouble(pt.y, g); - - if (zs != null) - writeDouble(zs.get(startIndex), g); - - g.writeEndArray(); - } - - private void writePath(MultiPathImpl mp, JsonGenerator g, int pathIndex, int startIndex, int vertices, AttributeStreamOfDbl zs) throws IOException { - Point2D pt = new Point2D(); - - g.writeStartArray(); - - for (int i = startIndex; i < startIndex + vertices; i++) { - mp.getXY(i, pt); - g.writeStartArray(); - writeDouble(pt.x, g); - writeDouble(pt.y, g); - - if (zs != null) - writeDouble(zs.get(i), g); - - g.writeEndArray(); - } - - if (mp.isClosedPath(pathIndex)) - closePath(mp, g, startIndex, zs); - - g.writeEndArray(); - } - - private void exportEnvelopeToGeoJson(JsonGenerator g, Envelope e) throws JsonGenerationException, IOException { - boolean empty = e.isEmpty(); - - g.writeStartObject(); - g.writeFieldName("bbox"); - - if (empty) { - g.writeNull(); - } else { - g.writeStartArray(); - writeDouble(e.getXMin(), g); - writeDouble(e.getYMin(), g); - writeDouble(e.getXMax(), g); - writeDouble(e.getYMax(), g); - g.writeEndArray(); - } - - g.writeEndObject(); - g.close(); - } - - private void writeDouble(double d, JsonGenerator g) throws IOException, JsonGenerationException { - if (NumberUtils.isNaN(d)) { - g.writeNull(); - } else { - g.writeNumber(d); - } - - return; - } + json_writer.endArray(); + json_writer.endArray(); + } } diff --git a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonLocal.java b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonLocal.java index 0abbadb5..9da02767 100644 --- a/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonLocal.java +++ b/src/main/java/com/esri/core/geometry/OperatorExportToGeoJsonLocal.java @@ -3,7 +3,7 @@ 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 + 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, @@ -23,29 +23,28 @@ package com.esri.core.geometry; class OperatorExportToGeoJsonLocal extends OperatorExportToGeoJson { - @Override - public JsonCursor execute(SpatialReference spatialReference, GeometryCursor geometryCursor) { - return new OperatorExportToGeoJsonCursor(false, spatialReference, geometryCursor); - } - - @Override - public String execute(SpatialReference spatialReference, Geometry geometry) { - SimpleGeometryCursor gc = new SimpleGeometryCursor(geometry); - JsonCursor cursor = new OperatorExportToGeoJsonCursor(false, spatialReference, gc); - return cursor.next(); - } - - @Override - public String execute(int exportFlags, SpatialReference spatialReference, Geometry geometry) { - SimpleGeometryCursor gc = new SimpleGeometryCursor(geometry); - JsonCursor cursor = new OperatorExportToGeoJsonCursor(exportFlags == GeoJsonExportFlags.geoJsonExportPreferMultiGeometry, spatialReference, gc); - return cursor.next(); - } - - @Override - public String execute(Geometry geometry) { - SimpleGeometryCursor gc = new SimpleGeometryCursor(geometry); - JsonCursor cursor = new OperatorExportToGeoJsonCursor(gc); - return cursor.next(); - } + @Override + public JsonCursor execute(SpatialReference spatialReference, GeometryCursor geometryCursor) { + return new OperatorExportToGeoJsonCursor(GeoJsonExportFlags.geoJsonExportDefaults, spatialReference, geometryCursor); + } + + @Override + public String execute(SpatialReference spatialReference, Geometry geometry) { + return OperatorExportToGeoJsonCursor.exportToGeoJson(GeoJsonExportFlags.geoJsonExportDefaults, geometry, spatialReference); + } + + @Override + public String execute(int exportFlags, SpatialReference spatialReference, Geometry geometry) { + return OperatorExportToGeoJsonCursor.exportToGeoJson(exportFlags, geometry, spatialReference); + } + + @Override + public String execute(Geometry geometry) { + return OperatorExportToGeoJsonCursor.exportToGeoJson(GeoJsonExportFlags.geoJsonExportSkipCRS, geometry, null); + } + + @Override + public String exportSpatialReference(int export_flags, SpatialReference spatial_reference) { + return OperatorExportToGeoJsonCursor.exportSpatialReference(export_flags, spatial_reference); + } } diff --git a/src/main/java/com/esri/core/geometry/OperatorExportToJsonCursor.java b/src/main/java/com/esri/core/geometry/OperatorExportToJsonCursor.java index b14fbb3e..9d24be43 100644 --- a/src/main/java/com/esri/core/geometry/OperatorExportToJsonCursor.java +++ b/src/main/java/com/esri/core/geometry/OperatorExportToJsonCursor.java @@ -24,442 +24,436 @@ package com.esri.core.geometry; import com.esri.core.geometry.VertexDescription.Semantics; + import java.io.IOException; import java.util.Map; class OperatorExportToJsonCursor extends JsonCursor { - GeometryCursor m_inputGeometryCursor; - SpatialReference m_spatialReference; - int m_index; - - public OperatorExportToJsonCursor(SpatialReference spatialReference, - GeometryCursor geometryCursor) { - m_index = -1; - if (geometryCursor == null) { - throw new IllegalArgumentException(); - } - - m_inputGeometryCursor = geometryCursor; - m_spatialReference = spatialReference; - } - - @Override - public int getID() { - return m_index; - } - - @Override - public String next() { - Geometry geometry; - if ((geometry = m_inputGeometryCursor.next()) != null) { - m_index = m_inputGeometryCursor.getGeometryID(); - return exportToString(geometry, m_spatialReference, null); - } - return null; - } - - static String exportToString(Geometry geometry, SpatialReference spatialReference, Map exportProperties) { - JsonWriter jsonWriter = new JsonStringWriter(); - exportToJson_(geometry, spatialReference, jsonWriter, exportProperties); - return (String) jsonWriter.getJson(); - } - - private static void exportToJson_(Geometry geometry, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - try { - int type = geometry.getType().value(); - switch (type) { - case Geometry.GeometryType.Point: - exportPointToJson((Point) geometry, spatialReference, jsonWriter, exportProperties); - break; - - case Geometry.GeometryType.MultiPoint: - exportMultiPointToJson((MultiPoint) geometry, spatialReference, jsonWriter, exportProperties); - break; - - case Geometry.GeometryType.Polyline: - exportPolylineToJson((Polyline) geometry, spatialReference, jsonWriter, exportProperties); - break; - - case Geometry.GeometryType.Polygon: - exportPolygonToJson((Polygon) geometry, spatialReference, jsonWriter, exportProperties); - break; - - case Geometry.GeometryType.Envelope: - exportEnvelopeToJson((Envelope) geometry, spatialReference, jsonWriter, exportProperties); - break; - - default: - throw new RuntimeException( - "not implemented for this geometry type"); - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - private static void exportPolygonToJson(Polygon pp, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - exportPolypathToJson(pp, "rings", spatialReference, jsonWriter, exportProperties); - } - - private static void exportPolylineToJson(Polyline pp, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - exportPolypathToJson(pp, "paths", spatialReference, jsonWriter, exportProperties); - } - - private static void exportPolypathToJson(MultiPath pp, String name, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - boolean bExportZs = pp.hasAttribute(Semantics.Z); - boolean bExportMs = pp.hasAttribute(Semantics.M); - - boolean bPositionAsF = false; - int decimals = 17; - - if (exportProperties != null) { - Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); - if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { - bPositionAsF = true; - decimals = ((Number) numberOfDecimalsXY).intValue(); - } - } - - jsonWriter.startObject(); - - if (bExportZs) { - jsonWriter.addPairBoolean("hasZ", true); - } - - if (bExportMs) { - jsonWriter.addPairBoolean("hasM", true); - } - - jsonWriter.addPairArray(name); - - if (!pp.isEmpty()) { - int n = pp.getPathCount(); // rings or paths - - MultiPathImpl mpImpl = (MultiPathImpl) pp._getImpl();// get impl for - // faster - // access - AttributeStreamOfDbl zs = null; - AttributeStreamOfDbl ms = null; - - if (bExportZs) { - zs = (AttributeStreamOfDbl) mpImpl - .getAttributeStreamRef(Semantics.Z); - } - - if (bExportMs) { - ms = (AttributeStreamOfDbl) mpImpl - .getAttributeStreamRef(Semantics.M); - } - - boolean bPolygon = pp instanceof Polygon; - Point2D pt = new Point2D(); - - for (int i = 0; i < n; i++) { - jsonWriter.addValueArray(); - int startindex = pp.getPathStart(i); - int numVertices = pp.getPathSize(i); - double startx = 0.0, starty = 0.0, startz = NumberUtils.NaN(), startm = NumberUtils.NaN(); - double z = NumberUtils.NaN(), m = NumberUtils.NaN(); - boolean bClosed = pp.isClosedPath(i); - for (int j = startindex; j < startindex + numVertices; j++) { - pp.getXY(j, pt); - - jsonWriter.addValueArray(); - - if (bPositionAsF) { - jsonWriter.addValueDoubleF(pt.x, decimals); - jsonWriter.addValueDoubleF(pt.y, decimals); - } else { - jsonWriter.addValueDouble(pt.x); - jsonWriter.addValueDouble(pt.y); - } - - if (bExportZs) { - z = zs.get(j); - jsonWriter.addValueDouble(z); - } - - if (bExportMs) { - m = ms.get(j); - jsonWriter.addValueDouble(m); - } - - if (j == startindex && bClosed) { - startx = pt.x; - starty = pt.y; - startz = z; - startm = m; - } - - jsonWriter.endArray(); - } - - // Close the Path/Ring by writing the Point at the start index - if (bClosed && (startx != pt.x || starty != pt.y || (bExportZs && !(NumberUtils.isNaN(startz) && NumberUtils.isNaN(z)) && startz != z) || (bExportMs && !(NumberUtils.isNaN(startm) && NumberUtils.isNaN(m)) && startm != m))) { - pp.getXY(startindex, pt); - // getPoint(startindex); - jsonWriter.addValueArray(); - - if (bPositionAsF) { - jsonWriter.addValueDoubleF(pt.x, decimals); - jsonWriter.addValueDoubleF(pt.y, decimals); - } else { - jsonWriter.addValueDouble(pt.x); - jsonWriter.addValueDouble(pt.y); - } - - if (bExportZs) { - z = zs.get(startindex); - jsonWriter.addValueDouble(z); - } - - if (bExportMs) { - m = ms.get(startindex); - jsonWriter.addValueDouble(m); - } - - jsonWriter.endArray(); - } - - jsonWriter.endArray(); - } - } - - jsonWriter.endArray(); - - if (spatialReference != null) { - writeSR(spatialReference, jsonWriter); - } - - jsonWriter.endObject(); - } - - private static void exportMultiPointToJson(MultiPoint mpt, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - boolean bExportZs = mpt.hasAttribute(Semantics.Z); - boolean bExportMs = mpt.hasAttribute(Semantics.M); - - boolean bPositionAsF = false; - int decimals = 17; - - if (exportProperties != null) { - Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); - if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { - bPositionAsF = true; - decimals = ((Number) numberOfDecimalsXY).intValue(); - } - } - - jsonWriter.startObject(); - - if (bExportZs) { - jsonWriter.addPairBoolean("hasZ", true); - } - - if (bExportMs) { - jsonWriter.addPairBoolean("hasM", true); - } - - jsonWriter.addPairArray("points"); - - if (!mpt.isEmpty()) { - MultiPointImpl mpImpl = (MultiPointImpl) mpt._getImpl();// get impl - // for - // faster - // access - AttributeStreamOfDbl zs = null; - AttributeStreamOfDbl ms = null; - - if (bExportZs) { - zs = (AttributeStreamOfDbl) mpImpl - .getAttributeStreamRef(Semantics.Z); - } - - if (bExportMs) { - ms = (AttributeStreamOfDbl) mpImpl - .getAttributeStreamRef(Semantics.M); - } - - Point2D pt = new Point2D(); - int n = mpt.getPointCount(); - for (int i = 0; i < n; i++) { - mpt.getXY(i, pt); - - jsonWriter.addValueArray(); - - if (bPositionAsF) { - jsonWriter.addValueDoubleF(pt.x, decimals); - jsonWriter.addValueDoubleF(pt.y, decimals); - } else { - jsonWriter.addValueDouble(pt.x); - jsonWriter.addValueDouble(pt.y); - } - - if (bExportZs) { - double z = zs.get(i); - jsonWriter.addValueDouble(z); - } - - if (bExportMs) { - double m = ms.get(i); - jsonWriter.addValueDouble(m); - } - - jsonWriter.endArray(); - } - } - - jsonWriter.endArray(); - - if (spatialReference != null) { - writeSR(spatialReference, jsonWriter); - } - - jsonWriter.endObject(); - } - - private static void exportPointToJson(Point pt, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - boolean bExportZs = pt.hasAttribute(Semantics.Z); - boolean bExportMs = pt.hasAttribute(Semantics.M); - - boolean bPositionAsF = false; - int decimals = 17; - - if (exportProperties != null) { - Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); - if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { - bPositionAsF = true; - decimals = ((Number) numberOfDecimalsXY).intValue(); - } - } - - jsonWriter.startObject(); - - if (pt.isEmpty()) { - jsonWriter.addPairNull("x"); - jsonWriter.addPairNull("y"); - - if (bExportZs) { - jsonWriter.addPairNull("z"); - } - - if (bExportMs) { - jsonWriter.addPairNull("m"); - } - } else { - - if (bPositionAsF) { - jsonWriter.addPairDoubleF("x", pt.getX(), decimals); - jsonWriter.addPairDoubleF("y", pt.getY(), decimals); - } else { - jsonWriter.addPairDouble("x", pt.getX()); - jsonWriter.addPairDouble("y", pt.getY()); - } - - if (bExportZs) { - jsonWriter.addPairDouble("z", pt.getZ()); - } - - if (bExportMs) { - jsonWriter.addPairDouble("m", pt.getM()); - } - } - - if (spatialReference != null) { - writeSR(spatialReference, jsonWriter); - } - - jsonWriter.endObject(); - } - - private static void exportEnvelopeToJson(Envelope env, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { - boolean bExportZs = env.hasAttribute(Semantics.Z); - boolean bExportMs = env.hasAttribute(Semantics.M); - - boolean bPositionAsF = false; - int decimals = 17; - - if (exportProperties != null) { - Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); - if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { - bPositionAsF = true; - decimals = ((Number) numberOfDecimalsXY).intValue(); - } - } - - jsonWriter.startObject(); - - if (env.isEmpty()) { - jsonWriter.addPairNull("xmin"); - jsonWriter.addPairNull("ymin"); - jsonWriter.addPairNull("xmax"); - jsonWriter.addPairNull("ymax"); - - if (bExportZs) { - jsonWriter.addPairNull("zmin"); - jsonWriter.addPairNull("zmax"); - } - - if (bExportMs) { - jsonWriter.addPairNull("mmin"); - jsonWriter.addPairNull("mmax"); - } - } else { - - if (bPositionAsF) { - jsonWriter.addPairDoubleF("xmin", env.getXMin(), decimals); - jsonWriter.addPairDoubleF("ymin", env.getYMin(), decimals); - jsonWriter.addPairDoubleF("xmax", env.getXMax(), decimals); - jsonWriter.addPairDoubleF("ymax", env.getYMax(), decimals); - } else { - jsonWriter.addPairDouble("xmin", env.getXMin()); - jsonWriter.addPairDouble("ymin", env.getYMin()); - jsonWriter.addPairDouble("xmax", env.getXMax()); - jsonWriter.addPairDouble("ymax", env.getYMax()); - } - - if (bExportZs) { - Envelope1D z = env.queryInterval(Semantics.Z, 0); - jsonWriter.addPairDouble("zmin", z.vmin); - jsonWriter.addPairDouble("zmax", z.vmax); - } - - if (bExportMs) { - Envelope1D m = env.queryInterval(Semantics.M, 0); - jsonWriter.addPairDouble("mmin", m.vmin); - jsonWriter.addPairDouble("mmax", m.vmax); - } - } - - if (spatialReference != null) { - writeSR(spatialReference, jsonWriter); - } - - jsonWriter.endObject(); - } - - private static void writeSR(SpatialReference spatialReference, JsonWriter jsonWriter) { - int wkid = spatialReference.getOldID(); - if (wkid > 0) { - jsonWriter.addPairObject("spatialReference"); - - jsonWriter.addPairInt("wkid", wkid); - - int latest_wkid = spatialReference.getLatestID(); - if (latest_wkid > 0 && latest_wkid != wkid) { - jsonWriter.addPairInt("latestWkid", latest_wkid); - } - - jsonWriter.endObject(); - } else { - String wkt = spatialReference.getText(); - if (wkt != null) { - jsonWriter.addPairObject("spatialReference"); - jsonWriter.addPairString("wkt", wkt); - jsonWriter.endObject(); - } - } - } + GeometryCursor m_inputGeometryCursor; + SpatialReference m_spatialReference; + int m_index; + + public OperatorExportToJsonCursor(SpatialReference spatialReference, GeometryCursor geometryCursor) { + m_index = -1; + if (geometryCursor == null) { + throw new IllegalArgumentException(); + } + + m_inputGeometryCursor = geometryCursor; + m_spatialReference = spatialReference; + } + + @Override + public int getID() { + return m_index; + } + + @Override + public String next() { + Geometry geometry; + if ((geometry = m_inputGeometryCursor.next()) != null) { + m_index = m_inputGeometryCursor.getGeometryID(); + return exportToString(geometry, m_spatialReference, null); + } + return null; + } + + static String exportToString(Geometry geometry, SpatialReference spatialReference, Map exportProperties) { + JsonWriter jsonWriter = new JsonStringWriter(); + exportToJson_(geometry, spatialReference, jsonWriter, exportProperties); + return (String) jsonWriter.getJson(); + } + + private static void exportToJson_(Geometry geometry, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + try { + int type = geometry.getType().value(); + switch (type) { + case Geometry.GeometryType.Point: + exportPointToJson((Point) geometry, spatialReference, jsonWriter, exportProperties); + break; + + case Geometry.GeometryType.MultiPoint: + exportMultiPointToJson((MultiPoint) geometry, spatialReference, jsonWriter, exportProperties); + break; + + case Geometry.GeometryType.Polyline: + exportPolylineToJson((Polyline) geometry, spatialReference, jsonWriter, exportProperties); + break; + + case Geometry.GeometryType.Polygon: + exportPolygonToJson((Polygon) geometry, spatialReference, jsonWriter, exportProperties); + break; + + case Geometry.GeometryType.Envelope: + exportEnvelopeToJson((Envelope) geometry, spatialReference, jsonWriter, exportProperties); + break; + + default: + throw new RuntimeException("not implemented for this geometry type"); + } + + } catch (Exception e) { + } + + } + + private static void exportPolygonToJson(Polygon pp, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + exportPolypathToJson(pp, "rings", spatialReference, jsonWriter, exportProperties); + } + + private static void exportPolylineToJson(Polyline pp, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + exportPolypathToJson(pp, "paths", spatialReference, jsonWriter, exportProperties); + } + + private static void exportPolypathToJson(MultiPath pp, String name, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + boolean bExportZs = pp.hasAttribute(Semantics.Z); + boolean bExportMs = pp.hasAttribute(Semantics.M); + + boolean bPositionAsF = false; + int decimals = 17; + + if (exportProperties != null) { + Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); + if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { + bPositionAsF = true; + decimals = ((Number) numberOfDecimalsXY).intValue(); + } + } + + jsonWriter.startObject(); + + if (bExportZs) { + jsonWriter.addPairBoolean("hasZ", true); + } + + if (bExportMs) { + jsonWriter.addPairBoolean("hasM", true); + } + + jsonWriter.addPairArray(name); + + if (!pp.isEmpty()) { + int n = pp.getPathCount(); // rings or paths + + MultiPathImpl mpImpl = (MultiPathImpl) pp._getImpl();// get impl for + // faster + // access + AttributeStreamOfDbl zs = null; + AttributeStreamOfDbl ms = null; + + if (bExportZs) { + zs = (AttributeStreamOfDbl) mpImpl.getAttributeStreamRef(Semantics.Z); + } + + if (bExportMs) { + ms = (AttributeStreamOfDbl) mpImpl.getAttributeStreamRef(Semantics.M); + } + + boolean bPolygon = pp instanceof Polygon; + Point2D pt = new Point2D(); + + for (int i = 0; i < n; i++) { + jsonWriter.addValueArray(); + int startindex = pp.getPathStart(i); + int numVertices = pp.getPathSize(i); + double startx = 0.0, starty = 0.0, startz = NumberUtils.NaN(), startm = NumberUtils.NaN(); + double z = NumberUtils.NaN(), m = NumberUtils.NaN(); + boolean bClosed = pp.isClosedPath(i); + for (int j = startindex; j < startindex + numVertices; j++) { + pp.getXY(j, pt); + + jsonWriter.addValueArray(); + + if (bPositionAsF) { + jsonWriter.addValueDouble(pt.x, decimals, true); + jsonWriter.addValueDouble(pt.y, decimals, true); + } else { + jsonWriter.addValueDouble(pt.x); + jsonWriter.addValueDouble(pt.y); + } + + if (bExportZs) { + z = zs.get(j); + jsonWriter.addValueDouble(z); + } + + if (bExportMs) { + m = ms.get(j); + jsonWriter.addValueDouble(m); + } + + if (j == startindex && bClosed) { + startx = pt.x; + starty = pt.y; + startz = z; + startm = m; + } + + jsonWriter.endArray(); + } + + // Close the Path/Ring by writing the Point at the start index + if (bClosed && (startx != pt.x || starty != pt.y || (bExportZs && !(NumberUtils.isNaN(startz) && NumberUtils.isNaN(z)) && startz != z) || (bExportMs && !(NumberUtils.isNaN(startm) && NumberUtils.isNaN(m)) && startm != m))) { + pp.getXY(startindex, pt); + // getPoint(startindex); + jsonWriter.addValueArray(); + + if (bPositionAsF) { + jsonWriter.addValueDouble(pt.x, decimals, true); + jsonWriter.addValueDouble(pt.y, decimals, true); + } else { + jsonWriter.addValueDouble(pt.x); + jsonWriter.addValueDouble(pt.y); + } + + if (bExportZs) { + z = zs.get(startindex); + jsonWriter.addValueDouble(z); + } + + if (bExportMs) { + m = ms.get(startindex); + jsonWriter.addValueDouble(m); + } + + jsonWriter.endArray(); + } + + jsonWriter.endArray(); + } + } + + jsonWriter.endArray(); + + if (spatialReference != null) { + writeSR(spatialReference, jsonWriter); + } + + jsonWriter.endObject(); + } + + private static void exportMultiPointToJson(MultiPoint mpt, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + boolean bExportZs = mpt.hasAttribute(Semantics.Z); + boolean bExportMs = mpt.hasAttribute(Semantics.M); + + boolean bPositionAsF = false; + int decimals = 17; + + if (exportProperties != null) { + Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); + if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { + bPositionAsF = true; + decimals = ((Number) numberOfDecimalsXY).intValue(); + } + } + + jsonWriter.startObject(); + + if (bExportZs) { + jsonWriter.addPairBoolean("hasZ", true); + } + + if (bExportMs) { + jsonWriter.addPairBoolean("hasM", true); + } + + jsonWriter.addPairArray("points"); + + if (!mpt.isEmpty()) { + MultiPointImpl mpImpl = (MultiPointImpl) mpt._getImpl();// get impl + // for + // faster + // access + AttributeStreamOfDbl zs = null; + AttributeStreamOfDbl ms = null; + + if (bExportZs) { + zs = (AttributeStreamOfDbl) mpImpl.getAttributeStreamRef(Semantics.Z); + } + + if (bExportMs) { + ms = (AttributeStreamOfDbl) mpImpl.getAttributeStreamRef(Semantics.M); + } + + Point2D pt = new Point2D(); + int n = mpt.getPointCount(); + for (int i = 0; i < n; i++) { + mpt.getXY(i, pt); + + jsonWriter.addValueArray(); + + if (bPositionAsF) { + jsonWriter.addValueDouble(pt.x, decimals, true); + jsonWriter.addValueDouble(pt.y, decimals, true); + } else { + jsonWriter.addValueDouble(pt.x); + jsonWriter.addValueDouble(pt.y); + } + + if (bExportZs) { + double z = zs.get(i); + jsonWriter.addValueDouble(z); + } + + if (bExportMs) { + double m = ms.get(i); + jsonWriter.addValueDouble(m); + } + + jsonWriter.endArray(); + } + } + + jsonWriter.endArray(); + + if (spatialReference != null) { + writeSR(spatialReference, jsonWriter); + } + + jsonWriter.endObject(); + } + + private static void exportPointToJson(Point pt, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + boolean bExportZs = pt.hasAttribute(Semantics.Z); + boolean bExportMs = pt.hasAttribute(Semantics.M); + + boolean bPositionAsF = false; + int decimals = 17; + + if (exportProperties != null) { + Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); + if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { + bPositionAsF = true; + decimals = ((Number) numberOfDecimalsXY).intValue(); + } + } + + jsonWriter.startObject(); + + if (pt.isEmpty()) { + jsonWriter.addPairNull("x"); + jsonWriter.addPairNull("y"); + + if (bExportZs) { + jsonWriter.addPairNull("z"); + } + + if (bExportMs) { + jsonWriter.addPairNull("m"); + } + } else { + + if (bPositionAsF) { + jsonWriter.addPairDouble("x", pt.getX(), decimals, true); + jsonWriter.addPairDouble("y", pt.getY(), decimals, true); + } else { + jsonWriter.addPairDouble("x", pt.getX()); + jsonWriter.addPairDouble("y", pt.getY()); + } + + if (bExportZs) { + jsonWriter.addPairDouble("z", pt.getZ()); + } + + if (bExportMs) { + jsonWriter.addPairDouble("m", pt.getM()); + } + } + + if (spatialReference != null) { + writeSR(spatialReference, jsonWriter); + } + + jsonWriter.endObject(); + } + + private static void exportEnvelopeToJson(Envelope env, SpatialReference spatialReference, JsonWriter jsonWriter, Map exportProperties) { + boolean bExportZs = env.hasAttribute(Semantics.Z); + boolean bExportMs = env.hasAttribute(Semantics.M); + + boolean bPositionAsF = false; + int decimals = 17; + + if (exportProperties != null) { + Object numberOfDecimalsXY = exportProperties.get("numberOfDecimalsXY"); + if (numberOfDecimalsXY != null && numberOfDecimalsXY instanceof Number) { + bPositionAsF = true; + decimals = ((Number) numberOfDecimalsXY).intValue(); + } + } + + jsonWriter.startObject(); + + if (env.isEmpty()) { + jsonWriter.addPairNull("xmin"); + jsonWriter.addPairNull("ymin"); + jsonWriter.addPairNull("xmax"); + jsonWriter.addPairNull("ymax"); + + if (bExportZs) { + jsonWriter.addPairNull("zmin"); + jsonWriter.addPairNull("zmax"); + } + + if (bExportMs) { + jsonWriter.addPairNull("mmin"); + jsonWriter.addPairNull("mmax"); + } + } else { + + if (bPositionAsF) { + jsonWriter.addPairDouble("xmin", env.getXMin(), decimals, true); + jsonWriter.addPairDouble("ymin", env.getYMin(), decimals, true); + jsonWriter.addPairDouble("xmax", env.getXMax(), decimals, true); + jsonWriter.addPairDouble("ymax", env.getYMax(), decimals, true); + } else { + jsonWriter.addPairDouble("xmin", env.getXMin()); + jsonWriter.addPairDouble("ymin", env.getYMin()); + jsonWriter.addPairDouble("xmax", env.getXMax()); + jsonWriter.addPairDouble("ymax", env.getYMax()); + } + + if (bExportZs) { + Envelope1D z = env.queryInterval(Semantics.Z, 0); + jsonWriter.addPairDouble("zmin", z.vmin); + jsonWriter.addPairDouble("zmax", z.vmax); + } + + if (bExportMs) { + Envelope1D m = env.queryInterval(Semantics.M, 0); + jsonWriter.addPairDouble("mmin", m.vmin); + jsonWriter.addPairDouble("mmax", m.vmax); + } + } + + if (spatialReference != null) { + writeSR(spatialReference, jsonWriter); + } + + jsonWriter.endObject(); + } + + private static void writeSR(SpatialReference spatialReference, JsonWriter jsonWriter) { + int wkid = spatialReference.getOldID(); + if (wkid > 0) { + jsonWriter.addPairObject("spatialReference"); + + jsonWriter.addPairInt("wkid", wkid); + + int latest_wkid = spatialReference.getLatestID(); + if (latest_wkid > 0 && latest_wkid != wkid) { + jsonWriter.addPairInt("latestWkid", latest_wkid); + } + + jsonWriter.endObject(); + } else { + String wkt = spatialReference.getText(); + if (wkt != null) { + jsonWriter.addPairObject("spatialReference"); + jsonWriter.addPairString("wkt", wkt); + jsonWriter.endObject(); + } + } + } } diff --git a/src/main/java/com/esri/core/geometry/OperatorFactoryLocal.java b/src/main/java/com/esri/core/geometry/OperatorFactoryLocal.java index f07cea96..160bcffd 100644 --- a/src/main/java/com/esri/core/geometry/OperatorFactoryLocal.java +++ b/src/main/java/com/esri/core/geometry/OperatorFactoryLocal.java @@ -25,9 +25,11 @@ package com.esri.core.geometry; import com.esri.core.geometry.Operator.Type; + import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.Reader; @@ -35,7 +37,9 @@ import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.HashMap; + import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonParser; /** @@ -209,6 +213,20 @@ public static MapGeometry loadGeometryFromJSONFileDbg(String file_name) { return mapGeom; } + public static MapGeometry loadGeometryFromJSONStringDbg(String json) { + if (json == null) { + throw new IllegalArgumentException(); + } + + MapGeometry mapGeom = null; + try { + mapGeom = OperatorImportFromJson.local().execute(Geometry.Type.Unknown, json); + } catch (Exception e) { + throw new IllegalArgumentException(e.toString()); + } + return mapGeom; + } + public static Geometry loadGeometryFromEsriShapeDbg(String file_name) { if (file_name == null) { throw new IllegalArgumentException(); diff --git a/src/main/java/com/esri/core/geometry/OperatorGeodesicBuffer.java b/src/main/java/com/esri/core/geometry/OperatorGeodesicBuffer.java index 4e7068f7..03cbae60 100644 --- a/src/main/java/com/esri/core/geometry/OperatorGeodesicBuffer.java +++ b/src/main/java/com/esri/core/geometry/OperatorGeodesicBuffer.java @@ -33,7 +33,7 @@ public Operator.Type getType() { /** * Creates a geodesic buffer around the input geometries * - * @param input_geometries The geometries to buffer. + * @param inputGeometries The geometries to buffer. * @param sr The Spatial_reference of the Geometries. * @param curveType The geodetic curve type of the segments. If the curve_type is Geodetic_curve::shape_preserving, then the segments are densified in the projection where they are defined before * buffering. @@ -43,13 +43,15 @@ public Operator.Type getType() { * default deviation. * @param bReserved Must be false. Reserved for future development. Will throw an exception if not false. * @param bUnion If True, the buffered geometries will be unioned, otherwise they wont be unioned. + * @param progressTracker Can be null. Allows to cancel lengthy operation. + * @return Geometry cursor over result buffers. */ abstract public GeometryCursor execute(GeometryCursor inputGeometries, SpatialReference sr, int curveType, double[] distancesMeters, double maxDeviationMeters, boolean bReserved, boolean bUnion, ProgressTracker progressTracker); /** * Creates a geodesic buffer around the input geometry * - * @param input_geometry The geometry to buffer. + * @param inputGeometry The geometry to buffer. * @param sr The Spatial_reference of the Geometry. * @param curveType The geodetic curve type of the segments. If the curve_type is Geodetic_curve::shape_preserving, then the segments are densified in the projection where they are defined before * buffering. @@ -57,6 +59,8 @@ public Operator.Type getType() { * @param maxDeviationMeters The deviation offset to use for convergence. The geodesic arcs of the resulting buffer will be closer than the max deviation of the true buffer. Pass in NaN to use the * default deviation. * @param bReserved Must be false. Reserved for future development. Will throw an exception if not false. + * @param progressTracker Can be null. Allows to cancel lengthy operation. + * @return Returns result buffer. */ abstract public Geometry execute(Geometry inputGeometry, SpatialReference sr, int curveType, double distanceMeters, double maxDeviationMeters, boolean bReserved, ProgressTracker progressTracker); diff --git a/src/main/java/com/esri/core/geometry/OperatorGeodeticDensifyByLength.java b/src/main/java/com/esri/core/geometry/OperatorGeodeticDensifyByLength.java index 5efcb134..ca2d1087 100644 --- a/src/main/java/com/esri/core/geometry/OperatorGeodeticDensifyByLength.java +++ b/src/main/java/com/esri/core/geometry/OperatorGeodeticDensifyByLength.java @@ -42,7 +42,7 @@ public Type getType() { * @param maxSegmentLengthMeters The maximum segment length (in meters) allowed. Must be a positive value. * @param sr The SpatialReference of the Geometry. * @param curveType The interpretation of a line connecting two points. - * @return Returns the densified geometries (It does nothing to geometries with dim < 1, but simply passes them along). + * @return Returns the densified geometries (It does nothing to geometries with dim less than 1, but simply passes them along). * * Note the behavior is not determined for any geodetic curve segments that connect two poles, or for loxodrome segments that connect to any pole. */ diff --git a/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJson.java b/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJson.java index 2c0c0287..88cb4653 100644 --- a/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJson.java +++ b/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJson.java @@ -24,8 +24,12 @@ package com.esri.core.geometry; import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; public abstract class OperatorImportFromGeoJson extends Operator { + @Override public Type getType() { return Type.ImportFromGeoJson; @@ -33,29 +37,40 @@ public Type getType() { /** * Performs the ImportFromGeoJson operation. + * + * @param type Use the {@link Geometry.Type} enum. + * @param jsonObject The JSONObject holding the geometry and spatial reference. + * @return Returns the imported MapGeometry. + * @throws JsonGeometryException + */ + public abstract MapGeometry execute(int importFlags, Geometry.Type type, JSONObject jsonObject, ProgressTracker progressTracker) throws JSONException; + + /** + * Deprecated, use version without import_flags. + * + * Performs the ImportFromGeoJson operation. + * * @param import_flags Use the {@link GeoJsonImportFlags} interface. - * @param type Use the {@link Geometry.Type} enum. + * @param type Use the {@link Geometry.Type} enum. * @param geoJsonString The string holding the Geometry in geoJson format. - * @return Returns the imported Geometry. - * @throws JSONException + * @return Returns the imported MapGeometry. + * @throws JSONException + * */ - public abstract MapGeometry execute(int import_flags, Geometry.Type type, - String geoJsonString, ProgressTracker progress_tracker) - throws JSONException; + public abstract MapGeometry execute(int import_flags, Geometry.Type type, String geoJsonString, ProgressTracker progress_tracker) throws JSONException; /** + * * Performs the ImportFromGeoJson operation. + * * @param import_flags Use the {@link GeoJsonImportFlags} interface. * @param geoJsonString The string holding the Geometry in geoJson format. * @return Returns the imported MapOGCStructure. - * @throws JSONException + * @throws JSONException */ - public abstract MapOGCStructure executeOGC(int import_flags, - String geoJsonString, ProgressTracker progress_tracker) - throws JSONException; + public abstract MapOGCStructure executeOGC(int import_flags, String geoJsonString, ProgressTracker progress_tracker) throws JSONException; public static OperatorImportFromGeoJson local() { - return (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance() - .getOperator(Type.ImportFromGeoJson); + return (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Type.ImportFromGeoJson); } } diff --git a/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJsonLocal.java b/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJsonLocal.java index 8ed6b64e..e04952a4 100644 --- a/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJsonLocal.java +++ b/src/main/java/com/esri/core/geometry/OperatorImportFromGeoJsonLocal.java @@ -24,142 +24,1244 @@ package com.esri.core.geometry; import com.esri.core.geometry.VertexDescription.Semantics; +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.JsonToken; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; + +import java.io.IOException; import java.util.ArrayList; class OperatorImportFromGeoJsonLocal extends OperatorImportFromGeoJson { + @Override - public MapGeometry execute(int importFlags, Geometry.Type type, - String geoJsonString, ProgressTracker progress_tracker) - throws JSONException { - JSONObject geoJsonObject = new JSONObject(geoJsonString); - Geometry geometry = importGeometryFromGeoJson_(importFlags, type, - geoJsonObject); - SpatialReference spatialReference = importSpatialReferenceFromGeoJson_(geoJsonObject); - MapGeometry mapGeometry = new MapGeometry(geometry, spatialReference); - return mapGeometry; + public MapGeometry execute(int importFlags, Geometry.Type type, String geoJsonString, + ProgressTracker progressTracker) throws JSONException { + try { + JsonFactory factory = new JsonFactory(); + JsonParser jsonParser = factory.createJsonParser(geoJsonString); + + jsonParser.nextToken(); + + MapGeometry map_geometry = OperatorImportFromGeoJsonHelper.importFromGeoJson(importFlags, type, + new JsonParserReader(jsonParser), progressTracker, false); + return map_geometry; + + } catch (JSONException jsonException) { + throw jsonException; + } catch (JsonParseException jsonParseException) { + throw new JSONException(jsonParseException.getMessage()); + } catch (IOException ioException) { + throw new JSONException(ioException.getMessage()); + } } - static JSONArray getJSONArray(JSONObject obj, String name) - throws JSONException { - if (obj.get(name) == JSONObject.NULL) - return new JSONArray(); - else - return obj.getJSONArray(name); + @Override + public MapGeometry execute(int importFlags, Geometry.Type type, JSONObject jsonObject, + ProgressTracker progressTracker) throws JSONException { + if (jsonObject == null) + return null; + + try { + return OperatorImportFromGeoJsonHelper.importFromGeoJson(importFlags, type, new JsonValueReader(jsonObject), + progressTracker, false); + } catch (JSONException jsonException) { + throw jsonException; + } catch (JsonParseException jsonParseException) { + throw new JSONException(jsonParseException.getMessage()); + } catch (IOException ioException) { + throw new JSONException(ioException.getMessage()); + } } - @Override - public MapOGCStructure executeOGC(int import_flags, String geoJsonString, - ProgressTracker progress_tracker) throws JSONException { - JSONObject geoJsonObject = new JSONObject(geoJsonString); - ArrayList structureStack = new ArrayList(0); - ArrayList objectStack = new ArrayList(0); - AttributeStreamOfInt32 indices = new AttributeStreamOfInt32(0); - AttributeStreamOfInt32 numGeometries = new AttributeStreamOfInt32(0); - - OGCStructure root = new OGCStructure(); - root.m_structures = new ArrayList(0); - structureStack.add(root); // add dummy root - objectStack.add(geoJsonObject); - indices.add(0); - numGeometries.add(1); - - while (!objectStack.isEmpty()) { - if (indices.getLast() == numGeometries.getLast()) { - structureStack.remove(structureStack.size() - 1); - indices.removeLast(); - numGeometries.removeLast(); - continue; - } - - OGCStructure lastStructure = structureStack.get(structureStack - .size() - 1); - JSONObject lastObject = objectStack.get(objectStack.size() - 1); - objectStack.remove(objectStack.size() - 1); - indices.write(indices.size() - 1, indices.getLast() + 1); - String typeString = lastObject.getString("type"); - - if (typeString.equalsIgnoreCase("GeometryCollection")) { - OGCStructure next = new OGCStructure(); - next.m_type = 7; - next.m_structures = new ArrayList(0); - lastStructure.m_structures.add(next); - structureStack.add(next); - - JSONArray geometries = getJSONArray(lastObject, "geometries"); - indices.add(0); - numGeometries.add(geometries.length()); - - for (int i = geometries.length() - 1; i >= 0; i--) - objectStack.add(geometries.getJSONObject(i)); + static final class OperatorImportFromGeoJsonHelper { + + private AttributeStreamOfDbl m_position; + private AttributeStreamOfDbl m_zs; + private AttributeStreamOfDbl m_ms; + private AttributeStreamOfInt32 m_paths; + private AttributeStreamOfInt8 m_path_flags; + private Point m_point; // special case for Points + private boolean m_b_has_zs; + private boolean m_b_has_ms; + private boolean m_b_has_zs_known; + private boolean m_b_has_ms_known; + private int m_num_embeddings; + + OperatorImportFromGeoJsonHelper() { + m_position = null; + m_zs = null; + m_ms = null; + m_paths = null; + m_path_flags = null; + m_point = null; + m_b_has_zs = false; + m_b_has_ms = false; + m_b_has_zs_known = false; + m_b_has_ms_known = false; + m_num_embeddings = 0; + } + + static MapGeometry importFromGeoJson(int importFlags, Geometry.Type type, JsonReader json_iterator, + ProgressTracker progress_tracker, boolean skip_coordinates) + throws JSONException, JsonParseException, IOException { + assert(json_iterator.currentToken() == JsonToken.START_OBJECT); + + OperatorImportFromGeoJsonHelper geo_json_helper = new OperatorImportFromGeoJsonHelper(); + boolean b_type_found = false; + boolean b_coordinates_found = false; + boolean b_crs_found = false; + boolean b_crsURN_found = false; + String geo_json_type = null; + + Geometry geometry = null; + SpatialReference spatial_reference = null; + + JsonToken current_token; + String field_name = null; + + while ((current_token = json_iterator.nextToken()) != JsonToken.END_OBJECT) { + field_name = json_iterator.currentString(); + + if (field_name.equals("type")) { + if (b_type_found) { + throw new IllegalArgumentException("invalid argument"); + } + + b_type_found = true; + current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.VALUE_STRING) { + throw new IllegalArgumentException("invalid argument"); + } + + geo_json_type = json_iterator.currentString(); + } else if (field_name.equals("coordinates")) { + if (b_coordinates_found) { + throw new IllegalArgumentException("invalid argument"); + } + + b_coordinates_found = true; + current_token = json_iterator.nextToken(); + + if (skip_coordinates) { + json_iterator.skipChildren(); + } else {// According to the spec, the value of the + // coordinates must be an array. However, I do an + // extra check for null too. + if (current_token != JsonToken.VALUE_NULL) { + if (current_token != JsonToken.START_ARRAY) { + throw new IllegalArgumentException("invalid argument"); + } + + geo_json_helper.import_coordinates_(json_iterator, progress_tracker); + } + } + } else if (field_name.equals("crs")) { + if (b_crs_found || b_crsURN_found) { + throw new IllegalArgumentException("invalid argument"); + } + + b_crs_found = true; + current_token = json_iterator.nextToken(); + + if ((importFlags & GeoJsonImportFlags.geoJsonImportSkipCRS) == 0) + spatial_reference = importSpatialReferenceFromCrs(json_iterator, progress_tracker); + else + json_iterator.skipChildren(); + } else if (field_name.equals("crsURN")) { + if (b_crs_found || b_crsURN_found) { + throw new IllegalArgumentException("invalid argument"); + } + + b_crsURN_found = true; + current_token = json_iterator.nextToken(); + + spatial_reference = importSpatialReferenceFromCrsUrn_(json_iterator, + progress_tracker); + } else { + json_iterator.nextToken(); + json_iterator.skipChildren(); + } + } + + // According to the spec, a GeoJSON object must have both a type and + // a coordinates array + if (!b_type_found || (!b_coordinates_found && !skip_coordinates)) { + throw new IllegalArgumentException("invalid argument"); + } + + if (!skip_coordinates) + geometry = geo_json_helper.createGeometry_(geo_json_type, type.value()); + + if (!b_crs_found && !b_crsURN_found && ((importFlags & GeoJsonImportFlags.geoJsonImportSkipCRS) == 0) + && ((importFlags & GeoJsonImportFlags.geoJsonImportNoWGS84Default) == 0)) { + spatial_reference = SpatialReference.create(4326); // the spec + // gives a + // default + // of 4326 + // if no crs + // is given + } + + MapGeometry map_geometry = new MapGeometry(geometry, spatial_reference); + + assert(geo_json_helper.m_paths == null || (geo_json_helper.m_path_flags != null + && geo_json_helper.m_paths.size() == geo_json_helper.m_path_flags.size())); + + return map_geometry; + } + + // We have to import the coordinates in the most general way possible to + // not assume the type of geometry we're parsing. + // JSON allows for unordered objects, so it's possible that the + // coordinates array can come before the type tag when parsing + // sequentially, otherwise + // we would have to parse using a JSON_object, which would be easier, + // but not as space/time efficient. So this function blindly imports the + // coordinates + // into the attribute stream(s), and will later assign them to a + // geometry after the type tag is found. + private void import_coordinates_(JsonReader json_iterator, ProgressTracker progress_tracker) + throws JSONException, JsonParseException, IOException { + assert(json_iterator.currentToken() == JsonToken.START_ARRAY); + + int coordinates_level_lower = 1; + int coordinates_level_upper = 4; + + json_iterator.nextToken(); + + while (json_iterator.currentToken() != JsonToken.END_ARRAY) { + if (isDouble_(json_iterator)) { + if (coordinates_level_upper > 1) { + coordinates_level_upper = 1; + } + } else if (json_iterator.currentToken() == JsonToken.START_ARRAY) { + if (coordinates_level_lower < 2) { + coordinates_level_lower = 2; + } + } else { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower > coordinates_level_upper) { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower == coordinates_level_upper && coordinates_level_lower == 1) {// special + // code + // for + // Points + readCoordinateAsPoint_(json_iterator); + } else { + boolean b_add_path_level_3 = true; + boolean b_polygon_start_level_4 = true; + + assert(json_iterator.currentToken() == JsonToken.START_ARRAY); + json_iterator.nextToken(); + + while (json_iterator.currentToken() != JsonToken.END_ARRAY) { + if (isDouble_(json_iterator)) { + if (coordinates_level_upper > 2) { + coordinates_level_upper = 2; + } + } else if (json_iterator.currentToken() == JsonToken.START_ARRAY) { + if (coordinates_level_lower < 3) { + coordinates_level_lower = 3; + } + } else { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower > coordinates_level_upper) { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower == coordinates_level_upper && coordinates_level_lower == 2) {// LineString + // or + // MultiPoint + addCoordinate_(json_iterator); + } else { + boolean b_add_path_level_4 = true; + + assert(json_iterator.currentToken() == JsonToken.START_ARRAY); + json_iterator.nextToken(); + + while (json_iterator.currentToken() != JsonToken.END_ARRAY) { + if (isDouble_(json_iterator)) { + if (coordinates_level_upper > 3) { + coordinates_level_upper = 3; + } + } else if (json_iterator.currentToken() == JsonToken.START_ARRAY) { + if (coordinates_level_lower < 4) { + coordinates_level_lower = 4; + } + } else { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower > coordinates_level_upper) { + throw new IllegalArgumentException("invalid argument"); + } + + if (coordinates_level_lower == coordinates_level_upper + && coordinates_level_lower == 3) {// Polygon + // or + // MultiLineString + if (b_add_path_level_3) { + addPath_(); + b_add_path_level_3 = false; + } + + addCoordinate_(json_iterator); + } else { + assert(json_iterator.currentToken() == JsonToken.START_ARRAY); + json_iterator.nextToken(); + + if (json_iterator.currentToken() != JsonToken.END_ARRAY) { + if (!isDouble_(json_iterator)) { + throw new IllegalArgumentException("invalid argument"); + } + + assert(coordinates_level_lower == coordinates_level_upper + && coordinates_level_lower == 4); + // MultiPolygon + + if (b_add_path_level_4) { + addPath_(); + addPathFlag_(b_polygon_start_level_4); + b_add_path_level_4 = false; + b_polygon_start_level_4 = false; + } + + addCoordinate_(json_iterator); + } + + json_iterator.nextToken(); + } + } + + json_iterator.nextToken(); + } + } + + json_iterator.nextToken(); + } + } + + if (m_paths != null) { + m_paths.add(m_position.size() / 2); // add final path size + } + if (m_path_flags != null) { + m_path_flags.add((byte) 0); // to match the paths size + } + + m_num_embeddings = coordinates_level_lower; + } + + private void readCoordinateAsPoint_(JsonReader json_iterator) + throws JSONException, JsonParseException, IOException { + assert(isDouble_(json_iterator)); + + m_point = new Point(); + + double x = readDouble_(json_iterator); + json_iterator.nextToken(); + double y = readDouble_(json_iterator); + json_iterator.nextToken(); + + if (NumberUtils.isNaN(y)) { + x = NumberUtils.NaN(); + } + + m_point.setXY(x, y); + + if (isDouble_(json_iterator)) { + double z = readDouble_(json_iterator); + json_iterator.nextToken(); + m_point.setZ(z); + } + + if (isDouble_(json_iterator)) { + double m = readDouble_(json_iterator); + json_iterator.nextToken(); + m_point.setM(m); + } + + if (json_iterator.currentToken() != JsonToken.END_ARRAY) { + throw new IllegalArgumentException("invalid argument"); + } + } + + private void addCoordinate_(JsonReader json_iterator) throws JSONException, JsonParseException, IOException { + assert(isDouble_(json_iterator)); + + if (m_position == null) { + m_position = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); + } + + double x = readDouble_(json_iterator); + json_iterator.nextToken(); + double y = readDouble_(json_iterator); + json_iterator.nextToken(); + + int size = m_position.size(); + + m_position.add(x); + m_position.add(y); + + if (isDouble_(json_iterator)) { + if (!m_b_has_zs_known) { + m_b_has_zs_known = true; + m_b_has_zs = true; + m_zs = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); + } else { + if (!m_b_has_zs) { + m_zs = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(size >> 1, + VertexDescription.getDefaultValue(Semantics.Z)); + m_b_has_zs = true; + } + } + + double z = readDouble_(json_iterator); + json_iterator.nextToken(); + m_zs.add(z); + } else { + if (!m_b_has_zs_known) { + m_b_has_zs_known = true; + m_b_has_zs = false; + } else { + if (m_b_has_zs) { + m_zs.add(VertexDescription.getDefaultValue(Semantics.Z)); + } + } + } + + if (isDouble_(json_iterator)) { + if (!m_b_has_ms_known) { + m_b_has_ms_known = true; + m_b_has_ms = true; + m_ms = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); + } else { + if (!m_b_has_ms) { + m_ms = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(size >> 1, + VertexDescription.getDefaultValue(Semantics.M)); + m_b_has_ms = true; + } + } + + double m = readDouble_(json_iterator); + json_iterator.nextToken(); + m_ms.add(m); + } else { + if (!m_b_has_ms_known) { + m_b_has_ms_known = true; + m_b_has_ms = false; + } else { + if (m_b_has_ms) { + m_zs.add(VertexDescription.getDefaultValue(Semantics.M)); + } + } + } + + if (json_iterator.currentToken() != JsonToken.END_ARRAY) { + throw new IllegalArgumentException("invalid argument"); + } + } + + private void addPath_() { + if (m_paths == null) { + m_paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream(0); + } + + if (m_position == null) { + m_paths.add(0); + } else { + m_paths.add(m_position.size() / 2); + } + } + + private void addPathFlag_(boolean b_polygon_start) { + if (m_path_flags == null) { + m_path_flags = (AttributeStreamOfInt8) AttributeStreamBase.createByteStream(0); + } + + if (b_polygon_start) { + m_path_flags.add((byte) (PathFlags.enumClosed | PathFlags.enumOGCStartPolygon)); + } else { + m_path_flags.add((byte) PathFlags.enumClosed); + } + } + + private double readDouble_(JsonReader json_iterator) throws JSONException, JsonParseException, IOException { + JsonToken current_token = json_iterator.currentToken(); + if (current_token == JsonToken.VALUE_NULL + || (current_token == JsonToken.VALUE_STRING && json_iterator.currentString().equals("NaN"))) { + return NumberUtils.NaN(); + } else { + return json_iterator.currentDoubleValue(); + } + } + + private boolean isDouble_(JsonReader json_iterator) throws JSONException, JsonParseException, IOException { + JsonToken current_token = json_iterator.currentToken(); + + if (current_token == JsonToken.VALUE_NUMBER_FLOAT) { + return true; + } + + if (current_token == JsonToken.VALUE_NUMBER_INT) { + return true; + } + + if (current_token == JsonToken.VALUE_NULL + || (current_token == JsonToken.VALUE_STRING && json_iterator.currentString().equals("NaN"))) { + return true; + } + + return false; + } + + private Geometry createGeometry_(String geo_json_type, int type) + throws JSONException, JsonParseException, IOException { + Geometry geometry; + + if (type != Geometry.GeometryType.Unknown) { + switch (type) { + case Geometry.GeometryType.Polygon: + if (!geo_json_type.equals("MultiPolygon") && !geo_json_type.equals("Polygon")) { + throw new GeometryException("invalid shape type"); + } + break; + case Geometry.GeometryType.Polyline: + if (!geo_json_type.equals("MultiLineString") && !geo_json_type.equals("LineString")) { + throw new GeometryException("invalid shape type"); + } + break; + case Geometry.GeometryType.MultiPoint: + if (!geo_json_type.equals("MultiPoint")) { + throw new GeometryException("invalid shape type"); + } + break; + case Geometry.GeometryType.Point: + if (!geo_json_type.equals("Point")) { + throw new GeometryException("invalid shape type"); + } + break; + default: + throw new GeometryException("invalid shape type"); + } + } + + if (m_position == null && m_point == null) { + if (geo_json_type.equals("Point")) { + if (m_num_embeddings > 1) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = new Point(); + } else if (geo_json_type.equals("MultiPoint")) { + if (m_num_embeddings > 2) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = new MultiPoint(); + } else if (geo_json_type.equals("LineString")) { + if (m_num_embeddings > 2) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = new Polyline(); + } else if (geo_json_type.equals("MultiLineString")) { + if (m_num_embeddings > 3) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = new Polyline(); + } else if (geo_json_type.equals("Polygon")) { + if (m_num_embeddings > 3) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = new Polygon(); + } else if (geo_json_type.equals("MultiPolygon")) { + assert(m_num_embeddings <= 4); + geometry = new Polygon(); + } else { + throw new IllegalArgumentException("invalid argument"); + } + } else if (m_num_embeddings == 1) { + if (!geo_json_type.equals("Point")) { + throw new IllegalArgumentException("invalid argument"); + } + + assert(m_point != null); + geometry = m_point; + } else if (m_num_embeddings == 2) { + if (geo_json_type.equals("MultiPoint")) { + geometry = createMultiPointFromStreams_(); + } else if (geo_json_type.equals("LineString")) { + geometry = createPolylineFromStreams_(); + } else { + throw new IllegalArgumentException("invalid argument"); + } + } else if (m_num_embeddings == 3) { + if (geo_json_type.equals("Polygon")) { + geometry = createPolygonFromStreams_(); + } else if (geo_json_type.equals("MultiLineString")) { + geometry = createPolylineFromStreams_(); + } else { + throw new IllegalArgumentException("invalid argument"); + } } else { - int ogcType; + if (!geo_json_type.equals("MultiPolygon")) { + throw new IllegalArgumentException("invalid argument"); + } + + geometry = createPolygonFromStreams_(); + } + + return geometry; + } + + private Geometry createPolygonFromStreams_() { + assert(m_position != null); + assert(m_paths != null); + assert((m_num_embeddings == 3 && m_path_flags == null) || (m_num_embeddings == 4 && m_path_flags != null)); + + Polygon polygon = new Polygon(); + MultiPathImpl multi_path_impl = (MultiPathImpl) polygon._getImpl(); + + checkPathPointCountsForMultiPath_(true); + multi_path_impl.setAttributeStreamRef(Semantics.POSITION, m_position); + + if (m_b_has_zs) { + assert(m_zs != null); + multi_path_impl.setAttributeStreamRef(Semantics.Z, m_zs); + } + + if (m_b_has_ms) { + assert(m_ms != null); + multi_path_impl.setAttributeStreamRef(Semantics.M, m_ms); + } + + if (m_path_flags == null) { + m_path_flags = (AttributeStreamOfInt8) AttributeStreamBase.createByteStream(m_paths.size(), (byte) 0); + m_path_flags.setBits(0, (byte) (PathFlags.enumClosed | PathFlags.enumOGCStartPolygon)); + + for (int i = 1; i < m_path_flags.size() - 1; i++) { + m_path_flags.setBits(i, (byte) PathFlags.enumClosed); + } + } + + multi_path_impl.setPathStreamRef(m_paths); + multi_path_impl.setPathFlagsStreamRef(m_path_flags); + multi_path_impl.notifyModified(MultiVertexGeometryImpl.DirtyFlags.DirtyAll); - if (typeString.equalsIgnoreCase("Point")) - ogcType = 1; - else if (typeString.equalsIgnoreCase("LineString")) - ogcType = 2; - else if (typeString.equalsIgnoreCase("Polygon")) - ogcType = 3; - else if (typeString.equalsIgnoreCase("MultiPoint")) - ogcType = 4; - else if (typeString.equalsIgnoreCase("MultiLineString")) - ogcType = 5; - else if (typeString.equalsIgnoreCase("MultiPolygon")) - ogcType = 6; - else - throw new UnsupportedOperationException(); + AttributeStreamOfInt8 path_flags_clone = new AttributeStreamOfInt8(m_path_flags); - Geometry geometry = importGeometryFromGeoJson_(import_flags, - Geometry.Type.Unknown, lastObject); + for (int i = 0; i < path_flags_clone.size() - 1; i++) { + assert((path_flags_clone.read(i) & PathFlags.enumClosed) != 0); + assert((m_path_flags.read(i) & PathFlags.enumClosed) != 0); + + if ((path_flags_clone.read(i) & PathFlags.enumOGCStartPolygon) != 0) {// Should + // be + // clockwise + if (!InternalUtils.isClockwiseRing(multi_path_impl, i)) { + multi_path_impl.reversePath(i); // make clockwise + } + } else {// Should be counter-clockwise + if (InternalUtils.isClockwiseRing(multi_path_impl, i)) { + multi_path_impl.reversePath(i); // make + // counter-clockwise + } + } + } + multi_path_impl.setPathFlagsStreamRef(path_flags_clone); + multi_path_impl.setDirtyOGCFlags(false); + + return polygon; + } + + private Geometry createPolylineFromStreams_() { + assert(m_position != null); + assert((m_num_embeddings == 2 && m_paths == null) || (m_num_embeddings == 3 && m_paths != null)); + assert(m_path_flags == null); + + Polyline polyline = new Polyline(); + MultiPathImpl multi_path_impl = (MultiPathImpl) polyline._getImpl(); + + if (m_paths == null) { + m_paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream(0); + m_paths.add(0); + m_paths.add(m_position.size() / 2); + } + + checkPathPointCountsForMultiPath_(false); + multi_path_impl.setAttributeStreamRef(Semantics.POSITION, m_position); + + if (m_b_has_zs) { + assert(m_zs != null); + multi_path_impl.setAttributeStreamRef(Semantics.Z, m_zs); + } + + if (m_b_has_ms) { + assert(m_ms != null); + multi_path_impl.setAttributeStreamRef(Semantics.M, m_ms); + } + + m_path_flags = (AttributeStreamOfInt8) AttributeStreamBase.createByteStream(m_paths.size(), (byte) 0); + + multi_path_impl.setPathStreamRef(m_paths); + multi_path_impl.setPathFlagsStreamRef(m_path_flags); + multi_path_impl.notifyModified(MultiVertexGeometryImpl.DirtyFlags.DirtyAll); + + return polyline; + } + + private Geometry createMultiPointFromStreams_() { + assert(m_position != null); + assert(m_paths == null); + assert(m_path_flags == null); - OGCStructure leaf = new OGCStructure(); - leaf.m_type = ogcType; - leaf.m_geometry = geometry; - lastStructure.m_structures.add(leaf); + MultiPoint multi_point = new MultiPoint(); + MultiPointImpl multi_point_impl = (MultiPointImpl) multi_point._getImpl(); + multi_point_impl.setAttributeStreamRef(Semantics.POSITION, m_position); + + if (m_b_has_zs) { + assert(m_zs != null); + multi_point_impl.setAttributeStreamRef(Semantics.Z, m_zs); + } + + if (m_b_has_ms) { + assert(m_ms != null); + multi_point_impl.setAttributeStreamRef(Semantics.M, m_ms); + } + multi_point_impl.resize(m_position.size() / 2); + multi_point_impl.notifyModified(MultiVertexGeometryImpl.DirtyFlags.DirtyAll); + + return multi_point; + } + + private void checkPathPointCountsForMultiPath_(boolean b_is_polygon) { + Point2D pt1 = new Point2D(), pt2 = new Point2D(); + double z1 = 0.0, z2 = 0.0, m1 = 0.0, m2 = 0.0; + int path_count = m_paths.size() - 1; + int guess_adjustment = 0; + + if (b_is_polygon) {// Polygon + guess_adjustment = path_count; // may remove up to path_count + // number of points + } else {// Polyline + for (int path = 0; path < path_count; path++) { + int path_size = m_paths.read(path + 1) - m_paths.read(path); + + if (path_size == 1) { + guess_adjustment--; // will add a point for each path + // containing only 1 point + } + } + + if (guess_adjustment == 0) { + return; // all paths are okay + } } + + AttributeStreamOfDbl adjusted_position = (AttributeStreamOfDbl) AttributeStreamBase + .createDoubleStream(m_position.size() - guess_adjustment); + AttributeStreamOfInt32 adjusted_paths = (AttributeStreamOfInt32) AttributeStreamBase + .createIndexStream(m_paths.size()); + AttributeStreamOfDbl adjusted_zs = null; + AttributeStreamOfDbl adjusted_ms = null; + + if (m_b_has_zs) { + adjusted_zs = (AttributeStreamOfDbl) AttributeStreamBase + .createDoubleStream(m_zs.size() - guess_adjustment); + } + + if (m_b_has_ms) { + adjusted_ms = (AttributeStreamOfDbl) AttributeStreamBase + .createDoubleStream(m_ms.size() - guess_adjustment); + } + + int adjusted_start = 0; + adjusted_paths.write(0, 0); + + for (int path = 0; path < path_count; path++) { + int path_start = m_paths.read(path); + int path_end = m_paths.read(path + 1); + int path_size = path_end - path_start; + assert(path_size != 0); // we should not have added empty parts + // on import + + if (path_size == 1) { + insertIntoAdjustedStreams_(adjusted_position, adjusted_zs, adjusted_ms, adjusted_start, path_start, + path_size); + insertIntoAdjustedStreams_(adjusted_position, adjusted_zs, adjusted_ms, adjusted_start + 1, + path_start, path_size); + adjusted_start += 2; + } else if (path_size >= 3 && b_is_polygon) { + m_position.read(path_start * 2, pt1); + m_position.read((path_end - 1) * 2, pt2); + + if (m_b_has_zs) { + z1 = m_zs.readAsDbl(path_start); + z2 = m_zs.readAsDbl(path_end - 1); + } + + if (m_b_has_ms) { + m1 = m_ms.readAsDbl(path_start); + m2 = m_ms.readAsDbl(path_end - 1); + } + + if (pt1.equals(pt2) && (NumberUtils.isNaN(z1) && NumberUtils.isNaN(z2) || z1 == z2) + && (NumberUtils.isNaN(m1) && NumberUtils.isNaN(m2) || m1 == m2)) { + insertIntoAdjustedStreams_(adjusted_position, adjusted_zs, adjusted_ms, adjusted_start, + path_start, path_size - 1); + adjusted_start += path_size - 1; + } else { + insertIntoAdjustedStreams_(adjusted_position, adjusted_zs, adjusted_ms, adjusted_start, + path_start, path_size); + adjusted_start += path_size; + } + } else { + insertIntoAdjustedStreams_(adjusted_position, adjusted_zs, adjusted_ms, adjusted_start, path_start, + path_size); + adjusted_start += path_size; + } + adjusted_paths.write(path + 1, adjusted_start); + } + + m_position = adjusted_position; + m_paths = adjusted_paths; + m_zs = adjusted_zs; + m_ms = adjusted_ms; } - MapOGCStructure mapOGCStructure = new MapOGCStructure(); - mapOGCStructure.m_ogcStructure = root; - mapOGCStructure.m_spatialReference = importSpatialReferenceFromGeoJson_(geoJsonObject); + private void insertIntoAdjustedStreams_(AttributeStreamOfDbl adjusted_position, + AttributeStreamOfDbl adjusted_zs, AttributeStreamOfDbl adjusted_ms, int adjusted_start, int path_start, + int count) { + adjusted_position.insertRange(adjusted_start * 2, m_position, path_start * 2, count * 2, true, 2, + adjusted_start * 2); + + if (m_b_has_zs) { + adjusted_zs.insertRange(adjusted_start, m_zs, path_start, count, true, 1, adjusted_start); + } + + if (m_b_has_ms) { + adjusted_ms.insertRange(adjusted_start, m_ms, path_start, count, true, 1, adjusted_start); + } + } + + static SpatialReference importSpatialReferenceFromCrs(JsonReader json_iterator, + ProgressTracker progress_tracker) throws JSONException, JsonParseException, IOException { + // According to the spec, a null crs corresponds to no spatial + // reference + if (json_iterator.currentToken() == JsonToken.VALUE_NULL) { + return null; + } + + if (json_iterator.currentToken() == JsonToken.VALUE_STRING) {// see + // http://wiki.geojson.org/RFC-001 + // (this + // is + // deprecated, + // but + // there + // may + // be + // data + // with + // this + // format) + + String crs_short_form = json_iterator.currentString(); + int wkid = GeoJsonCrsTables.getWkidFromCrsShortForm(crs_short_form); + + if (wkid == -1) { + throw new GeometryException("not implemented"); + } + + SpatialReference spatial_reference = null; + + try { + spatial_reference = SpatialReference.create(wkid); + } catch (Exception e) { + } + + return spatial_reference; + } + + if (json_iterator.currentToken() != JsonToken.START_OBJECT) { + throw new IllegalArgumentException("invalid argument"); + } + + // This is to support all cases of crs identifiers I've seen. Some + // may be rare or are legacy formats, but all are simple to + // accomodate. + boolean b_found_type = false; + boolean b_found_properties = false; + boolean b_found_properties_name = false; + boolean b_found_properties_href = false; + boolean b_found_properties_urn = false; + boolean b_found_properties_url = false; + boolean b_found_properties_code = false; + boolean b_found_esriwkt = false; + String crs_field = null, properties_field = null, type = null, crs_identifier_name = null, + crs_identifier_urn = null, crs_identifier_href = null, crs_identifier_url = null, esriwkt = null; + int crs_identifier_code = -1; + JsonToken current_token; + + while (json_iterator.nextToken() != JsonToken.END_OBJECT) { + crs_field = json_iterator.currentString(); + + if (crs_field.equals("type")) { + if (b_found_type) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_type = true; + + current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.VALUE_STRING) { + throw new IllegalArgumentException("invalid argument"); + } + + type = json_iterator.currentString(); + } else if (crs_field.equals("properties")) { + if (b_found_properties) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties = true; + + current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.START_OBJECT) { + throw new IllegalArgumentException("invalid argument"); + } + + while (json_iterator.nextToken() != JsonToken.END_OBJECT) { + properties_field = json_iterator.currentString(); + + if (properties_field.equals("name")) { + if (b_found_properties_name) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties_name = true; + crs_identifier_name = getCrsIdentifier_(json_iterator); + } else if (properties_field.equals("href")) { + if (b_found_properties_href) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties_href = true; + crs_identifier_href = getCrsIdentifier_(json_iterator); + } else if (properties_field.equals("urn")) { + if (b_found_properties_urn) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties_urn = true; + crs_identifier_urn = getCrsIdentifier_(json_iterator); + } else if (properties_field.equals("url")) { + if (b_found_properties_url) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties_url = true; + crs_identifier_url = getCrsIdentifier_(json_iterator); + } else if (properties_field.equals("code")) { + if (b_found_properties_code) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_properties_code = true; + + current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.VALUE_NUMBER_INT) { + throw new IllegalArgumentException("invalid argument"); + } + + crs_identifier_code = json_iterator.currentIntValue(); + } else { + json_iterator.nextToken(); + json_iterator.skipChildren(); + } + } + } else if (crs_field.equals("esriwkt")) { + if (b_found_esriwkt) { + throw new IllegalArgumentException("invalid argument"); + } + + b_found_esriwkt = true; + + current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.VALUE_STRING) { + throw new IllegalArgumentException("invalid argument"); + } + + esriwkt = json_iterator.currentString(); + } else { + json_iterator.nextToken(); + json_iterator.skipChildren(); + } + } + + if ((!b_found_type || !b_found_properties) && !b_found_esriwkt) { + throw new IllegalArgumentException("invalid argument"); + } + + int wkid = -1; + + if (b_found_properties_name) { + wkid = GeoJsonCrsTables.getWkidFromCrsName(crs_identifier_name); // see + // http://wiki.geojson.org/GeoJSON_draft_version_6 + // (most + // common) + } else if (b_found_properties_href) { + wkid = GeoJsonCrsTables.getWkidFromCrsHref(crs_identifier_href); // see + // http://wiki.geojson.org/GeoJSON_draft_version_6 + // (somewhat + // common) + } else if (b_found_properties_urn) { + wkid = GeoJsonCrsTables.getWkidFromCrsOgcUrn(crs_identifier_urn); // see + // http://wiki.geojson.org/GeoJSON_draft_version_5 + // (rare) + } else if (b_found_properties_url) { + wkid = GeoJsonCrsTables.getWkidFromCrsHref(crs_identifier_url); // see + // http://wiki.geojson.org/GeoJSON_draft_version_5 + // (rare) + } else if (b_found_properties_code) { + wkid = crs_identifier_code; // see + // http://wiki.geojson.org/GeoJSON_draft_version_5 + // (rare) + } else if (!b_found_esriwkt) { + throw new GeometryException("not implemented"); + } + + if (wkid < 0 && !b_found_esriwkt && !b_found_properties_name) { + throw new GeometryException("not implemented"); + } + + SpatialReference spatial_reference = null; + + if (wkid > 0) { + try { + spatial_reference = SpatialReference.create(wkid); + } catch (Exception e) { + } + } + + if (spatial_reference == null) { + try { + if (b_found_esriwkt) {// I exported crs wkt strings like + // this + spatial_reference = SpatialReference.create(esriwkt); + } else if (b_found_properties_name) {// AGOL exported crs + // wkt strings like + // this where the + // crs identifier of + // the properties + // name is like + // "ESRI:" + String potential_wkt = GeoJsonCrsTables.getWktFromCrsName(crs_identifier_name); + spatial_reference = SpatialReference.create(potential_wkt); + } + } catch (Exception e) { + } + } + + return spatial_reference; + } + + // see http://geojsonwg.github.io/draft-geojson/draft.html + static SpatialReference importSpatialReferenceFromCrsUrn_(JsonReader json_iterator, + ProgressTracker progress_tracker) throws JSONException, JsonParseException, IOException { + // According to the spec, a null crs corresponds to no spatial + // reference + if (json_iterator.currentToken() == JsonToken.VALUE_NULL) { + return null; + } + + if (json_iterator.currentToken() != JsonToken.VALUE_STRING) { + throw new IllegalArgumentException("invalid argument"); + } + + String crs_identifier_urn = json_iterator.currentString(); + + int wkid = GeoJsonCrsTables.getWkidFromCrsName(crs_identifier_urn); // This + // will + // check + // for + // short + // form + // name, + // as + // well + // as + // long + // form + // URNs + + if (wkid == -1) { + throw new GeometryException("not implemented"); + } + + SpatialReference spatial_reference = SpatialReference.create(wkid); + + return spatial_reference; + } + + private static String getCrsIdentifier_(JsonReader json_iterator) + throws JSONException, JsonParseException, IOException { + JsonToken current_token = json_iterator.nextToken(); + + if (current_token != JsonToken.VALUE_STRING) { + throw new IllegalArgumentException("invalid argument"); + } + + return json_iterator.currentString(); + } + + } + + static JSONArray getJSONArray(JSONObject obj, String name) throws JSONException { + if (obj.get(name) == JSONObject.NULL) + return new JSONArray(); + else + return obj.getJSONArray(name); + } + + @Override + public MapOGCStructure executeOGC(int import_flags, String geoJsonString, ProgressTracker progress_tracker) + throws JSONException { + MapOGCStructure mapOGCStructure = null; + try { + JSONObject geoJsonObject = new JSONObject(geoJsonString); + ArrayList structureStack = new ArrayList(0); + ArrayList objectStack = new ArrayList(0); + AttributeStreamOfInt32 indices = new AttributeStreamOfInt32(0); + AttributeStreamOfInt32 numGeometries = new AttributeStreamOfInt32(0); + OGCStructure root = new OGCStructure(); + root.m_structures = new ArrayList(0); + structureStack.add(root); // add dummy root + objectStack.add(geoJsonObject); + indices.add(0); + numGeometries.add(1); + while (!objectStack.isEmpty()) { + if (indices.getLast() == numGeometries.getLast()) { + structureStack.remove(structureStack.size() - 1); + indices.removeLast(); + numGeometries.removeLast(); + continue; + } + OGCStructure lastStructure = structureStack.get(structureStack.size() - 1); + JSONObject lastObject = objectStack.get(objectStack.size() - 1); + objectStack.remove(objectStack.size() - 1); + indices.write(indices.size() - 1, indices.getLast() + 1); + String typeString = lastObject.getString("type"); + if (typeString.equalsIgnoreCase("GeometryCollection")) { + OGCStructure next = new OGCStructure(); + next.m_type = 7; + next.m_structures = new ArrayList(0); + lastStructure.m_structures.add(next); + structureStack.add(next); + JSONArray geometries = getJSONArray(lastObject, "geometries"); + indices.add(0); + numGeometries.add(geometries.length()); + for (int i = geometries.length() - 1; i >= 0; i--) + objectStack.add(geometries.getJSONObject(i)); + } else { + int ogcType; + if (typeString.equalsIgnoreCase("Point")) + ogcType = 1; + else if (typeString.equalsIgnoreCase("LineString")) + ogcType = 2; + else if (typeString.equalsIgnoreCase("Polygon")) + ogcType = 3; + else if (typeString.equalsIgnoreCase("MultiPoint")) + ogcType = 4; + else if (typeString.equalsIgnoreCase("MultiLineString")) + ogcType = 5; + else if (typeString.equalsIgnoreCase("MultiPolygon")) + ogcType = 6; + else + throw new UnsupportedOperationException(); + + MapGeometry map_geometry = execute(import_flags | GeoJsonImportFlags.geoJsonImportSkipCRS, + Geometry.Type.Unknown, lastObject, null); + OGCStructure leaf = new OGCStructure(); + leaf.m_type = ogcType; + leaf.m_geometry = map_geometry.getGeometry(); + lastStructure.m_structures.add(leaf); + } + } + mapOGCStructure = new MapOGCStructure(); + mapOGCStructure.m_ogcStructure = root; + + if ((import_flags & GeoJsonImportFlags.geoJsonImportSkipCRS) == 0) + mapOGCStructure.m_spatialReference = importSpatialReferenceFromGeoJson_(import_flags, geoJsonObject); + } catch (JSONException jsonException) { + throw jsonException; + } catch (JsonParseException jsonParseException) { + throw new JSONException(jsonParseException.getMessage()); + } catch (IOException ioException) { + throw new JSONException(ioException.getMessage()); + } return mapOGCStructure; } - private static SpatialReference importSpatialReferenceFromGeoJson_( - JSONObject crsJSONObject) throws JSONException { - String wkidString = crsJSONObject.optString("crs", ""); + private static SpatialReference importSpatialReferenceFromGeoJson_(int importFlags, JSONObject crsJSONObject) + throws JSONException, JsonParseException, IOException { + + SpatialReference spatial_reference = null; + boolean b_crs_found = false, b_crsURN_found = false; - if (wkidString.equals("")) { - return SpatialReference.create(4326); + Object opt = crsJSONObject.opt("crs"); + + if (opt != null) { + b_crs_found = true; + JSONObject crs_object = new JSONObject(); + crs_object.put("crs", opt); + JsonValueReader json_iterator = new JsonValueReader(crs_object); + json_iterator.nextToken(); + json_iterator.nextToken(); + return OperatorImportFromGeoJsonHelper.importSpatialReferenceFromCrs(json_iterator, null); } - // wkidString will be of the form "EPSG:#" where # is an integer, the - // EPSG ID. - // If the ID is below 32,767, then the EPSG ID will agree with the - // well-known (WKID). + opt = crsJSONObject.opt("crsURN"); - if (wkidString.length() <= 5) { - throw new IllegalArgumentException(); + if (opt != null) { + b_crsURN_found = true; + JSONObject crs_object = new JSONObject(); + crs_object.put("crsURN", opt); + JsonValueReader json_iterator = new JsonValueReader(crs_object); + json_iterator.nextToken(); + json_iterator.nextToken(); + return OperatorImportFromGeoJsonHelper.importSpatialReferenceFromCrs(json_iterator, null); } - // Throws a JSON exception if this cannot appropriately be converted to - // an integer. - int wkid = Integer.valueOf(wkidString.substring(5)).intValue(); + if ((importFlags & GeoJsonImportFlags.geoJsonImportNoWGS84Default) == 0) { + spatial_reference = SpatialReference.create(4326); + } - return SpatialReference.create(wkid); + return spatial_reference; } - private static Geometry importGeometryFromGeoJson_(int importFlags, - Geometry.Type type, JSONObject geometryJSONObject) - throws JSONException { + /* + private static Geometry importGeometryFromGeoJson_(int importFlags, Geometry.Type type, + JSONObject geometryJSONObject) throws JSONException { String typeString = geometryJSONObject.getString("type"); - JSONArray coordinateArray = getJSONArray(geometryJSONObject, - "coordinates"); - + JSONArray coordinateArray = getJSONArray(geometryJSONObject, "coordinates"); if (typeString.equalsIgnoreCase("MultiPolygon")) { if (type != Geometry.Type.Polygon && type != Geometry.Type.Unknown) throw new IllegalArgumentException("invalid shapetype"); @@ -169,8 +1271,7 @@ private static Geometry importGeometryFromGeoJson_(int importFlags, throw new IllegalArgumentException("invalid shapetype"); return lineStringTaggedText_(true, importFlags, coordinateArray); } else if (typeString.equalsIgnoreCase("MultiPoint")) { - if (type != Geometry.Type.MultiPoint - && type != Geometry.Type.Unknown) + if (type != Geometry.Type.MultiPoint && type != Geometry.Type.Unknown) throw new IllegalArgumentException("invalid shapetype"); return multiPointTaggedText_(importFlags, coordinateArray); } else if (typeString.equalsIgnoreCase("Polygon")) { @@ -190,8 +1291,8 @@ private static Geometry importGeometryFromGeoJson_(int importFlags, } } - private static Geometry polygonTaggedText_(boolean bMultiPolygon, - int importFlags, JSONArray coordinateArray) throws JSONException { + private static Geometry polygonTaggedText_(boolean bMultiPolygon, int importFlags, JSONArray coordinateArray) + throws JSONException { MultiPath multiPath; MultiPathImpl multiPathImpl; AttributeStreamOfDbl zs = null; @@ -199,49 +1300,30 @@ private static Geometry polygonTaggedText_(boolean bMultiPolygon, AttributeStreamOfDbl position; AttributeStreamOfInt32 paths; AttributeStreamOfInt8 path_flags; - - position = (AttributeStreamOfDbl) AttributeStreamBase - .createDoubleStream(0); - paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream( - 1, 0); - path_flags = (AttributeStreamOfInt8) AttributeStreamBase - .createByteStream(1, (byte) 0); - + position = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); + paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream(1, 0); + path_flags = (AttributeStreamOfInt8) AttributeStreamBase.createByteStream(1, (byte) 0); multiPath = new Polygon(); multiPathImpl = (MultiPathImpl) multiPath._getImpl(); - int pointCount; - if (bMultiPolygon) { - pointCount = multiPolygonText_(zs, ms, position, paths, path_flags, - coordinateArray); + pointCount = multiPolygonText_(zs, ms, position, paths, path_flags, coordinateArray); } else { - pointCount = polygonText_(zs, ms, position, paths, path_flags, 0, - coordinateArray); + pointCount = polygonText_(zs, ms, position, paths, path_flags, 0, coordinateArray); } - if (pointCount != 0) { - assert (2 * pointCount == position.size()); - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.POSITION, position); + assert(2 * pointCount == position.size()); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.POSITION, position); multiPathImpl.setPathStreamRef(paths); multiPathImpl.setPathFlagsStreamRef(path_flags); - if (zs != null) { - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.Z, zs); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.Z, zs); } - if (ms != null) { - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.M, ms); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.M, ms); } - multiPathImpl.notifyModified(MultiPathImpl.DirtyFlags.DirtyAll); - - AttributeStreamOfInt8 path_flags_clone = new AttributeStreamOfInt8( - path_flags); - + AttributeStreamOfInt8 path_flags_clone = new AttributeStreamOfInt8(path_flags); for (int i = 0; i < path_flags_clone.size() - 1; i++) { if (((int) path_flags_clone.read(i) & (int) PathFlags.enumOGCStartPolygon) != 0) {// Should // be @@ -253,23 +1335,17 @@ private static Geometry polygonTaggedText_(boolean bMultiPolygon, multiPathImpl.reversePath(i); // make counter-clockwise } } - multiPathImpl.setPathFlagsStreamRef(path_flags_clone); - } - if ((importFlags & (int) GeoJsonImportFlags.geoJsonImportNonTrusted) == 0) { - multiPathImpl.setIsSimple(MultiPathImpl.GeometryXSimple.Weak, 0.0, - false); + multiPathImpl.setIsSimple(MultiPathImpl.GeometryXSimple.Weak, 0.0, false); } - multiPathImpl.setDirtyOGCFlags(false); - return multiPath; } - private static Geometry lineStringTaggedText_(boolean bMultiLineString, - int importFlags, JSONArray coordinateArray) throws JSONException { + private static Geometry lineStringTaggedText_(boolean bMultiLineString, int importFlags, JSONArray coordinateArray) + throws JSONException { MultiPath multiPath; MultiPathImpl multiPathImpl; AttributeStreamOfDbl zs = null; @@ -277,265 +1353,190 @@ private static Geometry lineStringTaggedText_(boolean bMultiLineString, AttributeStreamOfDbl position; AttributeStreamOfInt32 paths; AttributeStreamOfInt8 path_flags; - - position = (AttributeStreamOfDbl) AttributeStreamBase - .createDoubleStream(0); - paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream( - 1, 0); - path_flags = (AttributeStreamOfInt8) AttributeStreamBase - .createByteStream(1, (byte) 0); - + position = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); + paths = (AttributeStreamOfInt32) AttributeStreamBase.createIndexStream(1, 0); + path_flags = (AttributeStreamOfInt8) AttributeStreamBase.createByteStream(1, (byte) 0); multiPath = new Polyline(); multiPathImpl = (MultiPathImpl) multiPath._getImpl(); - int pointCount; - if (bMultiLineString) { - pointCount = multiLineStringText_(zs, ms, position, paths, - path_flags, coordinateArray); + pointCount = multiLineStringText_(zs, ms, position, paths, path_flags, coordinateArray); } else { - pointCount = lineStringText_(false, zs, ms, position, paths, - path_flags, coordinateArray); + pointCount = lineStringText_(false, zs, ms, position, paths, path_flags, coordinateArray); } - if (pointCount != 0) { - assert (2 * pointCount == position.size()); - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.POSITION, position); + assert(2 * pointCount == position.size()); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.POSITION, position); multiPathImpl.setPathStreamRef(paths); multiPathImpl.setPathFlagsStreamRef(path_flags); - if (zs != null) { - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.Z, zs); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.Z, zs); } - if (ms != null) { - multiPathImpl.setAttributeStreamRef( - VertexDescription.Semantics.M, ms); + multiPathImpl.setAttributeStreamRef(VertexDescription.Semantics.M, ms); } - multiPathImpl.notifyModified(MultiPathImpl.DirtyFlags.DirtyAll); } - return multiPath; } - private static Geometry multiPointTaggedText_(int importFlags, - JSONArray coordinateArray) throws JSONException { + private static Geometry multiPointTaggedText_(int importFlags, JSONArray coordinateArray) throws JSONException { MultiPoint multiPoint; MultiPointImpl multiPointImpl; AttributeStreamOfDbl zs = null; AttributeStreamOfDbl ms = null; AttributeStreamOfDbl position; - - position = (AttributeStreamOfDbl) AttributeStreamBase - .createDoubleStream(0); - + position = (AttributeStreamOfDbl) AttributeStreamBase.createDoubleStream(0); multiPoint = new MultiPoint(); multiPointImpl = (MultiPointImpl) multiPoint._getImpl(); - int pointCount = multiPointText_(zs, ms, position, coordinateArray); - if (pointCount != 0) { - assert (2 * pointCount == position.size()); + assert(2 * pointCount == position.size()); multiPointImpl.resize(pointCount); - multiPointImpl.setAttributeStreamRef( - VertexDescription.Semantics.POSITION, position); - + multiPointImpl.setAttributeStreamRef(VertexDescription.Semantics.POSITION, position); multiPointImpl.notifyModified(MultiPointImpl.DirtyFlags.DirtyAll); } - return multiPoint; } - private static Geometry pointTaggedText_(int importFlags, - JSONArray coordinateArray) throws JSONException { + private static Geometry pointTaggedText_(int importFlags, JSONArray coordinateArray) throws JSONException { Point point = new Point(); - int length = coordinateArray.length(); - if (length == 0) { point.setEmpty(); return point; } - - point.setXY(getDouble_(coordinateArray, 0), - getDouble_(coordinateArray, 1)); - + point.setXY(getDouble_(coordinateArray, 0), getDouble_(coordinateArray, 1)); return point; } - private static int multiPolygonText_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, - AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, + private static int multiPolygonText_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, + AttributeStreamOfDbl position, AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, JSONArray coordinateArray) throws JSONException { // At start of MultiPolygonText - int totalPointCount = 0; int length = coordinateArray.length(); - if (length == 0) return totalPointCount; - for (int current = 0; current < length; current++) { JSONArray subArray = coordinateArray.optJSONArray(current); - if (subArray == null) { - // Entry should be a JSONArray representing a polygon, but it is - // not a JSONArray. + if (subArray == null) {// Entry should be a JSONArray representing a + // polygon, but it is not a JSONArray. throw new IllegalArgumentException(""); } // At start of PolygonText - - totalPointCount = polygonText_(zs, ms, position, paths, path_flags, - totalPointCount, subArray); + totalPointCount = polygonText_(zs, ms, position, paths, path_flags, totalPointCount, subArray); } - return totalPointCount; } - private static int multiLineStringText_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, - AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, + private static int multiLineStringText_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, + AttributeStreamOfDbl position, AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, JSONArray coordinateArray) throws JSONException { // At start of MultiLineStringText - int totalPointCount = 0; int length = coordinateArray.length(); - if (length == 0) return totalPointCount; - for (int current = 0; current < length; current++) { JSONArray subArray = coordinateArray.optJSONArray(current); - if (subArray == null) { - // Entry should be a JSONArray representing a line string, but - // it is not a JSONArray. + if (subArray == null) {// Entry should be a JSONArray representing a + // line string, but it is not a JSONArray. throw new IllegalArgumentException(""); } // At start of LineStringText - - totalPointCount += lineStringText_(false, zs, ms, position, paths, - path_flags, subArray); + totalPointCount += lineStringText_(false, zs, ms, position, paths, path_flags, subArray); } - return totalPointCount; } - - private static int multiPointText_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + + private static int multiPointText_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, JSONArray coordinateArray) throws JSONException { // At start of MultiPointText - int pointCount = 0; - for (int current = 0; current < coordinateArray.length(); current++) { JSONArray subArray = coordinateArray.optJSONArray(current); - if (subArray == null) { - // Entry should be a JSONArray representing a point, but it is - // not a JSONArray. + if (subArray == null) {// Entry should be a JSONArray representing a + // point, but it is not a JSONArray. throw new IllegalArgumentException(""); } - pointCount += pointText_(zs, ms, position, subArray); } - return pointCount; } - private static int polygonText_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, - AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, - int totalPointCount, JSONArray coordinateArray) - throws JSONException { + private static int polygonText_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, int totalPointCount, + JSONArray coordinateArray) throws JSONException { // At start of PolygonText - int length = coordinateArray.length(); - if (length == 0) { return totalPointCount; } - boolean bFirstLineString = true; - for (int current = 0; current < length; current++) { JSONArray subArray = coordinateArray.optJSONArray(current); - if (subArray == null) { - // Entry should be a JSONArray representing a line string, but - // it is not a JSONArray. + if (subArray == null) {// Entry should be a JSONArray representing a + // line string, but it is not a JSONArray. throw new IllegalArgumentException(""); } - // At start of LineStringText - - int pointCount = lineStringText_(true, zs, ms, position, paths, - path_flags, subArray); - + int pointCount = lineStringText_(true, zs, ms, position, paths, path_flags, subArray); if (pointCount != 0) { if (bFirstLineString) { bFirstLineString = false; - path_flags.setBits(path_flags.size() - 2, - (byte) PathFlags.enumOGCStartPolygon); + path_flags.setBits(path_flags.size() - 2, (byte) PathFlags.enumOGCStartPolygon); } - - path_flags.setBits(path_flags.size() - 2, - (byte) PathFlags.enumClosed); + path_flags.setBits(path_flags.size() - 2, (byte) PathFlags.enumClosed); totalPointCount += pointCount; } } - return totalPointCount; } - - private static int lineStringText_(boolean bRing, AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, - AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, + + private static int lineStringText_(boolean bRing, AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, + AttributeStreamOfDbl position, AttributeStreamOfInt32 paths, AttributeStreamOfInt8 path_flags, JSONArray coordinateArray) throws JSONException { // At start of LineStringText - int pointCount = 0; int length = coordinateArray.length(); - if (length == 0) return pointCount; - boolean bStartPath = true; double startX = NumberUtils.TheNaN; double startY = NumberUtils.TheNaN; double startZ = NumberUtils.TheNaN; double startM = NumberUtils.TheNaN; - for (int current = 0; current < length; current++) { JSONArray subArray = coordinateArray.optJSONArray(current); - if (subArray == null) { - // Entry should be a JSONArray representing a single point, but - // it is not a JSONArray. + if (subArray == null) {// Entry should be a JSONArray representing a + // single point, but it is not a JSONArray. throw new IllegalArgumentException(""); } - // At start of x - double x = getDouble_(subArray, 0); double y = getDouble_(subArray, 1); double z = NumberUtils.TheNaN; double m = NumberUtils.TheNaN; - boolean bAddPoint = true; - - if (bRing && pointCount >= 2 && current == length - 1) { - // If the last point in the ring is not equal to the start - // point, then let's add it. - - if ((startX == x || (NumberUtils.isNaN(startX) && NumberUtils - .isNaN(x))) - && (startY == y || (NumberUtils.isNaN(startY) && NumberUtils - .isNaN(y)))) { + if (bRing && pointCount >= 2 && current == length - 1) {// If the + // last + // point in + // the ring + // is not + // equal to + // the start + // point, + // then + // let's add + // it. + if ((startX == x || (NumberUtils.isNaN(startX) && NumberUtils.isNaN(x))) + && (startY == y || (NumberUtils.isNaN(startY) && NumberUtils.isNaN(y)))) { bAddPoint = false; } } - if (bAddPoint) { if (bStartPath) { bStartPath = false; @@ -544,72 +1545,54 @@ private static int lineStringText_(boolean bRing, AttributeStreamOfDbl zs, startZ = z; startM = m; } - pointCount++; addToStreams_(zs, ms, position, x, y, z, m); } } - if (pointCount == 1) { pointCount++; addToStreams_(zs, ms, position, startX, startY, startZ, startM); } - paths.add(position.size() / 2); path_flags.add((byte) 0); - return pointCount; } - private static int pointText_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + private static int pointText_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, JSONArray coordinateArray) throws JSONException { // At start of PointText - int length = coordinateArray.length(); - if (length == 0) return 0; - // At start of x - double x = getDouble_(coordinateArray, 0); double y = getDouble_(coordinateArray, 1); double z = NumberUtils.TheNaN; double m = NumberUtils.TheNaN; - addToStreams_(zs, ms, position, x, y, z, m); - return 1; } - private static void addToStreams_(AttributeStreamOfDbl zs, - AttributeStreamOfDbl ms, AttributeStreamOfDbl position, double x, - double y, double z, double m) { + private static void addToStreams_(AttributeStreamOfDbl zs, AttributeStreamOfDbl ms, AttributeStreamOfDbl position, + double x, double y, double z, double m) { position.add(x); position.add(y); - if (zs != null) zs.add(z); - if (ms != null) ms.add(m); } - private static double getDouble_(JSONArray coordinateArray, int index) - throws JSONException { + private static double getDouble_(JSONArray coordinateArray, int index) throws JSONException { if (index < 0 || index >= coordinateArray.length()) { throw new IllegalArgumentException(""); } - if (coordinateArray.isNull(index)) { return NumberUtils.TheNaN; } - if (coordinateArray.optDouble(index, NumberUtils.TheNaN) != NumberUtils.TheNaN) { return coordinateArray.getDouble(index); } - throw new IllegalArgumentException(""); - } + }*/ } diff --git a/src/main/java/com/esri/core/geometry/OperatorImportFromJsonCursor.java b/src/main/java/com/esri/core/geometry/OperatorImportFromJsonCursor.java index 56f79a87..8c267d7a 100644 --- a/src/main/java/com/esri/core/geometry/OperatorImportFromJsonCursor.java +++ b/src/main/java/com/esri/core/geometry/OperatorImportFromJsonCursor.java @@ -271,7 +271,6 @@ static MapGeometry importFromJsonParser(int gt, JsonReader parser) { mp = new MapGeometry(geometry, spatial_reference); } catch (Exception e) { - e.printStackTrace(); return null; } diff --git a/src/main/java/com/esri/core/geometry/OperatorIntersection.java b/src/main/java/com/esri/core/geometry/OperatorIntersection.java index 2f23f11c..ee3b4833 100644 --- a/src/main/java/com/esri/core/geometry/OperatorIntersection.java +++ b/src/main/java/com/esri/core/geometry/OperatorIntersection.java @@ -59,8 +59,8 @@ public abstract GeometryCursor execute(GeometryCursor inputGeometries, *The bitmask of values (1 << dim), where dim is the desired dimension value, is used to indicate *what dimensions of geometry one wants to be returned. For example, to return *multipoints and lines only, pass (1 << 0) | (1 << 1), which is equivalen to 1 | 2, or 3. - *@return Returns the cursor of the intersection result. The cursors' get_geometry_ID method returns the current ID of the input geometry - *being processed. Wh dimensionMask is a bitmask, there will be n result geometries per one input geometry returned, where n is the number + *@return Returns the cursor of the intersection result. The cursors' getGeometryID method returns the current ID of the input geometry + *being processed. When dimensionMask is a bitmask, there will be n result geometries per one input geometry returned, where n is the number *of bits set in the bitmask. For example, if the dimensionMask is 5, there will be two geometries per one input geometry. * *The operator intersects every geometry in the input_geometries with the first geometry of the intersector and returns the result. @@ -68,7 +68,7 @@ public abstract GeometryCursor execute(GeometryCursor inputGeometries, *Note, when the dimensionMask is -1, then for each intersected pair of geometries, *the result has the lower of dimentions of the two geometries. That is, the dimension of the Polyline/Polyline intersection *is always 1 (that is, for polylines it never returns crossing points, but the overlaps only). - *If dimensionMask is 7, the operation will return any possible + *If dimensionMask is 7, the operation will return any possible intersections. */ public abstract GeometryCursor execute(GeometryCursor input_geometries, GeometryCursor intersector, SpatialReference sr, diff --git a/src/main/java/com/esri/core/geometry/OperatorShapePreservingDensify.java b/src/main/java/com/esri/core/geometry/OperatorShapePreservingDensify.java index 3a4a31ce..b25d66e0 100644 --- a/src/main/java/com/esri/core/geometry/OperatorShapePreservingDensify.java +++ b/src/main/java/com/esri/core/geometry/OperatorShapePreservingDensify.java @@ -43,7 +43,7 @@ public Type getType() { * @param maxLengthMeters The maximum segment length allowed. Must be a positive value to be used. Pass zero or NaN to disable densification by length. * @param maxDeviationMeters The maximum deviation. Must be a positive value to be used. Pass zero or NaN to disable densification by deviation. * @param reserved Must be 0 or NaN. Reserved for future use. Throws and exception if not NaN or 0. - * @return Returns the densified geometries (It does nothing to geometries with dim < 1, but simply passes them along). + * @return Returns the densified geometries (It does nothing to geometries with dim less than 1, but simply passes them along). * * The operation always starts from the lowest point on the segment, thus guaranteeing that topologically equal segments are always densified exactly the same. */ @@ -58,7 +58,7 @@ public Type getType() { * @param maxLengthMeters The maximum segment length allowed. Must be a positive value to be used. Pass zero or NaN to disable densification by length. * @param maxDeviationMeters The maximum deviation. Must be a positive value to be used. Pass zero or NaN to disable densification by deviation. * @param reserved Must be 0 or NaN. Reserved for future use. Throws and exception if not NaN or 0. - * @return Returns the densified geometries (It does nothing to geometries with dim < 1, but simply passes them along). + * @return Returns the densified geometries (It does nothing to geometries with dim less than 1, but simply passes them along). * * The operation always starts from the lowest point on the segment, thus guaranteeing that topologically equal segments are always densified exactly the same. */ diff --git a/src/main/java/com/esri/core/geometry/Point.java b/src/main/java/com/esri/core/geometry/Point.java index 0f553257..2343c5df 100644 --- a/src/main/java/com/esri/core/geometry/Point.java +++ b/src/main/java/com/esri/core/geometry/Point.java @@ -33,10 +33,9 @@ * location in a two-dimensional XY-Plane. In case of Geographic Coordinate * Systems, the X coordinate is the longitude and the Y is the latitude. */ -public final class Point extends Geometry implements Serializable { - private static final long serialVersionUID = 2L;// TODO:remove as we use - // writeReplace and - // GeometrySerializer +public class Point extends Geometry implements Serializable { + //We are using writeReplace instead. + //private static final long serialVersionUID = 2L; double[] m_attributes; // use doubles to store everything (long are bitcast) @@ -47,7 +46,7 @@ public Point() { m_description = VertexDescriptionDesignerImpl.getDefaultDescriptor2D(); } - Point(VertexDescription vd) { + public Point(VertexDescription vd) { if (vd == null) throw new IllegalArgumentException(); m_description = vd; @@ -129,7 +128,7 @@ public final void setXY(Point2D pt) { /** * Returns XYZ coordinates of the point. Z will be set to 0 if Z is missing. */ - Point3D getXYZ() { + public Point3D getXYZ() { if (isEmptyImpl()) throw new GeometryException( "This operation should not be performed on an empty geometry."); @@ -151,7 +150,7 @@ Point3D getXYZ() { * @param pt * The point to create the XYZ coordinate from. */ - void setXYZ(Point3D pt) { + public void setXYZ(Point3D pt) { _touch(); boolean bHasZ = hasAttribute(Semantics.Z); if (!bHasZ && !VertexDescription.isDefaultValue(Semantics.Z, pt.z)) {// add @@ -388,7 +387,7 @@ protected void _assignVertexDescriptionImpl(VertexDescription newDescription) { int[] mapping = VertexDescriptionDesignerImpl.mapAttributes(newDescription, m_description); - double[] newAttributes = new double[newDescription._getTotalComponents()]; + double[] newAttributes = new double[newDescription.getTotalComponentCount()]; int j = 0; for (int i = 0, n = newDescription.getAttributeCount(); i < n; i++) { @@ -424,9 +423,9 @@ protected void _assignVertexDescriptionImpl(VertexDescription newDescription) { * Sets the Point to a default, non-empty state. */ void _setToDefault() { - resizeAttributes(m_description._getTotalComponents()); + resizeAttributes(m_description.getTotalComponentCount()); Point.attributeCopy(m_description._getDefaultPointAttributes(), - m_attributes, m_description._getTotalComponents()); + m_attributes, m_description.getTotalComponentCount()); m_attributes[0] = NumberUtils.NaN(); m_attributes[1] = NumberUtils.NaN(); } @@ -465,9 +464,9 @@ public void copyTo(Geometry dst) { pointDst.assignVertexDescription(m_description); } else { pointDst.assignVertexDescription(m_description); - pointDst.resizeAttributes(m_description._getTotalComponents()); + pointDst.resizeAttributes(m_description.getTotalComponentCount()); attributeCopy(m_attributes, pointDst.m_attributes, - m_description._getTotalComponents()); + m_description.getTotalComponentCount()); } } @@ -595,7 +594,7 @@ public boolean equals(Object _other) { else return false; - for (int i = 0, n = m_description._getTotalComponents(); i < n; i++) + for (int i = 0, n = m_description.getTotalComponentCount(); i < n; i++) if (m_attributes[i] != otherPt.m_attributes[i]) return false; @@ -610,7 +609,7 @@ public boolean equals(Object _other) { public int hashCode() { int hashCode = m_description.hashCode(); if (!isEmptyImpl()) { - for (int i = 0, n = m_description._getTotalComponents(); i < n; i++) { + for (int i = 0, n = m_description.getTotalComponentCount(); i < n; i++) { long bits = Double.doubleToLongBits(m_attributes[i]); int hc = (int) (bits ^ (bits >>> 32)); hashCode = NumberUtils.hash(hashCode, hc); diff --git a/src/main/java/com/esri/core/geometry/Point2D.java b/src/main/java/com/esri/core/geometry/Point2D.java index f9fa71ab..245b8156 100644 --- a/src/main/java/com/esri/core/geometry/Point2D.java +++ b/src/main/java/com/esri/core/geometry/Point2D.java @@ -48,6 +48,10 @@ public Point2D(double x, double y) { this.y = y; } + public Point2D(Point2D other) { + setCoords(other); + } + public static Point2D construct(double x, double y) { return new Point2D(x, y); } @@ -122,20 +126,29 @@ public void negate(Point2D other) { } public void interpolate(Point2D other, double alpha) { - x = x * (1.0 - alpha) + other.x * alpha; - y = y * (1.0 - alpha) + other.y * alpha; + MathUtils.lerp(this, other, alpha, this); } public void interpolate(Point2D p1, Point2D p2, double alpha) { - x = p1.x * (1.0 - alpha) + p2.x * alpha; - y = p1.y * (1.0 - alpha) + p2.y * alpha; + MathUtils.lerp(p1, p2, alpha, this); } - + + /** + * Calculates this = this * f + shift + * @param f + * @param shift + */ public void scaleAdd(double f, Point2D shift) { x = x * f + shift.x; y = y * f + shift.y; } + /** + * Calculates this = other * f + shift + * @param f + * @param other + * @param shift + */ public void scaleAdd(double f, Point2D other, Point2D shift) { x = other.x * f + shift.x; y = other.y * f + shift.y; @@ -152,12 +165,19 @@ public void scale(double f) { } /** - * Compares two vertices lexicographicaly. + * Compares two vertices lexicographically by y. */ public int compare(Point2D other) { return y < other.y ? -1 : (y > other.y ? 1 : (x < other.x ? -1 : (x > other.x ? 1 : 0))); } + /** + * Compares two vertices lexicographically by x. + */ + int compareX(Point2D other) { + return x < other.x ? -1 : (x > other.x ? 1 : (y < other.y ? -1 + : (y > other.y ? 1 : 0))); + } public void normalize(Point2D other) { double len = other.length(); @@ -266,7 +286,7 @@ void _setNan() { } boolean _isNan() { - return NumberUtils.isNaN(x); + return NumberUtils.isNaN(x) || NumberUtils.isNaN(y); } // calculates which quarter of xy plane the vector lies in. First quater is @@ -475,9 +495,269 @@ public static int orientationRobust(Point2D p, Point2D q, Point2D r) { return det_mp.signum(); } + private static int inCircleRobustMP_(Point2D p, Point2D q, Point2D r, Point2D s) { + BigDecimal sx_mp = new BigDecimal(s.x), sy_mp = new BigDecimal(s.y); + + BigDecimal psx_mp = new BigDecimal(p.x), psy_mp = new BigDecimal(p.y); + psx_mp = psx_mp.subtract(sx_mp); + psy_mp = psy_mp.subtract(sy_mp); + + BigDecimal qsx_mp = new BigDecimal(q.x), qsy_mp = new BigDecimal(q.y); + qsx_mp = qsx_mp.subtract(sx_mp); + qsy_mp = qsy_mp.subtract(sy_mp); + + BigDecimal rsx_mp = new BigDecimal(r.x), rsy_mp = new BigDecimal(r.y); + rsx_mp = rsx_mp.subtract(sx_mp); + rsy_mp = rsy_mp.subtract(sy_mp); + + BigDecimal pq_det_mp = psx_mp.multiply(qsy_mp).subtract(psy_mp.multiply(qsx_mp)); + BigDecimal qr_det_mp = qsx_mp.multiply(rsy_mp).subtract(qsy_mp.multiply(rsx_mp)); + BigDecimal pr_det_mp = psx_mp.multiply(rsy_mp).subtract(psy_mp.multiply(rsx_mp)); + + BigDecimal p_parab_mp = psx_mp.multiply(psx_mp).add(psy_mp.multiply(psy_mp)); + BigDecimal q_parab_mp = qsx_mp.multiply(qsx_mp).add(qsy_mp.multiply(qsy_mp)); + BigDecimal r_parab_mp = rsx_mp.multiply(rsx_mp).add(rsy_mp.multiply(rsy_mp)); + + BigDecimal det_mp = (p_parab_mp.multiply(qr_det_mp).subtract(q_parab_mp.multiply(pr_det_mp))) + .add(r_parab_mp.multiply(pq_det_mp)); + + return det_mp.signum(); + } + + /** + * Calculates if the point s is inside of the circumcircle inscribed by the clockwise oriented triangle p-q-r. + * Returns 1 for outside, -1 for inside, and 0 for cocircular. + * Note that the convention used here differs from what is commonly found in literature, which can define the relation + * in terms of a counter-clockwise oriented circle, and this flips the sign (think of the signed volume of the tetrahedron). + * May use high precision arithmetics for some special cases. + */ + static int inCircleRobust(Point2D p, Point2D q, Point2D r, Point2D s) { + ECoordinate psx_ec = new ECoordinate(), psy_ec = new ECoordinate(); + psx_ec.set(p.x); + psx_ec.sub(s.x); + psy_ec.set(p.y); + psy_ec.sub(s.y); + + ECoordinate qsx_ec = new ECoordinate(), qsy_ec = new ECoordinate(); + qsx_ec.set(q.x); + qsx_ec.sub(s.x); + qsy_ec.set(q.y); + qsy_ec.sub(s.y); + + ECoordinate rsx_ec = new ECoordinate(), rsy_ec = new ECoordinate(); + rsx_ec.set(r.x); + rsx_ec.sub(s.x); + rsy_ec.set(r.y); + rsy_ec.sub(s.y); + + ECoordinate psx_ec_qsy_ec = new ECoordinate(); + psx_ec_qsy_ec.set(psx_ec); + psx_ec_qsy_ec.mul(qsy_ec); + ECoordinate psy_ec_qsx_ec = new ECoordinate(); + psy_ec_qsx_ec.set(psy_ec); + psy_ec_qsx_ec.mul(qsx_ec); + ECoordinate qsx_ec_rsy_ec = new ECoordinate(); + qsx_ec_rsy_ec.set(qsx_ec); + qsx_ec_rsy_ec.mul(rsy_ec); + ECoordinate qsy_ec_rsx_ec = new ECoordinate(); + qsy_ec_rsx_ec.set(qsy_ec); + qsy_ec_rsx_ec.mul(rsx_ec); + ECoordinate psx_ec_rsy_ec = new ECoordinate(); + psx_ec_rsy_ec.set(psx_ec); + psx_ec_rsy_ec.mul(rsy_ec); + ECoordinate psy_ec_rsx_ec = new ECoordinate(); + psy_ec_rsx_ec.set(psy_ec); + psy_ec_rsx_ec.mul(rsx_ec); + + ECoordinate pq_det_ec = new ECoordinate(); + pq_det_ec.set(psx_ec_qsy_ec); + pq_det_ec.sub(psy_ec_qsx_ec); + ECoordinate qr_det_ec = new ECoordinate(); + qr_det_ec.set(qsx_ec_rsy_ec); + qr_det_ec.sub(qsy_ec_rsx_ec); + ECoordinate pr_det_ec = new ECoordinate(); + pr_det_ec.set(psx_ec_rsy_ec); + pr_det_ec.sub(psy_ec_rsx_ec); + + ECoordinate psx_ec_psx_ec = new ECoordinate(); + psx_ec_psx_ec.set(psx_ec); + psx_ec_psx_ec.mul(psx_ec); + ECoordinate psy_ec_psy_ec = new ECoordinate(); + psy_ec_psy_ec.set(psy_ec); + psy_ec_psy_ec.mul(psy_ec); + ECoordinate qsx_ec_qsx_ec = new ECoordinate(); + qsx_ec_qsx_ec.set(qsx_ec); + qsx_ec_qsx_ec.mul(qsx_ec); + ECoordinate qsy_ec_qsy_ec = new ECoordinate(); + qsy_ec_qsy_ec.set(qsy_ec); + qsy_ec_qsy_ec.mul(qsy_ec); + ECoordinate rsx_ec_rsx_ec = new ECoordinate(); + rsx_ec_rsx_ec.set(rsx_ec); + rsx_ec_rsx_ec.mul(rsx_ec); + ECoordinate rsy_ec_rsy_ec = new ECoordinate(); + rsy_ec_rsy_ec.set(rsy_ec); + rsy_ec_rsy_ec.mul(rsy_ec); + + ECoordinate p_parab_ec = new ECoordinate(); + p_parab_ec.set(psx_ec_psx_ec); + p_parab_ec.add(psy_ec_psy_ec); + ECoordinate q_parab_ec = new ECoordinate(); + q_parab_ec.set(qsx_ec_qsx_ec); + q_parab_ec.add(qsy_ec_qsy_ec); + ECoordinate r_parab_ec = new ECoordinate(); + r_parab_ec.set(rsx_ec_rsx_ec); + r_parab_ec.add(rsy_ec_rsy_ec); + + p_parab_ec.mul(qr_det_ec); + q_parab_ec.mul(pr_det_ec); + r_parab_ec.mul(pq_det_ec); + + ECoordinate det_ec = new ECoordinate(); + det_ec.set(p_parab_ec); + det_ec.sub(q_parab_ec); + det_ec.add(r_parab_ec); + + if (!det_ec.isFuzzyZero()) { + double det_ec_value = det_ec.value(); + + if (det_ec_value < 0.0) + return -1; + + if (det_ec_value > 0.0) + return 1; + + return 0; + } + + return inCircleRobustMP_(p, q, r, s); + } + + private static Point2D calculateCenterFromThreePointsHelperMP_(Point2D from, Point2D mid_point, Point2D to) { + assert(!mid_point.isEqual(to) && !mid_point.isEqual(from) && !from.isEqual(to)); + BigDecimal mx = new BigDecimal(mid_point.x); + mx = mx.subtract(new BigDecimal(from.x)); + BigDecimal my = new BigDecimal(mid_point.y); + my = my.subtract(new BigDecimal(from.y)); + BigDecimal tx = new BigDecimal(to.x); + tx = tx.subtract(new BigDecimal(from.x)); + BigDecimal ty = new BigDecimal(to.y); + ty = ty.subtract(new BigDecimal(from.y)); + + BigDecimal d = mx.multiply(ty); + BigDecimal tmp = my.multiply(tx); + d = d.subtract(tmp); + + if (d.signum() == 0) { + return Point2D.construct(NumberUtils.NaN(), NumberUtils.NaN()); + } + + d = d.multiply(new BigDecimal(2.0)); + + BigDecimal mx2 = mx.multiply(mx); + BigDecimal my2 = my.multiply(my); + BigDecimal m_norm2 = mx2.add(my2); + BigDecimal tx2 = tx.multiply(tx); + BigDecimal ty2 = ty.multiply(ty); + BigDecimal t_norm2 = tx2.add(ty2); + + BigDecimal xo = my.multiply(t_norm2); + tmp = ty.multiply(m_norm2); + xo = xo.subtract(tmp); + xo = xo.divide(d, BigDecimal.ROUND_HALF_EVEN); + + BigDecimal yo = mx.multiply(t_norm2); + tmp = tx.multiply(m_norm2); + yo = yo.subtract(tmp); + yo = yo.divide(d, BigDecimal.ROUND_HALF_EVEN); + + Point2D center = Point2D.construct(from.x - xo.doubleValue(), from.y + yo.doubleValue()); + return center; + } + + private static Point2D calculateCenterFromThreePointsHelper_(Point2D from, Point2D mid_point, Point2D to) { + assert(!mid_point.isEqual(to) && !mid_point.isEqual(from) && !from.isEqual(to)); + ECoordinate mx = new ECoordinate(mid_point.x); + mx.sub(from.x); + ECoordinate my = new ECoordinate(mid_point.y); + my.sub(from.y); + ECoordinate tx = new ECoordinate(to.x); + tx.sub(from.x); + ECoordinate ty = new ECoordinate(to.y); + ty.sub(from.y); + + ECoordinate d = new ECoordinate(mx); + d.mul(ty); + ECoordinate tmp = new ECoordinate(my); + tmp.mul(tx); + d.sub(tmp); + + if (d.value() == 0.0) { + return Point2D.construct(NumberUtils.NaN(), NumberUtils.NaN()); + } + + d.mul(2.0); + + ECoordinate mx2 = new ECoordinate(mx); + mx2.mul(mx); + ECoordinate my2 = new ECoordinate(my); + my2.mul(my); + ECoordinate m_norm2 = new ECoordinate(mx2); + m_norm2.add(my2); + ECoordinate tx2 = new ECoordinate(tx); + tx2.mul(tx); + ECoordinate ty2 = new ECoordinate(ty); + ty2.mul(ty); + ECoordinate t_norm2 = new ECoordinate(tx2); + t_norm2.add(ty2); + + ECoordinate xo = new ECoordinate(my); + xo.mul(t_norm2); + tmp = new ECoordinate(ty); + tmp.mul(m_norm2); + xo.sub(tmp); + xo.div(d); + + ECoordinate yo = new ECoordinate(mx); + yo.mul(t_norm2); + tmp = new ECoordinate(tx); + tmp.mul(m_norm2); + yo.sub(tmp); + yo.div(d); + + Point2D center = Point2D.construct(from.x - xo.value(), from.y + yo.value()); + double r1 = Point2D.construct(from.x - center.x, from.y - center.y).length(); + double r2 = Point2D.construct(mid_point.x - center.x, mid_point.y - center.y).length(); + double r3 = Point2D.construct(to.x - center.x, to.y - center.y).length(); + double base = r1 + Math.abs(from.x) + Math.abs(mid_point.x) + Math.abs(to.x) + Math.abs(from.y) + + Math.abs(mid_point.y) + Math.abs(to.y); + + double tol = 1e-15; + if ((Math.abs(r1 - r2) <= base * tol && Math.abs(r1 - r3) <= base * tol)) + return center;//returns center value for MP_value type or when calculated radius value for from - center, mid - center, and to - center are very close. + + return Point2D.construct(NumberUtils.NaN(), NumberUtils.NaN()); + } + + static Point2D calculateCircleCenterFromThreePoints(Point2D from, Point2D mid_point, Point2D to) { + if (from.isEqual(to) || from.isEqual(mid_point) || to.isEqual(mid_point)) { + return new Point2D(NumberUtils.NaN(), NumberUtils.NaN()); + } + + Point2D pt = calculateCenterFromThreePointsHelper_(from, mid_point, to); //use error tracking calculations + if (pt.isNaN()) + return calculateCenterFromThreePointsHelperMP_(from, mid_point, to); //use precise calculations + else { + return pt; + } + } + @Override public int hashCode() { return NumberUtils.hash(NumberUtils.hash(x), y); } + double getAxis(int ordinate) { + assert(ordinate == 0 || ordinate == 1); + return (ordinate == 0 ? x : y); + } } diff --git a/src/main/java/com/esri/core/geometry/Point3D.java b/src/main/java/com/esri/core/geometry/Point3D.java index a8316ff3..849b00e1 100644 --- a/src/main/java/com/esri/core/geometry/Point3D.java +++ b/src/main/java/com/esri/core/geometry/Point3D.java @@ -42,11 +42,17 @@ public final class Point3D implements Serializable { public Point3D() { } + public Point3D(Point3D other) { + setCoords(other); + } + + public Point3D(double x, double y, double z) { + setCoords(x, y, z); + } + public static Point3D construct(double x, double y, double z) { Point3D pt = new Point3D(); - pt.x = x; - pt.y = y; - pt.z = z; + pt.setCoords(x, y, z); return pt; } @@ -56,6 +62,10 @@ public void setCoords(double x, double y, double z) { this.z = z; } + public void setCoords(Point3D other) { + setCoords(other.x, other.y, other.z); + } + public void setZero() { x = 0.0; y = 0.0; @@ -64,38 +74,62 @@ public void setZero() { public void normalize() { double len = length(); - if (len != 0) - return; + if (len == 0) { + x = 1.0; + y = 0.0; + z = 0.0; + } else { + x /= len; + y /= len; + z /= len; + } + } - x /= len; - y /= len; - z /= len; + public double dotProduct(Point3D other) { + return x * other.x + y * other.y + z * other.z; + } + + public double sqrLength() { + return x * x + y * y + z * z; } public double length() { return Math.sqrt(x * x + y * y + z * z); } - public Point3D(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; + public void sub(Point3D other) + { + x -= other.x; + y -= other.y; + z -= other.z; + } + + public void sub(Point3D p1, Point3D p2) { + x = p1.x - p2.x; + y = p1.y - p2.y; + z = p1.z - p2.z; } - public Point3D sub(Point3D other) { - return new Point3D(x - other.x, y - other.y, z - other.z); + public void scale(double f, Point3D other) { + x = f * other.x; + y = f * other.y; + z = f * other.z; } - public Point3D mul(double factor) { - return new Point3D(x * factor, y * factor, z * factor); + public void mul(double factor) { + x *= factor; + y *= factor; + z *= factor; } void _setNan() { x = NumberUtils.NaN(); + y = NumberUtils.NaN(); + z = NumberUtils.NaN(); } boolean _isNan() { - return NumberUtils.isNaN(x); + return NumberUtils.isNaN(x) || NumberUtils.isNaN(y) || NumberUtils.isNaN(z); } } diff --git a/src/main/java/com/esri/core/geometry/Polygon.java b/src/main/java/com/esri/core/geometry/Polygon.java index 6d2a97f4..cb027357 100644 --- a/src/main/java/com/esri/core/geometry/Polygon.java +++ b/src/main/java/com/esri/core/geometry/Polygon.java @@ -30,7 +30,7 @@ /** * A polygon is a collection of one or many interior or exterior rings. */ -public final class Polygon extends MultiPath implements Serializable { +public class Polygon extends MultiPath implements Serializable { private static final long serialVersionUID = 2L;// TODO:remove as we use // writeReplace and @@ -43,7 +43,7 @@ public Polygon() { m_impl = new MultiPathImpl(true); } - Polygon(VertexDescription vd) { + public Polygon(VertexDescription vd) { m_impl = new MultiPathImpl(true, vd); } diff --git a/src/main/java/com/esri/core/geometry/PolygonUtils.java b/src/main/java/com/esri/core/geometry/PolygonUtils.java index 26701d30..d56f2220 100644 --- a/src/main/java/com/esri/core/geometry/PolygonUtils.java +++ b/src/main/java/com/esri/core/geometry/PolygonUtils.java @@ -26,7 +26,7 @@ final class PolygonUtils { - enum PiPResult { + public enum PiPResult { PiPOutside, PiPInside, PiPBoundary }; @@ -34,7 +34,7 @@ enum PiPResult { /** * Tests if Point is inside the Polygon. Returns PiPOutside if not in * polygon, PiPInside if in the polygon, PiPBoundary is if on the border. It - * tests border only if the tolerance is > 0, otherwise PiPBoundary cannot + * tests border only if the tolerance is greater than 0, otherwise PiPBoundary cannot * be returned. Note: If the tolerance is not 0, the test is more expensive * because it calculates closest distance from a point to each segment. * @@ -79,7 +79,7 @@ static PiPResult isPointInPolygon2D(Polygon polygon, double inputPointXVal, /** * Tests if Point is inside the Polygon's ring. Returns PiPOutside if not in * ring, PiPInside if in the ring, PiPBoundary is if on the border. It tests - * border only if the tolerance is > 0, otherwise PiPBoundary cannot be + * border only if the tolerance is greater than 0, otherwise PiPBoundary cannot be * returned. Note: If the tolerance is not 0, the test is more expensive * because it calculates closest distance from a point to each segment. * @@ -127,33 +127,10 @@ public static PiPResult isPointInAnyOuterRing(Polygon polygon, // internal and external boundaries. } - // #ifndef DOTNET - // /** - // *Tests point is inside the Polygon for an array of points. - // *Returns PiPOutside if not in polygon, PiPInside if in the polygon, - // PiPBoundary is if on the border. - // *It tests border only if the tolerance is > 0, otherwise PiPBoundary - // cannot be returned. - // *Note: If the tolerance is not 0, the test is more expensive. - // * - // *O(n*m) complexity, where n is the number of polygon segments, m is the - // number of input points. - // */ - // static void TestPointsInPolygon2D(Polygon polygon, const Point2D* - // inputPoints, int count, double tolerance, PiPResult testResults) - // { - // LOCALREFCLASS2(Array, Point2D*, int, inputPointsArr, - // const_cast(inputPoints), count); - // LOCALREFCLASS2(Array, PolygonUtils::PiPResult*, - // int, testResultsArr, testResults, count); - // TestPointsInPolygon2D(polygon, inputPointsArr, count, tolerance, - // testResultsArr); - // } - // #endif /** * Tests point is inside the Polygon for an array of points. Returns * PiPOutside if not in polygon, PiPInside if in the polygon, PiPBoundary is - * if on the border. It tests border only if the tolerance is > 0, otherwise + * if on the border. It tests border only if the tolerance is greater than 0, otherwise * PiPBoundary cannot be returned. Note: If the tolerance is not 0, the test * is more expensive. * @@ -182,31 +159,11 @@ static void testPointsInPolygon2D(Polygon polygon, double[] xyStreamBuffer, xyStreamBuffer[i * 2 + 1], tolerance); } - // public static void testPointsInPolygon2D(Polygon polygon, Geometry geom, - // int count, double tolerance, PiPResult[] testResults) - // { - // if(geom.getType() == Type.Point) - // { - // - // } - // else if(Geometry.isMultiVertex(geom.getType())) - // { - // - // } - // - // - // if (inputPoints.length < count || testResults.length < count) - // throw new IllegalArgumentException();//GEOMTHROW(invalid_argument); - // - // for (int i = 0; i < count; i++) - // testResults[i] = isPointInPolygon2D(polygon, inputPoints[i], tolerance); - // } - /** * Tests point is inside an Area Geometry (Envelope, Polygon) for an array * of points. Returns PiPOutside if not in area, PiPInside if in the area, * PiPBoundary is if on the border. It tests border only if the tolerance is - * > 0, otherwise PiPBoundary cannot be returned. Note: If the tolerance is + * greater than 0, otherwise PiPBoundary cannot be returned. Note: If the tolerance is * not 0, the test is more expensive. * * O(n*m) complexity, where n is the number of polygon segments, m is the diff --git a/src/main/java/com/esri/core/geometry/Polyline.java b/src/main/java/com/esri/core/geometry/Polyline.java index b95e9f81..4c83a147 100644 --- a/src/main/java/com/esri/core/geometry/Polyline.java +++ b/src/main/java/com/esri/core/geometry/Polyline.java @@ -31,7 +31,7 @@ * A polyline is a collection of one or many paths. * */ -public final class Polyline extends MultiPath implements Serializable { +public class Polyline extends MultiPath implements Serializable { private static final long serialVersionUID = 2L;// TODO:remove as we use // writeReplace and @@ -44,7 +44,7 @@ public Polyline() { m_impl = new MultiPathImpl(false); } - Polyline(VertexDescription vd) { + public Polyline(VertexDescription vd) { m_impl = new MultiPathImpl(false, vd); } diff --git a/src/main/java/com/esri/core/geometry/PolylinePath.java b/src/main/java/com/esri/core/geometry/PolylinePath.java deleted file mode 100644 index 6d3342da..00000000 --- a/src/main/java/com/esri/core/geometry/PolylinePath.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 1995-2015 Esri - - 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. - - For additional information, contact: - Environmental Systems Research Institute, Inc. - Attn: Contracts Dept - 380 New York Street - Redlands, California, USA 92373 - - email: contracts@esri.com - */ - -package com.esri.core.geometry; - -import java.util.Comparator; - -class PolylinePath { - Point2D m_fromPoint; - Point2D m_toPoint; - double m_fromDist; // from lower left corner; -1.0 if point is not on - // clipping bounday - double m_toDist; // from lower left corner; -1.0 if point is not on clipping - // bounday - int m_path; // from polyline - boolean m_used; - - public PolylinePath() { - } - - public PolylinePath(Point2D fromPoint, Point2D toPoint, double fromDist, - double toDist, int path) { - m_fromPoint = fromPoint; - m_toPoint = toPoint; - m_fromDist = fromDist; - m_toDist = toDist; - m_path = path; - m_used = false; - } - - void setValues(Point2D fromPoint, Point2D toPoint, double fromDist, - double toDist, int path) { - m_fromPoint = fromPoint; - m_toPoint = toPoint; - m_fromDist = fromDist; - m_toDist = toDist; - m_path = path; - m_used = false; - } - - // to be used in Use SORTARRAY - -} - -class PolylinePathComparator implements Comparator { - @Override - public int compare(PolylinePath v1, PolylinePath v2) { - if ((v1).m_fromDist < (v2).m_fromDist) - return -1; - else if ((v1).m_fromDist > (v2).m_fromDist) - return 1; - else - return 0; - } - -} diff --git a/src/main/java/com/esri/core/geometry/PtSrlzr.java b/src/main/java/com/esri/core/geometry/PtSrlzr.java new file mode 100644 index 00000000..68dd1aae --- /dev/null +++ b/src/main/java/com/esri/core/geometry/PtSrlzr.java @@ -0,0 +1,88 @@ +/* + Copyright 1995-2015 Esri + + 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. + + For additional information, contact: + Environmental Systems Research Institute, Inc. + Attn: Contracts Dept + 380 New York Street + Redlands, California, USA 92373 + + email: contracts@esri.com + */ +package com.esri.core.geometry; + +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +//This is a writeReplace class for Point +public class PtSrlzr implements Serializable { + private static final long serialVersionUID = 1L; + double[] attribs; + int descriptionBitMask; + + public Object readResolve() throws ObjectStreamException { + Point point = null; + try { + VertexDescription vd = VertexDescriptionDesignerImpl + .getVertexDescription(descriptionBitMask); + point = new Point(vd); + if (attribs != null) { + point.setXY(attribs[0], attribs[1]); + int index = 2; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + point.setAttribute(semantics, ord, attribs[index++]); + } + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot read geometry from stream"); + } + + return point; + } + + public void setGeometryByValue(Point point) throws ObjectStreamException { + try { + attribs = null; + if (point == null) { + descriptionBitMask = 1; + } + + VertexDescription vd = point.getDescription(); + descriptionBitMask = vd.m_semanticsBitArray; + if (point.isEmpty()) { + return; + } + + attribs = new double[vd.getTotalComponentCount()]; + attribs[0] = point.getX(); + attribs[1] = point.getY(); + int index = 2; + for (int i = 1, n = vd.getAttributeCount(); i < n; i++) { + int semantics = vd.getSemantics(i); + int comps = VertexDescription.getComponentCount(semantics); + for (int ord = 0; ord < comps; ord++) { + attribs[index++] = point.getAttributeAsDbl(semantics, ord); + } + } + } catch (Exception ex) { + throw new InvalidObjectException("Cannot serialize this geometry"); + } + } +} diff --git a/src/main/java/com/esri/core/geometry/QuadTree.java b/src/main/java/com/esri/core/geometry/QuadTree.java index 9f6be163..32d81c3c 100644 --- a/src/main/java/com/esri/core/geometry/QuadTree.java +++ b/src/main/java/com/esri/core/geometry/QuadTree.java @@ -28,24 +28,31 @@ public class QuadTree { public static final class QuadTreeIterator { /** - * Resets the iterator to an starting state on the Quad_tree. If the + * Resets the iterator to an starting state on the QuadTree. If the * input Geometry is a Line segment, then the query will be the segment. - * Otherwise the query will be the Envelope_2D bounding the Geometry. - * \param query The Geometry used for the query. \param tolerance The - * tolerance used for the intersection tests. + * Otherwise the query will be the Envelope2D bounding the Geometry. + * \param query The Geometry used for the query. + * \param tolerance The tolerance used for the intersection tests. */ public void resetIterator(Geometry query, double tolerance) { - m_impl.resetIterator(query, tolerance); + if (!m_b_sorted) + ((QuadTreeImpl.QuadTreeIteratorImpl) m_impl).resetIterator(query, tolerance); + else + ((QuadTreeImpl.QuadTreeSortedIteratorImpl) m_impl).resetIterator(query, tolerance); } /** - * Resets the iterator to a starting state on the Quad_tree using the - * input Envelope_2D as the query. \param query The Envelope_2D used for - * the query. \param tolerance The tolerance used for the intersection + * Resets the iterator to a starting state on the QuadTree using the + * input Envelope2D as the query. + * \param query The Envelope2D used for the query. + * \param tolerance The tolerance used for the intersection * tests. */ public void resetIterator(Envelope2D query, double tolerance) { - m_impl.resetIterator(query, tolerance); + if (!m_b_sorted) + ((QuadTreeImpl.QuadTreeIteratorImpl) m_impl).resetIterator(query, tolerance); + else + ((QuadTreeImpl.QuadTreeSortedIteratorImpl) m_impl).resetIterator(query, tolerance); } /** @@ -53,7 +60,10 @@ public void resetIterator(Envelope2D query, double tolerance) { * Element_handle. */ public int next() { - return m_impl.next(); + if (!m_b_sorted) + return ((QuadTreeImpl.QuadTreeIteratorImpl) m_impl).next(); + else + return ((QuadTreeImpl.QuadTreeSortedIteratorImpl) m_impl).next(); } /** @@ -63,136 +73,260 @@ Object getImpl_() { return m_impl; } - // Creates an iterator on the input Quad_tree_impl. The query will be - // the Envelope_2D bounding the input Geometry. - private QuadTreeIterator(Object obj) { - m_impl = (QuadTreeImpl.QuadTreeIteratorImpl) obj; + // Creates an iterator on the input QuadTreeImpl. The query will be + // the Envelope2D bounding the input Geometry. + private QuadTreeIterator(Object obj, boolean bSorted) { + + m_impl = obj; + m_b_sorted = bSorted; } - private QuadTreeImpl.QuadTreeIteratorImpl m_impl; - }; + private Object m_impl; + private boolean m_b_sorted; + } /** - * Creates a Quad_tree with the root having the extent of the input - * Envelope_2D, and height of the input height, where the root starts at - * height 0. Note that the height cannot be larger than 16 if on a 32 bit - * platform and 32 if on a 64 bit platform. \param extent The extent of the - * Quad_tree. \param height The max height of the Quad_tree. + * Creates a QuadTree with the root having the extent of the input + * Envelope2D, and height of the input height, where the root starts at height 0. + * \param extent The extent of the QuadTree. + * \param height The max height of the QuadTree. */ public QuadTree(Envelope2D extent, int height) { m_impl = new QuadTreeImpl(extent, height); } /** - * Inserts the element and bounding_box into the Quad_tree. Note that a copy + * Creates a QuadTree with the root having the extent of the input Envelope2D, and height of the input height, where the root starts at height 0. + * \param extent The extent of the QuadTreeImpl. + * \param height The max height of the QuadTreeImpl. + * \param bStoreDuplicates Put true to place elements deeper into the quad tree at intesecting quads, duplicates will be stored. Put false to only place elements into quads that can contain it.. + */ + public QuadTree(Envelope2D extent, int height, boolean bStoreDuplicates) { + m_impl = new QuadTreeImpl(extent, height, bStoreDuplicates); + } + + /** + * Inserts the element and bounding_box into the QuadTree. Note that a copy * will me made of the input bounding_box. Note that this will invalidate - * any active iterator on the Quad_tree. Returns an Element_handle - * corresponding to the element and bounding_box. \param element The element - * of the Geometry to be inserted. \param bounding_box The bounding_box of + * any active iterator on the QuadTree. Returns an Element_handle + * corresponding to the element and bounding_box. + * \param element The element of the Geometry to be inserted. + * \param bounding_box The bounding_box of * the Geometry to be inserted. */ - public int insert(int element, Envelope2D bounding_box) { - return m_impl.insert(element, bounding_box); + public int insert(int element, Envelope2D boundingBox) { + return m_impl.insert(element, boundingBox); } /** - * Inserts the element and bounding_box into the Quad_tree at the given + * Inserts the element and bounding_box into the QuadTree at the given * quad_handle. Note that a copy will me made of the input bounding_box. - * Note that this will invalidate any active iterator on the Quad_tree. + * Note that this will invalidate any active iterator on the QuadTree. * Returns an Element_handle corresponding to the element and bounding_box. - * \param element The element of the Geometry to be inserted. \param - * bounding_box The bounding_box of the Geometry to be inserted. \param - * hint_index A handle used as a hint where to place the element. This can + * \param element The element of the Geometry to be inserted. + * \param bounding_box The bounding_box of the Geometry to be inserted. + * \param hint_index A handle used as a hint where to place the element. This can * be a handle obtained from a previous insertion and is useful on data * having strong locality such as segments of a Polygon. */ - public int insert(int element, Envelope2D bounding_box, int hint_index) { - return m_impl.insert(element, bounding_box, hint_index); + public int insert(int element, Envelope2D boundingBox, int hintIndex) { + return m_impl.insert(element, boundingBox, hintIndex); } /** * Removes the element and bounding_box at the given element_handle. Note - * that this will invalidate any active iterator on the Quad_tree. \param - * element_handle The handle corresponding to the element and bounding_box + * that this will invalidate any active iterator on the QuadTree. + * \param element_handle The handle corresponding to the element and bounding_box * to be removed. */ - public void removeElement(int element_handle) { - m_impl.removeElement(element_handle); + public void removeElement(int elementHandle) { + m_impl.removeElement(elementHandle); + } + + /** + * Returns the element at the given element_handle. + * \param element_handle The handle corresponding to the element to be retrieved. + */ + public int getElement(int elementHandle) { + return m_impl.getElement(elementHandle); + } + + /** + * Returns the element extent at the given element_handle. + * \param element_handle The handle corresponding to the element extent to be retrieved. + */ + public Envelope2D getElementExtent(int elementHandle) { + return m_impl.getElementExtent(elementHandle); + } + + /** + * Returns the extent of all elements in the quad tree. + */ + public Envelope2D getDataExtent() { + return m_impl.getDataExtent(); + } + + /** + * Returns the extent of the quad tree. + */ + public Envelope2D getQuadTreeExtent() { + return m_impl.getQuadTreeExtent(); + } + + /** + * Returns the number of elements in the subtree rooted at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. + */ + public int getSubTreeElementCount(int quadHandle) { + return m_impl.getSubTreeElementCount(quadHandle); + } + + /** + * Returns the number of elements contained in the subtree rooted at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. + */ + public int getContainedSubTreeElementCount(int quadHandle) { + return m_impl.getContainedSubTreeElementCount(quadHandle); + } + + /** + * Returns the number of elements in the quad tree that intersect the qiven query. Some elements may be duplicated if the quad tree stores duplicates. + * \param query The Envelope2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + * \param max_count If the intersection count becomes greater than or equal to the max_count, then max_count is returned. + */ + public int getIntersectionCount(Envelope2D query, double tolerance, int maxCount) { + return m_impl.getIntersectionCount(query, tolerance, maxCount); } /** - * Returns the element at the given element_handle. \param element_handle - * The handle corresponding to the element to be retrieved. + * Returns true if the quad tree has data intersecting the given query. + * \param query The Envelope2D used for the query. + * \param tolerance The tolerance used for the intersection tests. */ - public int getElement(int element_handle) { - return m_impl.getElement(element_handle); + public boolean hasData(Envelope2D query, double tolerance) { + return m_impl.hasData(query, tolerance); } /** * Returns the height of the quad at the given quad_handle. \param * quad_handle The handle corresponding to the quad. */ - public int getHeight(int quad_handle) { - return m_impl.getHeight(quad_handle); + public int getHeight(int quadHandle) { + return m_impl.getHeight(quadHandle); } /** - * Returns the extent of the quad at the given quad_handle. \param - * quad_handle The handle corresponding to the quad. + * Returns the max height the quad tree can grow to. */ - public Envelope2D getExtent(int quad_handle) { - return m_impl.getExtent(quad_handle); + public int getMaxHeight() { + return m_impl.getMaxHeight(); + } + + /** + * Returns the extent of the quad at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. + */ + public Envelope2D getExtent(int quadHandle) { + return m_impl.getExtent(quadHandle); } /** * Returns the Quad_handle of the quad containing the given element_handle. * \param element_handle The handle corresponding to the element. */ - public int getQuad(int element_handle) { - return m_impl.getQuad(element_handle); + public int getQuad(int elementHandle) { + return m_impl.getQuad(elementHandle); } /** - * Returns the number of elements in the Quad_tree. + * Returns the number of elements in the QuadTree. */ public int getElementCount() { return m_impl.getElementCount(); } /** - * Gets an iterator on the Quad_tree. The query will be the Envelope_2D that + * Gets an iterator on the QuadTree. The query will be the Envelope2D that * bounds the input Geometry. To reuse the existing iterator on the same - * Quad_tree but with a new query, use the reset_iterator function on the - * Quad_tree_iterator. \param query The Geometry used for the query. If the + * QuadTree but with a new query, use the reset_iterator function on the + * QuadTree_iterator. + * \param query The Geometry used for the query. If the * Geometry is a Line segment, then the query will be the segment. Otherwise - * the query will be the Envelope_2D bounding the Geometry. \param tolerance - * The tolerance used for the intersection tests. + * the query will be the Envelope2D bounding the Geometry. + * \param tolerance The tolerance used for the intersection tests. */ public QuadTreeIterator getIterator(Geometry query, double tolerance) { - QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, - tolerance); - return new QuadTreeIterator(iterator); + QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, tolerance); + return new QuadTreeIterator(iterator, false); } /** - * Gets an iterator on the Quad_tree using the input Envelope_2D as the - * query. To reuse the existing iterator on the same Quad_tree but with a - * new query, use the reset_iterator function on the Quad_tree_iterator. - * \param query The Envelope_2D used for the query. \param tolerance The - * tolerance used for the intersection tests. + * Gets an iterator on the QuadTree using the input Envelope2D as the + * query. To reuse the existing iterator on the same QuadTree but with a + * new query, use the reset_iterator function on the QuadTree_iterator. + * \param query The Envelope2D used for the query. + * \param tolerance The tolerance used for the intersection tests. */ public QuadTreeIterator getIterator(Envelope2D query, double tolerance) { - QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, - tolerance); - return new QuadTreeIterator(iterator); + QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, tolerance); + return new QuadTreeIterator(iterator, false); } /** - * Gets an iterator on the Quad_tree. + * Gets an iterator on the QuadTree. */ public QuadTreeIterator getIterator() { QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(); - return new QuadTreeIterator(iterator); + return new QuadTreeIterator(iterator, false); + } + + /** + * Gets an iterator on the QuadTree. The query will be the Envelope2D that bounds the input Geometry. + * To reuse the existing iterator on the same QuadTree but with a new query, use the reset_iterator function on the QuadTree_iterator. + * \param query The Geometry used for the query. If the Geometry is a Line segment, then the query will be the segment. Otherwise the query will be the Envelope2D bounding the Geometry. + * \param tolerance The tolerance used for the intersection tests. + * \param bSorted Put true to iterate the quad tree in the order of the Element_types. + */ + public QuadTreeIterator getIterator(Geometry query, double tolerance, boolean bSorted) { + if (!bSorted) { + QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, tolerance); + return new QuadTreeIterator(iterator, false); + } else { + QuadTreeImpl.QuadTreeSortedIteratorImpl iterator = m_impl.getSortedIterator(query, tolerance); + return new QuadTreeIterator(iterator, true); + } + } + + /** + * Gets an iterator on the QuadTree using the input Envelope2D as the query. + * To reuse the existing iterator on the same QuadTree but with a new query, use the reset_iterator function on the QuadTree_iterator. + * \param query The Envelope2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + * \param bSorted Put true to iterate the quad tree in the order of the Element_types. + */ + public QuadTreeIterator getIterator(Envelope2D query, double tolerance, boolean bSorted) { + if (!bSorted) { + QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(query, tolerance); + return new QuadTreeIterator(iterator, false); + } else { + QuadTreeImpl.QuadTreeSortedIteratorImpl iterator = m_impl.getSortedIterator(query, tolerance); + return new QuadTreeIterator(iterator, true); + } + } + + /** + * Gets an iterator on the QuadTree. + * \param bSorted Put true to iterate the quad tree in the order of the Element_types. + */ + public QuadTreeIterator getIterator(boolean bSorted) { + if (!bSorted) { + QuadTreeImpl.QuadTreeIteratorImpl iterator = m_impl.getIterator(); + return new QuadTreeIterator(iterator, false); + } else { + QuadTreeImpl.QuadTreeSortedIteratorImpl iterator = m_impl.getSortedIterator(); + return new QuadTreeIterator(iterator, true); + } } /** diff --git a/src/main/java/com/esri/core/geometry/QuadTreeImpl.java b/src/main/java/com/esri/core/geometry/QuadTreeImpl.java index cbef824b..fe48999a 100644 --- a/src/main/java/com/esri/core/geometry/QuadTreeImpl.java +++ b/src/main/java/com/esri/core/geometry/QuadTreeImpl.java @@ -42,7 +42,7 @@ void resetIterator(Geometry query, double tolerance) { query.queryLooseEnvelope2D(m_query_box); m_query_box.inflate(tolerance, tolerance); - if (m_query_box.isIntersecting(m_quad_tree.m_extent)) { + if (m_quad_tree.m_root != -1 && m_query_box.isIntersecting(m_quad_tree.m_extent)) { int type = query.getType().value(); m_b_linear = Geometry.isSegment(type); @@ -57,8 +57,7 @@ void resetIterator(Geometry query, double tolerance) { m_quads_stack.add(m_quad_tree.m_root); m_extents_stack.add(m_quad_tree.m_extent); - m_next_element_handle = m_quad_tree - .getFirstElement_(m_quad_tree.m_root); + m_next_element_handle = m_quad_tree.get_first_element_(m_quad_tree.m_root); } else m_next_element_handle = -1; } @@ -77,11 +76,10 @@ void resetIterator(Envelope2D query, double tolerance) { m_query_box.inflate(tolerance, tolerance); m_tolerance = NumberUtils.NaN(); // we don't need it - if (m_query_box.isIntersecting(m_quad_tree.m_extent)) { + if (m_quad_tree.m_root != -1 && m_query_box.isIntersecting(m_quad_tree.m_extent)) { m_quads_stack.add(m_quad_tree.m_root); m_extents_stack.add(m_quad_tree.m_extent); - m_next_element_handle = m_quad_tree - .getFirstElement_(m_quad_tree.m_root); + m_next_element_handle = m_quad_tree.get_first_element_(m_quad_tree.m_root); m_b_linear = false; } else m_next_element_handle = -1; @@ -104,7 +102,7 @@ int next() { Envelope2D extent_inf = null; Envelope2D[] child_extents = null; - if (m_b_linear) {// Should this memory be cached for reuse? + if (m_b_linear) { start = new Point2D(); end = new Point2D(); extent_inf = new Envelope2D(); @@ -113,10 +111,8 @@ int next() { boolean b_found_hit = false; while (!b_found_hit) { while (m_current_element_handle != -1) { - int current_box_handle = m_quad_tree - .getBoxHandle_(m_current_element_handle); - bounding_box = m_quad_tree - .getBoundingBox_(current_box_handle); + int current_data_handle = m_quad_tree.get_data_(m_current_element_handle); + bounding_box = m_quad_tree.get_bounding_box_value_(current_data_handle); if (bounding_box.isIntersecting(m_query_box)) { if (m_b_linear) { @@ -136,21 +132,14 @@ int next() { } // get next element_handle - m_current_element_handle = m_quad_tree - .getNextElement_(m_current_element_handle); + m_current_element_handle = m_quad_tree.get_next_element_(m_current_element_handle); } - // If m_current_element_handle equals -1, then we've exhausted - // our search in the current quadtree node + // If m_current_element_handle equals -1, then we've exhausted our search in the current quadtree node if (m_current_element_handle == -1) { - // get the last node from the stack and add the children - // whose extent intersects m_query_box + // get the last node from the stack and add the children whose extent intersects m_query_box int current_quad = m_quads_stack.getLast(); - Envelope2D current_extent = m_extents_stack - .get(m_extents_stack.size() - 1); - - double x_mid = 0.5 * (current_extent.xmin + current_extent.xmax); - double y_mid = 0.5 * (current_extent.ymin + current_extent.ymax); + Envelope2D current_extent = m_extents_stack.get(m_extents_stack.size() - 1); if (child_extents == null) { child_extents = new Envelope2D[4]; @@ -160,38 +149,30 @@ int next() { child_extents[3] = new Envelope2D(); } - setChildExtents_(current_extent, child_extents); + set_child_extents_(current_extent, child_extents); m_quads_stack.removeLast(); m_extents_stack.remove(m_extents_stack.size() - 1); for (int quadrant = 0; quadrant < 4; quadrant++) { - int child_handle = m_quad_tree.getChild_(current_quad, - quadrant); - - if (child_handle != -1 - && m_quad_tree - .getSubTreeElementCount(child_handle) > 0) { - if (child_extents[quadrant] - .isIntersecting(m_query_box)) { + int child_handle = m_quad_tree.get_child_(current_quad, quadrant); + + if (child_handle != -1 && m_quad_tree.getSubTreeElementCount(child_handle) > 0) { + if (child_extents[quadrant].isIntersecting(m_query_box)) { if (m_b_linear) { start.setCoords(m_query_start); end.setCoords(m_query_end); - extent_inf - .setCoords(child_extents[quadrant]); - extent_inf - .inflate(m_tolerance, m_tolerance); + extent_inf.setCoords(child_extents[quadrant]); + extent_inf.inflate(m_tolerance, m_tolerance); if (extent_inf.clipLine(start, end) > 0) { Envelope2D child_extent = new Envelope2D(); - child_extent - .setCoords(child_extents[quadrant]); + child_extent.setCoords(child_extents[quadrant]); m_quads_stack.add(child_handle); m_extents_stack.add(child_extent); } } else { Envelope2D child_extent = new Envelope2D(); - child_extent - .setCoords(child_extents[quadrant]); + child_extent.setCoords(child_extents[quadrant]); m_quads_stack.add(child_handle); m_extents_stack.add(child_extent); } @@ -204,24 +185,20 @@ int next() { if (m_quads_stack.size() == 0) return -1; - m_current_element_handle = m_quad_tree - .getFirstElement_(m_quads_stack.get(m_quads_stack - .size() - 1)); + m_current_element_handle = m_quad_tree.get_first_element_(m_quads_stack.get(m_quads_stack.size() - 1)); } } // We did not exhaust our search in the current node, so we return // the element at m_current_element_handle in m_element_nodes - m_next_element_handle = m_quad_tree - .getNextElement_(m_current_element_handle); + m_next_element_handle = m_quad_tree.get_next_element_(m_current_element_handle); return m_current_element_handle; } // Creates an iterator on the input Quad_tree_impl. The query will be // the Envelope_2D bounding the input Geometry. - QuadTreeIteratorImpl(QuadTreeImpl quad_tree_impl, Geometry query, - double tolerance) { + QuadTreeIteratorImpl(QuadTreeImpl quad_tree_impl, Geometry query, double tolerance) { m_quad_tree = quad_tree_impl; m_query_box = new Envelope2D(); m_quads_stack = new AttributeStreamOfInt32(0); @@ -231,8 +208,7 @@ int next() { // Creates an iterator on the input Quad_tree_impl using the input // Envelope_2D as the query. - QuadTreeIteratorImpl(QuadTreeImpl quad_tree_impl, Envelope2D query, - double tolerance) { + QuadTreeIteratorImpl(QuadTreeImpl quad_tree_impl, Envelope2D query, double tolerance) { m_quad_tree = quad_tree_impl; m_query_box = new Envelope2D(); m_quads_stack = new AttributeStreamOfInt32(0); @@ -257,147 +233,346 @@ int next() { private int m_next_element_handle; private QuadTreeImpl m_quad_tree; private AttributeStreamOfInt32 m_quads_stack; - private ArrayList m_extents_stack; // this won't grow bigger - // than 4 * - // (m_quad_tree->m_height - // - 1) + private ArrayList m_extents_stack; // this won't grow bigger than 4 * (m_quad_tree->m_height - 1) + } + + static final class QuadTreeSortedIteratorImpl { + /** + * Resets the iterator to a starting state on the Quad_tree_impl. If the input Geometry is a Line segment, then the query will be the segment. Otherwise the query will be the Envelope_2D bounding the Geometry. + * \param query The Geometry used for the query. + * \param tolerance The tolerance used for the intersection tests. + * \param tolerance The tolerance used for the intersection tests. + */ + void resetIterator(Geometry query, double tolerance) { + m_quad_tree_iterator_impl.resetIterator(query, tolerance); + m_sorted_handles.resize(0); + m_index = -1; + } + + /** + * Resets the iterator to a starting state on the Quad_tree_impl using the input Envelope_2D as the query. + * \param query The Envelope_2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + */ + void resetIterator(Envelope2D query, double tolerance) { + m_quad_tree_iterator_impl.resetIterator(query, tolerance); + m_sorted_handles.resize(0); + m_index = -1; + } + + /** + * Moves the iterator to the next Element_handle and returns the Element_handle. + */ + int next() { + if (m_index == -1) { + int element_handle = -1; + while ((element_handle = m_quad_tree_iterator_impl.next()) != -1) + m_sorted_handles.add(element_handle); + + m_bucket_sort.sort(m_sorted_handles, 0, m_sorted_handles.size(), new Sorter(m_quad_tree_iterator_impl.m_quad_tree)); + } + + if (m_index == m_sorted_handles.size() - 1) + return -1; + + m_index++; + return m_sorted_handles.get(m_index); + } + + //Creates a sorted iterator on the input Quad_tree_iterator_impl + QuadTreeSortedIteratorImpl(QuadTreeIteratorImpl quad_tree_iterator_impl) { + m_bucket_sort = new BucketSort(); + m_sorted_handles = new AttributeStreamOfInt32(0); + m_quad_tree_iterator_impl = quad_tree_iterator_impl; + m_index = -1; + } + + private class Sorter extends ClassicSort { + public Sorter(QuadTreeImpl quad_tree) { + m_quad_tree = quad_tree; + } + + @Override + public void userSort(int begin, int end, AttributeStreamOfInt32 indices) { + indices.sort(begin, end); + } + + @Override + public double getValue(int e) { + return m_quad_tree.getElement(e); + } + + private QuadTreeImpl m_quad_tree; + } + + private BucketSort m_bucket_sort; + private AttributeStreamOfInt32 m_sorted_handles; + private QuadTreeIteratorImpl m_quad_tree_iterator_impl; + int m_index; } /** - * Creates a Quad_tree_impl with the root having the extent of the input - * Envelope_2D, and height of the input height, where the root starts at - * height 0. Note that the height cannot be larger than 16 if on a 32 bit - * platform and 32 if on a 64 bit platform. \param extent The extent of the - * Quad_tree_impl. \param height The max height of the Quad_tree_impl. + * Creates a Quad_tree_impl with the root having the extent of the input Envelope_2D, and height of the input height, where the root starts at height 0. + * \param extent The extent of the Quad_tree_impl. + * \param height The max height of the Quad_tree_impl. */ QuadTreeImpl(Envelope2D extent, int height) { - m_quad_tree_nodes = new StridedIndexTypeCollection(11); - m_element_nodes = new StridedIndexTypeCollection(5); - m_boxes = new ArrayList(0); - m_free_boxes = new AttributeStreamOfInt32(0); + m_quad_tree_nodes = new StridedIndexTypeCollection(10); + m_element_nodes = new StridedIndexTypeCollection(4); + m_data = new ArrayList(0); + m_free_data = new AttributeStreamOfInt32(0); + m_b_store_duplicates = false; + + m_extent = new Envelope2D(); + m_data_extent = new Envelope2D(); + + reset_(extent, height); + } + + /** + * Creates a Quad_tree_impl with the root having the extent of the input Envelope_2D, and height of the input height, where the root starts at height 0. + * \param extent The extent of the Quad_tree_impl. + * \param height The max height of the Quad_tree_impl. + * \param b_store_duplicates Put true to place elements deeper into the quad tree at intesecting quads, duplicates will be stored. Put false to only place elements into quads that can contain it. + */ + QuadTreeImpl(Envelope2D extent, int height, boolean b_store_duplicates) { + m_quad_tree_nodes = (b_store_duplicates ? new StridedIndexTypeCollection(11) : new StridedIndexTypeCollection(10)); + m_element_nodes = new StridedIndexTypeCollection(4); + m_data = new ArrayList(0); + m_free_data = new AttributeStreamOfInt32(0); + m_b_store_duplicates = b_store_duplicates; + m_extent = new Envelope2D(); + m_data_extent = new Envelope2D(); + reset_(extent, height); } /** - * Resets the Quad_tree_impl to the given extent and height. \param extent - * The extent of the Quad_tree_impl. \param height The max height of the - * Quad_tree_impl. + * Resets the Quad_tree_impl to the given extent and height. + * \param extent The extent of the Quad_tree_impl. + * \param height The max height of the Quad_tree_impl. */ void reset(Envelope2D extent, int height) { m_quad_tree_nodes.deleteAll(false); m_element_nodes.deleteAll(false); - m_boxes.clear(); - m_free_boxes.clear(false); + m_data.clear(); + m_free_data.clear(false); reset_(extent, height); } /** - * Inserts the element and bounding_box into the Quad_tree_impl. Note that - * this will invalidate any active iterator on the Quad_tree_impl. Returns - * an int corresponding to the element and bounding_box. \param element The - * element of the Geometry to be inserted. \param bounding_box The - * bounding_box of the Geometry to be inserted. + * Inserts the element and bounding_box into the Quad_tree_impl. + * Note that this will invalidate any active iterator on the Quad_tree_impl. + * Returns an Element_handle corresponding to the element and bounding_box. + * \param element The element of the Geometry to be inserted. + * \param bounding_box The bounding_box of the Geometry to be inserted. */ int insert(int element, Envelope2D bounding_box) { - return insert_(element, bounding_box, 0, m_extent, m_root, false, -1); + if (m_root == -1) + create_root_(); + + if (m_b_store_duplicates) { + int success = insert_duplicates_(element, bounding_box, 0, m_extent, m_root, false, -1); + + if (success != -1) { + if (m_data_extent.isEmpty()) + m_data_extent.setCoords(bounding_box); + else + m_data_extent.merge(bounding_box); + } + + return success; + } + + int element_handle = insert_(element, bounding_box, 0, m_extent, m_root, false, -1); + + if (element_handle != -1) { + if (m_data_extent.isEmpty()) + m_data_extent.setCoords(bounding_box); + else + m_data_extent.merge(bounding_box); + } + + return element_handle; } /** - * Inserts the element and bounding_box into the Quad_tree_impl at the given - * quad_handle. Note that this will invalidate any active iterator on the - * Quad_tree_impl. Returns an int corresponding to the element and - * bounding_box. \param element The element of the Geometry to be inserted. + * Inserts the element and bounding_box into the Quad_tree_impl at the given quad_handle. + * Note that this will invalidate any active iterator on the Quad_tree_impl. + * Returns an Element_handle corresponding to the element and bounding_box. + * \param element The element of the Geometry to be inserted. * \param bounding_box The bounding_box of the Geometry to be inserted. - * \param hint_index A handle used as a hint where to place the element. - * This can be a handle obtained from a previous insertion and is useful on - * data having strong locality such as segments of a Polygon. + * \param hint_index A handle used as a hint where to place the element. This can be a handle obtained from a previous insertion and is useful on data having strong locality such as segments of a Polygon. */ int insert(int element, Envelope2D bounding_box, int hint_index) { + if (m_root == -1) + create_root_(); + + if (m_b_store_duplicates) { + int success = insert_duplicates_(element, bounding_box, 0, m_extent, m_root, false, -1); + + if (success != -1) { + if (m_data_extent.isEmpty()) + m_data_extent.setCoords(bounding_box); + else + m_data_extent.merge(bounding_box); + } + return success; + } + int quad_handle; if (hint_index == -1) quad_handle = m_root; else - quad_handle = getQuad_(hint_index); + quad_handle = get_quad_(hint_index); int quad_height = getHeight(quad_handle); Envelope2D quad_extent = getExtent(quad_handle); - return insert_(element, bounding_box, quad_height, quad_extent, - quad_handle, false, -1); + + int element_handle = insert_(element, bounding_box, quad_height, quad_extent, quad_handle, false, -1); + + if (element_handle != -1) { + if (m_data_extent.isEmpty()) + m_data_extent.setCoords(bounding_box); + else + m_data_extent.merge(bounding_box); + } + + return element_handle; } /** - * Removes the element and bounding_box at the given element_handle. Note - * that this will invalidate any active iterator on the Quad_tree_impl. - * \param element_handle The handle corresponding to the element and - * bounding_box to be removed. + * Removes the element and bounding_box at the given element_handle. + * Note that this will invalidate any active iterator on the Quad_tree_impl. + * \param element_handle The handle corresponding to the element and bounding_box to be removed. */ void removeElement(int element_handle) { - int quad_handle = getQuad_(element_handle); - int nextElementHandle = disconnectElementHandle_(element_handle); - freeElementAndBoxNode_(element_handle); + if (m_b_store_duplicates) + throw new GeometryException("invalid call"); + + int quad_handle = get_quad_(element_handle); + disconnect_element_handle_(element_handle); + free_element_and_box_node_(element_handle); + + int q = quad_handle; + + while (q != -1) { + set_sub_tree_element_count_(q, get_sub_tree_element_count_(q) - 1); + int parent = get_parent_(q); + + if (get_sub_tree_element_count_(q) == 0) { + assert (get_local_element_count_(q) == 0); + + if (q != m_root) { + int quadrant = get_quadrant_(q); + m_quad_tree_nodes.deleteElement(q); + set_child_(parent, quadrant, -1); + } + } - for (int q = quad_handle; q != -1; q = getParent_(q)) { - setSubTreeElementCount_(q, getSubTreeElementCount_(q) - 1); - assert (getSubTreeElementCount_(q) >= 0); + q = parent; } } /** * Returns the element at the given element_handle. - * \param element_handle - * The handle corresponding to the element to be retrieved. + * \param element_handle The handle corresponding to the element to be retrieved. */ int getElement(int element_handle) { - return getElement_(element_handle); + return get_element_value_(get_data_(element_handle)); } + /** + * Returns the ith unique element. + * \param i The index corresponding to the ith unique element. + */ + int getElementAtIndex(int i) { + return m_data.get(i).element; + } - /** - * Returns a reference to the element extent at the given element_handle. - * \param element_handle - * The handle corresponding to the element to be retrieved. - */ - Envelope2D getElementExtent(int element_handle) - { - int box_handle = getBoxHandle_(element_handle); - return getBoundingBox_(box_handle); - } + /** + * Returns the element extent at the given element_handle. + * \param element_handle The handle corresponding to the element extent to be retrieved. + */ + Envelope2D getElementExtent(int element_handle) { + int data_handle = get_data_(element_handle); + return get_bounding_box_value_(data_handle); + } /** - * Returns the height of the quad at the given quad_handle. \param - * quad_handle The handle corresponding to the quad. + * Returns the extent of the ith unique element. + * \param i The index corresponding to the ith unique element. + */ + Envelope2D getElementExtentAtIndex(int i) { + return m_data.get(i).box; + } + + /** + * Returns the extent of all elements in the quad tree. + */ + Envelope2D getDataExtent() { + return m_data_extent; + } + + /** + * Returns the extent of the quad tree. + */ + Envelope2D getQuadTreeExtent() { + return m_extent; + } + + /** + * Returns the height of the quad at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. */ int getHeight(int quad_handle) { - return getHeight_(quad_handle); + return get_height_(quad_handle); + } + + int getMaxHeight() { + return m_height; } /** - * Returns the extent of the quad at the given quad_handle. \param - * quad_handle The handle corresponding to the quad. + * Returns the extent of the quad at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. */ Envelope2D getExtent(int quad_handle) { Envelope2D quad_extent = new Envelope2D(); quad_extent.setCoords(m_extent); - int height = getHeight_(quad_handle); - int morten_number = getMortenNumber_(quad_handle); - int mask = 3; + if (quad_handle == m_root) + return quad_extent; + + AttributeStreamOfInt32 quadrants = new AttributeStreamOfInt32(0); - for (int i = 0; i < 2 * height; i += 2) { - int child = (int) (mask & (morten_number >> i)); + int q = quad_handle; - if (child == 0) {// northeast + do { + quadrants.add(get_quadrant_(q)); + q = get_parent_(q); + + } while (q != m_root); + + int sz = quadrants.size(); + assert (sz == getHeight(quad_handle)); + + for (int i = 0; i < sz; i++) { + int child = quadrants.getLast(); + quadrants.removeLast(); + + if (child == 0) {//northeast quad_extent.xmin = 0.5 * (quad_extent.xmin + quad_extent.xmax); quad_extent.ymin = 0.5 * (quad_extent.ymin + quad_extent.ymax); - } else if (child == 1) {// northwest + } else if (child == 1) {//northwest quad_extent.xmax = 0.5 * (quad_extent.xmin + quad_extent.xmax); quad_extent.ymin = 0.5 * (quad_extent.ymin + quad_extent.ymax); - } else if (child == 2) {// southwest + } else if (child == 2) {//southwest quad_extent.xmax = 0.5 * (quad_extent.xmin + quad_extent.xmax); quad_extent.ymax = 0.5 * (quad_extent.ymin + quad_extent.ymax); - } else {// southeast + } else {//southeast quad_extent.xmin = 0.5 * (quad_extent.xmin + quad_extent.xmax); quad_extent.ymax = 0.5 * (quad_extent.ymin + quad_extent.ymax); } @@ -407,26 +582,134 @@ Envelope2D getExtent(int quad_handle) { } /** - * Returns the int of the quad containing the given element_handle. \param - * element_handle The handle corresponding to the element. + * Returns the Quad_handle of the quad containing the given element_handle. + * \param element_handle The handle corresponding to the element. */ int getQuad(int element_handle) { - return getQuad_(element_handle); + return get_quad_(element_handle); } /** * Returns the number of elements in the Quad_tree_impl. */ int getElementCount() { - return getSubTreeElementCount_(m_root); + if (m_root == -1) + return 0; + + assert (get_sub_tree_element_count_(m_root) == m_data.size()); + return get_sub_tree_element_count_(m_root); } /** - * Returns the number of elements in the subtree rooted at the given - * quad_handle. \param quad_handle The handle corresponding to the quad. + * Returns the number of elements in the subtree rooted at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. */ int getSubTreeElementCount(int quad_handle) { - return getSubTreeElementCount_(quad_handle); + return get_sub_tree_element_count_(quad_handle); + } + + /** + * Returns the number of elements contained in the subtree rooted at the given quad_handle. + * \param quad_handle The handle corresponding to the quad. + */ + int getContainedSubTreeElementCount(int quad_handle) { + if (!m_b_store_duplicates) + return get_sub_tree_element_count_(quad_handle); + + return get_contained_sub_tree_element_count_(quad_handle); + } + + /** + * Returns the number of elements in the quad tree that intersect the qiven query. Some elements may be duplicated if the quad tree stores duplicates. + * \param query The Envelope_2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + * \param max_count If the intersection count becomes greater than or equal to the max_count, then max_count is returned. + */ + int getIntersectionCount(Envelope2D query, double tolerance, int max_count) { + if (m_root == -1) + return 0; + + Envelope2D query_inflated = new Envelope2D(); + query_inflated.setCoords(query); + query_inflated.inflate(tolerance, tolerance); + + AttributeStreamOfInt32 quads_stack = new AttributeStreamOfInt32(0); + ArrayList extents_stack = new ArrayList(0); + quads_stack.add(m_root); + extents_stack.add(new Envelope2D(m_extent.xmin, m_extent.ymin, m_extent.xmax, m_extent.ymax)); + + Envelope2D[] child_extents = new Envelope2D[4]; + child_extents[0] = new Envelope2D(); + child_extents[1] = new Envelope2D(); + child_extents[2] = new Envelope2D(); + child_extents[3] = new Envelope2D(); + + Envelope2D current_extent = new Envelope2D(); + + int intersection_count = 0; + + while (quads_stack.size() > 0) { + boolean b_subdivide = false; + + int current_quad_handle = quads_stack.getLast(); + current_extent.setCoords(extents_stack.get(extents_stack.size() - 1)); + + quads_stack.removeLast(); + extents_stack.remove(extents_stack.size() - 1); + + + if (query_inflated.contains(current_extent)) { + intersection_count += getSubTreeElementCount(current_quad_handle); + + if (max_count > 0 && intersection_count >= max_count) + return max_count; + } else { + if (query_inflated.isIntersecting(current_extent)) { + for (int element_handle = get_first_element_(current_quad_handle); element_handle != -1; element_handle = get_next_element_(element_handle)) { + int data_handle = get_data_(element_handle); + Envelope2D env = get_bounding_box_value_(data_handle); + + if (env.isIntersecting(query_inflated)) { + intersection_count++; + + if (max_count > 0 && intersection_count >= max_count) + return max_count; + } + } + + b_subdivide = getHeight(current_quad_handle) + 1 <= m_height; + } + } + + if (b_subdivide) { + set_child_extents_(current_extent, child_extents); + + for (int i = 0; i < 4; i++) { + int child_handle = get_child_(current_quad_handle, i); + + if (child_handle != -1 && getSubTreeElementCount(child_handle) > 0) { + boolean b_is_intersecting = query_inflated.isIntersecting(child_extents[i]); + + if (b_is_intersecting) { + quads_stack.add(child_handle); + extents_stack.add(new Envelope2D(child_extents[i].xmin, child_extents[i].ymin, child_extents[i].xmax, child_extents[i].ymax)); + } + } + } + } + } + + return intersection_count; + } + + /** + * Returns true if the quad tree has data intersecting the given query. + * \param query The Envelope_2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + */ + boolean hasData(Envelope2D query, double tolerance) { + int count = getIntersectionCount(query, tolerance, 1); + return count >= 1; } /** @@ -460,37 +743,58 @@ QuadTreeIteratorImpl getIterator() { return new QuadTreeIteratorImpl(this); } + /** + * Gets a sorted iterator on the Quad_tree_impl. The Element_handles will be returned in increasing order of their corresponding Element_types. + * The query will be the Envelope_2D that bounds the input Geometry. + * To reuse the existing iterator on the same Quad_tree_impl but with a new query, use the reset_iterator function on the Quad_tree_sorted_iterator_impl. + * \param query The Geometry used for the query. If the Geometry is a Line segment, then the query will be the segment. Otherwise the query will be the Envelope_2D bounding the Geometry. + * \param tolerance The tolerance used for the intersection tests. + */ + QuadTreeSortedIteratorImpl getSortedIterator(Geometry query, double tolerance) { + return new QuadTreeSortedIteratorImpl(getIterator(query, tolerance)); + } + + /** + * Gets a sorted iterator on the Quad_tree_impl using the input Envelope_2D as the query. The Element_handles will be returned in increasing order of their corresponding Element_types. + * To reuse the existing iterator on the same Quad_tree_impl but with a new query, use the reset_iterator function on the Quad_tree_iterator_impl. + * \param query The Envelope_2D used for the query. + * \param tolerance The tolerance used for the intersection tests. + */ + QuadTreeSortedIteratorImpl getSortedIterator(Envelope2D query, double tolerance) { + return new QuadTreeSortedIteratorImpl(getIterator(query, tolerance)); + } + + /** + * Gets a sorted iterator on the Quad_tree. The Element_handles will be returned in increasing order of their corresponding Element_types + */ + QuadTreeSortedIteratorImpl getSortedIterator() { + return new QuadTreeSortedIteratorImpl(getIterator()); + } + private void reset_(Envelope2D extent, int height) { - // We need 2 * height bits for the morten number, which is of type - // Index_type (more than enough). - if (height < 0 || 2 * height > 8 * 4) + if (height < 0 || height > 127) throw new IllegalArgumentException("invalid height"); m_height = height; m_extent.setCoords(extent); m_root = m_quad_tree_nodes.newElement(); - setSubTreeElementCount_(m_root, 0); - setLocalElementCount_(m_root, 0); - setMortenNumber_(m_root, 0); - setHeight_(m_root, 0); + m_data_extent.setEmpty(); + m_root = -1; } - private int insert_(int element, Envelope2D bounding_box, int height, - Envelope2D quad_extent, int quad_handle, boolean b_flushing, - int flushed_element_handle) { + private int insert_(int element, Envelope2D bounding_box, int height, Envelope2D quad_extent, int quad_handle, boolean b_flushing, int flushed_element_handle) { if (!quad_extent.contains(bounding_box)) { assert (!b_flushing); if (height == 0) return -1; - return insert_(element, bounding_box, 0, m_extent, m_root, - b_flushing, flushed_element_handle); + return insert_(element, bounding_box, 0, m_extent, m_root, b_flushing, flushed_element_handle); } if (!b_flushing) { - for (int q = quad_handle; q != -1; q = getParent_(q)) - setSubTreeElementCount_(q, getSubTreeElementCount_(q) + 1); + for (int q = quad_handle; q != -1; q = get_parent_(q)) + set_sub_tree_element_count_(q, get_sub_tree_element_count_(q) + 1); } Envelope2D current_extent = new Envelope2D(); @@ -505,9 +809,8 @@ private int insert_(int element, Envelope2D bounding_box, int height, child_extents[3] = new Envelope2D(); int current_height; - for (current_height = height; current_height < m_height - && canPushDown_(current_quad_handle); current_height++) { - setChildExtents_(current_extent, child_extents); + for (current_height = height; current_height < m_height && can_push_down_(current_quad_handle); current_height++) { + set_child_extents_(current_extent, child_extents); boolean b_contains = false; @@ -515,12 +818,11 @@ private int insert_(int element, Envelope2D bounding_box, int height, if (child_extents[i].contains(bounding_box)) { b_contains = true; - int child_handle = getChild_(current_quad_handle, i); + int child_handle = get_child_(current_quad_handle, i); if (child_handle == -1) - child_handle = createChild_(current_quad_handle, i); + child_handle = create_child_(current_quad_handle, i); - setSubTreeElementCount_(child_handle, - getSubTreeElementCount_(child_handle) + 1); + set_sub_tree_element_count_(child_handle, get_sub_tree_element_count_(child_handle) + 1); current_quad_handle = child_handle; current_extent.setCoords(child_extents[i]); @@ -532,22 +834,112 @@ private int insert_(int element, Envelope2D bounding_box, int height, break; } - return insertAtQuad_(element, bounding_box, current_height, - current_extent, current_quad_handle, b_flushing, quad_handle, - flushed_element_handle); + return insert_at_quad_(element, bounding_box, current_height, current_extent, current_quad_handle, b_flushing, quad_handle, flushed_element_handle, -1); } - private int insertAtQuad_(int element, Envelope2D bounding_box, - int current_height, Envelope2D current_extent, - int current_quad_handle, boolean b_flushing, int quad_handle, - int flushed_element_handle) { - // If the bounding box is not contained in any of the current_node's - // children, or if the current_height is m_height, then insert the - // element and + private int insert_duplicates_(int element, Envelope2D bounding_box, int height, Envelope2D quad_extent, int quad_handle, boolean b_flushing, int flushed_element_handle) { + assert (b_flushing || m_root == quad_handle); + + if (!b_flushing) // If b_flushing is true, then the sub tree element counts are already accounted for since the element already lies in the current incoming quad + { + if (!quad_extent.contains(bounding_box)) + return -1; + + set_sub_tree_element_count_(quad_handle, get_sub_tree_element_count_(quad_handle) + 1); + set_contained_sub_tree_element_count_(quad_handle, get_contained_sub_tree_element_count_(quad_handle) + 1); + } + + double bounding_box_max_dim = Math.max(bounding_box.getWidth(), bounding_box.getHeight()); + + int element_handle = -1; + AttributeStreamOfInt32 quads_stack = new AttributeStreamOfInt32(0); + ArrayList extents_stack = new ArrayList(0); + AttributeStreamOfInt32 heights_stack = new AttributeStreamOfInt32(0); + quads_stack.add(quad_handle); + extents_stack.add(new Envelope2D(quad_extent.xmin, quad_extent.ymin, quad_extent.xmax, quad_extent.ymax)); + heights_stack.add(height); + + Envelope2D[] child_extents = new Envelope2D[4]; + child_extents[0] = new Envelope2D(); + child_extents[1] = new Envelope2D(); + child_extents[2] = new Envelope2D(); + child_extents[3] = new Envelope2D(); + + Envelope2D current_extent = new Envelope2D(); + + while (quads_stack.size() > 0) { + boolean b_subdivide = false; + + int current_quad_handle = quads_stack.getLast(); + current_extent.setCoords(extents_stack.get(extents_stack.size() - 1)); + int current_height = heights_stack.getLast(); + + quads_stack.removeLast(); + extents_stack.remove(extents_stack.size() - 1); + heights_stack.removeLast(); + + if (current_height + 1 < m_height && can_push_down_(current_quad_handle)) { + double current_extent_max_dim = Math.max(current_extent.getWidth(), current_extent.getHeight()); + + if (bounding_box_max_dim <= current_extent_max_dim / 2.0) + b_subdivide = true; + } + + if (b_subdivide) { + set_child_extents_(current_extent, child_extents); + + boolean b_contains = false; + + for (int i = 0; i < 4; i++) { + b_contains = child_extents[i].contains(bounding_box); + + if (b_contains) { + int child_handle = get_child_(current_quad_handle, i); + if (child_handle == -1) + child_handle = create_child_(current_quad_handle, i); + + quads_stack.add(child_handle); + extents_stack.add(new Envelope2D(child_extents[i].xmin, child_extents[i].ymin, child_extents[i].xmax, child_extents[i].ymax)); + heights_stack.add(current_height + 1); + + set_sub_tree_element_count_(child_handle, get_sub_tree_element_count_(child_handle) + 1); + set_contained_sub_tree_element_count_(child_handle, get_contained_sub_tree_element_count_(child_handle) + 1); + break; + } + } + + if (!b_contains) { + for (int i = 0; i < 4; i++) { + boolean b_intersects = child_extents[i].isIntersecting(bounding_box); + + if (b_intersects) { + int child_handle = get_child_(current_quad_handle, i); + if (child_handle == -1) + child_handle = create_child_(current_quad_handle, i); + + quads_stack.add(child_handle); + extents_stack.add(new Envelope2D(child_extents[i].xmin, child_extents[i].ymin, child_extents[i].xmax, child_extents[i].ymax)); + heights_stack.add(current_height + 1); + + set_sub_tree_element_count_(child_handle, get_sub_tree_element_count_(child_handle) + 1); + } + } + } + } else { + element_handle = insert_at_quad_(element, bounding_box, current_height, current_extent, current_quad_handle, b_flushing, quad_handle, flushed_element_handle, element_handle); + b_flushing = false; // flushing is false after the first inserted element has been flushed down, all subsequent inserts will be new + } + } + + return 0; + } + + private int insert_at_quad_(int element, Envelope2D bounding_box, int current_height, Envelope2D current_extent, int current_quad_handle, boolean b_flushing, int quad_handle, int flushed_element_handle, int duplicate_element_handle) { + // If the bounding box is not contained in any of the current_node's children, or if the current_height is m_height, then insert the element and // bounding box into the current_node - int head_element_handle = getFirstElement_(current_quad_handle); - int tail_element_handle = getLastElement_(current_quad_handle); + int head_element_handle = get_first_element_(current_quad_handle); + int tail_element_handle = get_last_element_(current_quad_handle); int element_handle = -1; if (b_flushing) { @@ -556,295 +948,350 @@ private int insertAtQuad_(int element, Envelope2D bounding_box, if (current_quad_handle == quad_handle) return flushed_element_handle; - disconnectElementHandle_(flushed_element_handle); // Take it out of - // the incoming - // quad_handle, - // and place in - // current_quad_handle + disconnect_element_handle_(flushed_element_handle); // Take it out of the incoming quad_handle, and place in current_quad_handle element_handle = flushed_element_handle; } else { - element_handle = createElementAndBoxNode_(); - setElement_(element_handle, element); // insert element at the new - // tail of the list - // (next_element_handle). - setBoundingBox_(getBoxHandle_(element_handle), bounding_box); // insert - // bounding_box + if (duplicate_element_handle == -1) { + element_handle = create_element_(); + set_data_values_(get_data_(element_handle), element, bounding_box); + } else { + assert (m_b_store_duplicates); + element_handle = create_element_from_duplicate_(duplicate_element_handle); + } } assert (!b_flushing || element_handle == flushed_element_handle); - setQuad_(element_handle, current_quad_handle); // set parent quad - // (needed for removal - // of element) + set_quad_(element_handle, current_quad_handle); // set parent quad (needed for removal of element) - // assign the prev pointer of the new tail to point at the old tail - // (tail_element_handle) - // assign the next pointer of the old tail to point at the new tail - // (next_element_handle) + // assign the prev pointer of the new tail to point at the old tail (tail_element_handle) + // assign the next pointer of the old tail to point at the new tail (next_element_handle) if (tail_element_handle != -1) { - setPrevElement_(element_handle, tail_element_handle); - setNextElement_(tail_element_handle, element_handle); + set_prev_element_(element_handle, tail_element_handle); + set_next_element_(tail_element_handle, element_handle); } else { assert (head_element_handle == -1); - setFirstElement_(current_quad_handle, element_handle); + set_first_element_(current_quad_handle, element_handle); } // assign the new tail - setLastElement_(current_quad_handle, element_handle); + set_last_element_(current_quad_handle, element_handle); - setLocalElementCount_(current_quad_handle, - getLocalElementCount_(current_quad_handle) + 1); + set_local_element_count_(current_quad_handle, get_local_element_count_(current_quad_handle) + 1); - if (canFlush_(current_quad_handle)) + if (can_flush_(current_quad_handle)) flush_(current_height, current_extent, current_quad_handle); return element_handle; } - private int disconnectElementHandle_(int element_handle) { + private static void set_child_extents_(Envelope2D current_extent, Envelope2D[] child_extents) { + double x_mid = 0.5 * (current_extent.xmin + current_extent.xmax); + double y_mid = 0.5 * (current_extent.ymin + current_extent.ymax); + + child_extents[0].setCoords(x_mid, y_mid, current_extent.xmax, current_extent.ymax); // northeast + child_extents[1].setCoords(current_extent.xmin, y_mid, x_mid, current_extent.ymax); // northwest + child_extents[2].setCoords(current_extent.xmin, current_extent.ymin, x_mid, y_mid); // southwest + child_extents[3].setCoords(x_mid, current_extent.ymin, current_extent.xmax, y_mid); // southeast + } + + private void disconnect_element_handle_(int element_handle) { assert (element_handle != -1); - int quad_handle = getQuad_(element_handle); - int head_element_handle = getFirstElement_(quad_handle); - int tail_element_handle = getLastElement_(quad_handle); - int prev_element_handle = getPrevElement_(element_handle); - int next_element_handle = getNextElement_(element_handle); + int quad_handle = get_quad_(element_handle); + int head_element_handle = get_first_element_(quad_handle); + int tail_element_handle = get_last_element_(quad_handle); + int prev_element_handle = get_prev_element_(element_handle); + int next_element_handle = get_next_element_(element_handle); assert (head_element_handle != -1 && tail_element_handle != -1); if (head_element_handle == element_handle) { if (next_element_handle != -1) - setPrevElement_(next_element_handle, -1); + set_prev_element_(next_element_handle, -1); else { assert (head_element_handle == tail_element_handle); - assert (getLocalElementCount_(quad_handle) == 1); - setLastElement_(quad_handle, -1); + assert (get_local_element_count_(quad_handle) == 1); + set_last_element_(quad_handle, -1); } - setFirstElement_(quad_handle, next_element_handle); + set_first_element_(quad_handle, next_element_handle); } else if (tail_element_handle == element_handle) { assert (prev_element_handle != -1); - assert (getLocalElementCount_(quad_handle) >= 2); - setNextElement_(prev_element_handle, -1); - setLastElement_(quad_handle, prev_element_handle); + assert (get_local_element_count_(quad_handle) >= 2); + set_next_element_(prev_element_handle, -1); + set_last_element_(quad_handle, prev_element_handle); } else { assert (next_element_handle != -1 && prev_element_handle != -1); - assert (getLocalElementCount_(quad_handle) >= 3); - setPrevElement_(next_element_handle, prev_element_handle); - setNextElement_(prev_element_handle, next_element_handle); + assert (get_local_element_count_(quad_handle) >= 3); + set_prev_element_(next_element_handle, prev_element_handle); + set_next_element_(prev_element_handle, next_element_handle); } - setPrevElement_(element_handle, -1); - setNextElement_(element_handle, -1); - - setLocalElementCount_(quad_handle, - getLocalElementCount_(quad_handle) - 1); - assert (getLocalElementCount_(quad_handle) >= 0); + set_prev_element_(element_handle, -1); + set_next_element_(element_handle, -1); - return next_element_handle; + set_local_element_count_(quad_handle, get_local_element_count_(quad_handle) - 1); + assert (get_local_element_count_(quad_handle) >= 0); } - private static void setChildExtents_(Envelope2D current_extent, - Envelope2D[] child_extents) { - double x_mid = 0.5 * (current_extent.xmin + current_extent.xmax); - double y_mid = 0.5 * (current_extent.ymin + current_extent.ymax); - - child_extents[0].setCoords(x_mid, y_mid, current_extent.xmax, - current_extent.ymax); // northeast - child_extents[1].setCoords(current_extent.xmin, y_mid, x_mid, - current_extent.ymax); // northwest - child_extents[2].setCoords(current_extent.xmin, current_extent.ymin, - x_mid, y_mid); // southwest - child_extents[3].setCoords(x_mid, current_extent.ymin, - current_extent.xmax, y_mid); // southeast - } - - private boolean canFlush_(int quad_handle) { - return getLocalElementCount_(quad_handle) == 8 - && !hasChildren_(quad_handle); + private boolean can_flush_(int quad_handle) { + return get_local_element_count_(quad_handle) == m_flushing_count && !has_children_(quad_handle); } private void flush_(int height, Envelope2D extent, int quad_handle) { int element; - Envelope2D bounding_box; + Envelope2D bounding_box = new Envelope2D(); assert (quad_handle != -1); - int element_handle = getFirstElement_(quad_handle), next_handle; - int box_handle; + int element_handle = get_first_element_(quad_handle), next_handle = -1; + int data_handle = -1; assert (element_handle != -1); do { - box_handle = getBoxHandle_(element_handle); - element = m_element_nodes.getField(element_handle, 0); - bounding_box = getBoundingBox_(box_handle); - insert_(element, bounding_box, height, extent, quad_handle, true, - element_handle); + data_handle = get_data_(element_handle); + element = get_element_value_(data_handle); + bounding_box.setCoords(get_bounding_box_value_(data_handle)); + + next_handle = get_next_element_(element_handle); + + if (!m_b_store_duplicates) + insert_(element, bounding_box, height, extent, quad_handle, true, element_handle); + else + insert_duplicates_(element, bounding_box, height, extent, quad_handle, true, element_handle); - next_handle = getNextElement_(element_handle); element_handle = next_handle; } while (element_handle != -1); } - boolean canPushDown_(int quad_handle) { - return getLocalElementCount_(quad_handle) >= 8 - || hasChildren_(quad_handle); + private boolean can_push_down_(int quad_handle) { + return get_local_element_count_(quad_handle) >= m_flushing_count || has_children_(quad_handle); } - boolean hasChildren_(int parent) { - return getChild_(parent, 0) != -1 || getChild_(parent, 1) != -1 - || getChild_(parent, 2) != -1 || getChild_(parent, 3) != -1; + private boolean has_children_(int parent) { + return get_child_(parent, 0) != -1 || get_child_(parent, 1) != -1 || get_child_(parent, 2) != -1 || get_child_(parent, 3) != -1; } - private int createChild_(int parent, int quadrant) { + private int create_child_(int parent, int quadrant) { int child = m_quad_tree_nodes.newElement(); - setChild_(parent, quadrant, child); - setSubTreeElementCount_(child, 0); - setLocalElementCount_(child, 0); - setParent_(child, parent); - setHeight_(child, getHeight_(parent) + 1); - setMortenNumber_(child, (quadrant << (2 * getHeight_(parent))) - | getMortenNumber_(parent)); + set_child_(parent, quadrant, child); + set_sub_tree_element_count_(child, 0); + set_local_element_count_(child, 0); + set_parent_(child, parent); + set_height_and_quadrant_(child, get_height_(parent) + 1, quadrant); + + if (m_b_store_duplicates) + set_contained_sub_tree_element_count_(child, 0); + return child; } - private int createElementAndBoxNode_() { + private void create_root_() { + m_root = m_quad_tree_nodes.newElement(); + set_sub_tree_element_count_(m_root, 0); + set_local_element_count_(m_root, 0); + set_height_and_quadrant_(m_root, 0, 0); + + if (m_b_store_duplicates) + set_contained_sub_tree_element_count_(m_root, 0); + } + + private int create_element_() { int element_handle = m_element_nodes.newElement(); - int box_handle; + int data_handle; - if (m_free_boxes.size() > 0) { - box_handle = m_free_boxes.getLast(); - m_free_boxes.removeLast(); + if (m_free_data.size() > 0) { + data_handle = m_free_data.get(m_free_data.size() - 1); + m_free_data.removeLast(); } else { - box_handle = m_boxes.size(); - m_boxes.add(new Envelope2D()); + data_handle = m_data.size(); + m_data.add(null); } - setBoxHandle_(element_handle, box_handle); + set_data_(element_handle, data_handle); return element_handle; } - private void freeElementAndBoxNode_(int element_handle) { - m_free_boxes.add(getBoxHandle_(element_handle)); + private int create_element_from_duplicate_(int duplicate_element_handle) { + int element_handle = m_element_nodes.newElement(); + int data_handle = get_data_(duplicate_element_handle); + set_data_(element_handle, data_handle); + return element_handle; + } + + private void free_element_and_box_node_(int element_handle) { + int data_handle = get_data_(element_handle); + m_free_data.add(data_handle); m_element_nodes.deleteElement(element_handle); } - private int getChild_(int quad_handle, int quadrant) { + private int get_child_(int quad_handle, int quadrant) { return m_quad_tree_nodes.getField(quad_handle, quadrant); } - private void setChild_(int parent, int quadrant, int child) { + private void set_child_(int parent, int quadrant, int child) { m_quad_tree_nodes.setField(parent, quadrant, child); } - private int getFirstElement_(int quad_handle) { + private int get_first_element_(int quad_handle) { return m_quad_tree_nodes.getField(quad_handle, 4); } - private void setFirstElement_(int quad_handle, int head) { + private void set_first_element_(int quad_handle, int head) { m_quad_tree_nodes.setField(quad_handle, 4, head); } - private int getLastElement_(int quad_handle) { + private int get_last_element_(int quad_handle) { return m_quad_tree_nodes.getField(quad_handle, 5); } - private void setLastElement_(int quad_handle, int tail) { + private void set_last_element_(int quad_handle, int tail) { m_quad_tree_nodes.setField(quad_handle, 5, tail); } - private int getMortenNumber_(int quad_handle) { - return m_quad_tree_nodes.getField(quad_handle, 6); + + private int get_quadrant_(int quad_handle) { + int height_quadrant_hybrid = m_quad_tree_nodes.getField(quad_handle, 6); + int quadrant = height_quadrant_hybrid & m_quadrant_mask; + return quadrant; } - private void setMortenNumber_(int quad_handle, int morten_number) { - m_quad_tree_nodes.setField(quad_handle, 6, morten_number); + private int get_height_(int quad_handle) { + int height_quadrant_hybrid = m_quad_tree_nodes.getField(quad_handle, 6); + int height = height_quadrant_hybrid >> m_height_bit_shift; + return height; } - private int getLocalElementCount_(int quad_handle) { - return m_quad_tree_nodes.getField(quad_handle, 7); + private void set_height_and_quadrant_(int quad_handle, int height, int quadrant) { + assert (quadrant >= 0 && quadrant <= 3); + int height_quadrant_hybrid = (int) ((height << m_height_bit_shift) | quadrant); + m_quad_tree_nodes.setField(quad_handle, 6, height_quadrant_hybrid); } - private int getSubTreeElementCount_(int quad_handle) { - return m_quad_tree_nodes.getField(quad_handle, 8); + private int get_local_element_count_(int quad_handle) { + return m_quad_tree_nodes.getField(quad_handle, 7); } - private void setLocalElementCount_(int quad_handle, int count) { + private void set_local_element_count_(int quad_handle, int count) { m_quad_tree_nodes.setField(quad_handle, 7, count); } - private void setSubTreeElementCount_(int quad_handle, int count) { + private int get_sub_tree_element_count_(int quad_handle) { + return m_quad_tree_nodes.getField(quad_handle, 8); + } + + private void set_sub_tree_element_count_(int quad_handle, int count) { m_quad_tree_nodes.setField(quad_handle, 8, count); } - private int getParent_(int child) { + private int get_parent_(int child) { return m_quad_tree_nodes.getField(child, 9); } - private void setParent_(int child, int parent) { + private void set_parent_(int child, int parent) { m_quad_tree_nodes.setField(child, 9, parent); } - private int getHeight_(int quad_handle) { - return (int) m_quad_tree_nodes.getField(quad_handle, 10); + private int get_contained_sub_tree_element_count_(int quad_handle) { + return m_quad_tree_nodes.getField(quad_handle, 10); } - private void setHeight_(int quad_handle, int height) { - m_quad_tree_nodes.setField(quad_handle, 10, height); + private void set_contained_sub_tree_element_count_(int quad_handle, int count) { + m_quad_tree_nodes.setField(quad_handle, 10, count); } - private int getElement_(int element_handle) { + private int get_data_(int element_handle) { return m_element_nodes.getField(element_handle, 0); } - private void setElement_(int element_handle, int element) { - m_element_nodes.setField(element_handle, 0, element); + private void set_data_(int element_handle, int data_handle) { + m_element_nodes.setField(element_handle, 0, data_handle); } - private int getPrevElement_(int element_handle) { + private int get_prev_element_(int element_handle) { return m_element_nodes.getField(element_handle, 1); } - private int getNextElement_(int element_handle) { + private int get_next_element_(int element_handle) { return m_element_nodes.getField(element_handle, 2); } - private void setPrevElement_(int element_handle, int prev_handle) { + private void set_prev_element_(int element_handle, int prev_handle) { m_element_nodes.setField(element_handle, 1, prev_handle); } - private void setNextElement_(int element_handle, int next_handle) { + private void set_next_element_(int element_handle, int next_handle) { m_element_nodes.setField(element_handle, 2, next_handle); } - private int getQuad_(int element_handle) { + private int get_quad_(int element_handle) { return m_element_nodes.getField(element_handle, 3); } - private void setQuad_(int element_handle, int parent) { + private void set_quad_(int element_handle, int parent) { m_element_nodes.setField(element_handle, 3, parent); } - private int getBoxHandle_(int element_handle) { - return m_element_nodes.getField(element_handle, 4); - } - - private void setBoxHandle_(int element_handle, int box_handle) { - m_element_nodes.setField(element_handle, 4, box_handle); + private int get_element_value_(int data_handle) { + return m_data.get(data_handle).element; } - private Envelope2D getBoundingBox_(int box_handle) { - return m_boxes.get(box_handle); + private Envelope2D get_bounding_box_value_(int data_handle) { + return m_data.get(data_handle).box; } - private void setBoundingBox_(int box_handle, Envelope2D bounding_box) { - m_boxes.get(box_handle).setCoords(bounding_box); + private void set_data_values_(int data_handle, int element, Envelope2D bounding_box) { + m_data.set(data_handle, new Data(element, bounding_box)); } - private int m_root; private Envelope2D m_extent; - private int m_height; + private Envelope2D m_data_extent; private StridedIndexTypeCollection m_quad_tree_nodes; private StridedIndexTypeCollection m_element_nodes; - private ArrayList m_boxes; - private AttributeStreamOfInt32 m_free_boxes; + private ArrayList m_data; + private AttributeStreamOfInt32 m_free_data; + private int m_root; + private int m_height; + private boolean m_b_store_duplicates; + + private int m_quadrant_mask = 3; + private int m_height_bit_shift = 2; + private int m_flushing_count = 5; + + static final class Data { + int element; + Envelope2D box; + + Data(int element_, Envelope2D box_) { + element = element_; + box = new Envelope2D(); + box.setCoords(box_); + } + } + + /* m_quad_tree_nodes + * 0: m_north_east_child + * 1: m_north_west_child + * 2: m_south_west_child + * 3: m_south_east_child + * 4: m_head_element + * 5: m_tail_element + * 6: m_quadrant_and_height + * 7: m_local_element_count + * 8: m_sub_tree_element_count + * 9: m_parent_quad + * 10: m_height + */ + + /* m_element_nodes + * 0: m_data_handle + * 1: m_prev + * 2: m_next + * 3: m_parent_quad + */ + + /* m_data + * element + * box + */ } diff --git a/src/main/java/com/esri/core/geometry/RasterizedGeometry2DImpl.java b/src/main/java/com/esri/core/geometry/RasterizedGeometry2DImpl.java index ded79caf..ff21c8ec 100644 --- a/src/main/java/com/esri/core/geometry/RasterizedGeometry2DImpl.java +++ b/src/main/java/com/esri/core/geometry/RasterizedGeometry2DImpl.java @@ -138,14 +138,16 @@ void strokeDrawPolyPath(SimpleRasterizer rasterizer, SegmentIteratorImpl segIter = polyPath.querySegmentIterator(); double strokeHalfWidth = m_transform.transform(tol) + 1.5; - double shortSegment = 0.5; + double shortSegment = 0.25; Point2D vec = new Point2D(); Point2D vecA = new Point2D(); Point2D vecB = new Point2D(); - // TODO check this Java workaroung Point2D ptStart = new Point2D(); Point2D ptEnd = new Point2D(); + Point2D prev_start = new Point2D(); + Point2D prev_end = new Point2D(); + double[] helper_xy_10_elm = new double[10]; Envelope2D segEnv = new Envelope2D(); Point2D ptOld = new Point2D(); while (segIter.nextPath()) { @@ -155,18 +157,22 @@ void strokeDrawPolyPath(SimpleRasterizer rasterizer, while (segIter.hasNextSegment()) { Segment seg = segIter.nextSegment(); ptStart.x = seg.getStartX(); - ptStart.y = seg.getStartY();// Point2D ptStart = - // seg.getStartXY(); + ptStart.y = seg.getStartY(); ptEnd.x = seg.getEndX(); - ptEnd.y = seg.getEndY();// Point2D ptEnd = seg.getEndXY(); + ptEnd.y = seg.getEndY(); segEnv.setEmpty(); segEnv.merge(ptStart.x, ptStart.y); segEnv.mergeNE(ptEnd.x, ptEnd.y); if (!m_geomEnv.isIntersectingNE(segEnv)) { if (hasFan) { - fillConvexPolygon(rasterizer, fan, 4); + rasterizer.startAddingEdges(); + rasterizer.addSegmentStroke(prev_start.x, prev_start.y, + prev_end.x, prev_end.y, strokeHalfWidth, false, + helper_xy_10_elm); + rasterizer.renderEdges(SimpleRasterizer.EVEN_ODD); hasFan = false; } + first = true; continue; } @@ -181,34 +187,25 @@ void strokeDrawPolyPath(SimpleRasterizer rasterizer, ptStart.setCoords(ptOld); } - vec.sub(ptEnd, ptStart); - double len = vec.length(); - boolean bShort = len < shortSegment; - if (len == 0) { - vec.setCoords(1.0, 0); - len = 1.0; - continue; - } + prev_start.setCoords(ptStart); + prev_end.setCoords(ptEnd); + + rasterizer.startAddingEdges(); + hasFan = !rasterizer.addSegmentStroke(prev_start.x, + prev_start.y, prev_end.x, prev_end.y, strokeHalfWidth, + true, helper_xy_10_elm); + rasterizer.renderEdges(SimpleRasterizer.EVEN_ODD); + if (!hasFan) + ptOld.setCoords(prev_end); + } - if (!bShort) - ptOld.setCoords(ptEnd); - - vec.scale(strokeHalfWidth / len); - vecA.setCoords(-vec.y, vec.x); - vecB.setCoords(vec.y, -vec.x); - ptStart.sub(vec); - ptEnd.add(vec); - fan[0].add(ptStart, vecA); - fan[1].add(ptStart, vecB); - fan[2].add(ptEnd, vecB); - fan[3].add(ptEnd, vecA); - if (!bShort) - fillConvexPolygon(rasterizer, fan, 4); - else - hasFan = true; + if (hasFan) { + rasterizer.startAddingEdges(); + hasFan = !rasterizer.addSegmentStroke(prev_start.x, + prev_start.y, prev_end.x, prev_end.y, strokeHalfWidth, + false, helper_xy_10_elm); + rasterizer.renderEdges(SimpleRasterizer.EVEN_ODD); } - if (hasFan) - fillConvexPolygon(rasterizer, fan, 4); } } diff --git a/src/main/java/com/esri/core/geometry/Segment.java b/src/main/java/com/esri/core/geometry/Segment.java index be94b723..ca2ea184 100644 --- a/src/main/java/com/esri/core/geometry/Segment.java +++ b/src/main/java/com/esri/core/geometry/Segment.java @@ -25,16 +25,13 @@ package com.esri.core.geometry; import com.esri.core.geometry.VertexDescription.Semantics; + import java.io.Serializable; /** * A base class for segments. Presently only Line segments are supported. */ public abstract class Segment extends Geometry implements Serializable { - - // UPDATED PORT TO MATCH NATIVE AS OF JAN 30 2011 - private static final long serialVersionUID = 1L; - double m_xStart; double m_yStart; @@ -49,11 +46,11 @@ public abstract class Segment extends Geometry implements Serializable { /** * Returns XY coordinates of the start point. */ - Point2D getStartXY() { + public Point2D getStartXY() { return Point2D.construct(m_xStart, m_yStart); } - void getStartXY(Point2D pt) { + public void getStartXY(Point2D pt) { pt.x = m_xStart; pt.y = m_yStart; } @@ -61,29 +58,29 @@ void getStartXY(Point2D pt) { /** * Sets the XY coordinates of the start point. */ - void setStartXY(Point2D pt) { + public void setStartXY(Point2D pt) { _setXY(0, pt); } - void setStartXY(double x, double y) { + public void setStartXY(double x, double y) { _setXY(0, Point2D.construct(x, y)); } /** * Returns XYZ coordinates of the start point. Z if 0 if Z is missing. */ - Point3D getStartXYZ() { + public Point3D getStartXYZ() { return _getXYZ(0); } /** * Sets the XYZ coordinates of the start point. */ - void setStartXYZ(Point3D pt) { + public void setStartXYZ(Point3D pt) { _setXYZ(0, pt); } - void setStartXYZ(double x, double y, double z) { + public void setStartXYZ(double x, double y, double z) { _setXYZ(0, Point3D.construct(x, y, z)); } @@ -193,11 +190,11 @@ public double getEndY() { * * @return The XY coordinates of the end point. */ - Point2D getEndXY() { + public Point2D getEndXY() { return Point2D.construct(m_xEnd, m_yEnd); } - void getEndXY(Point2D pt) { + public void getEndXY(Point2D pt) { pt.x = m_xEnd; pt.y = m_yEnd; } @@ -208,11 +205,11 @@ void getEndXY(Point2D pt) { * @param pt * The end point of the segment. */ - void setEndXY(Point2D pt) { + public void setEndXY(Point2D pt) { _setXY(1, pt); } - void setEndXY(double x, double y) { + public void setEndXY(double x, double y) { _setXY(1, Point2D.construct(x, y)); } @@ -221,18 +218,18 @@ void setEndXY(double x, double y) { * * @return The XYZ coordinates of the end point. */ - Point3D getEndXYZ() { + public Point3D getEndXYZ() { return _getXYZ(1); } /** * Sets the XYZ coordinates of the end point. */ - void setEndXYZ(Point3D pt) { + public void setEndXYZ(Point3D pt) { _setXYZ(1, pt); } - void setEndXYZ(double x, double y, double z) { + public void setEndXYZ(double x, double y, double z) { _setXYZ(1, Point3D.construct(x, y, z)); } @@ -358,7 +355,7 @@ int intersect(Segment other, Point2D[] intersectionPoints, * Returns TRUE if this segment intersects with the other segment with the * given tolerance. */ - boolean isIntersecting(Segment other, double tolerance) { + public boolean isIntersecting(Segment other, double tolerance) { return _isIntersecting(other, tolerance, false) != 0; } @@ -366,7 +363,7 @@ boolean isIntersecting(Segment other, double tolerance) { * Returns TRUE if the point and segment intersect (not disjoint) for the * given tolerance. */ - boolean isIntersecting(Point2D pt, double tolerance) { + public boolean isIntersecting(Point2D pt, double tolerance) { return _isIntersectingPoint(pt, tolerance, false); } @@ -485,7 +482,7 @@ protected void _assignVertexDescriptionImpl(VertexDescription newDescription) { int[] mapping = VertexDescriptionDesignerImpl.mapAttributes(newDescription, m_description); - double[] newAttributes = new double[(newDescription._getTotalComponents() - 2) * 2]; + double[] newAttributes = new double[(newDescription.getTotalComponentCount() - 2) * 2]; int old_offset0 = _getEndPointOffset(m_description, 0); int old_offset1 = _getEndPointOffset(m_description, 1); @@ -583,7 +580,7 @@ private void _set(int endPoint, Point src) { int attributeIndex = m_description.getAttributeIndex(semantics); if (attributeIndex >= 0) { if (m_attributes != null) - _resizeAttributes(m_description._getTotalComponents() - 2); + _resizeAttributes(m_description.getTotalComponentCount() - 2); return m_attributes[_getEndPointOffset(m_description, endPoint) + m_description._getPointAttributeOffset(attributeIndex) @@ -626,7 +623,7 @@ else if (ordinate != 0) } if (m_attributes == null) - _resizeAttributes(m_description._getTotalComponents() - 2); + _resizeAttributes(m_description.getTotalComponentCount() - 2); m_attributes[_getEndPointOffset(m_description, endPoint) + m_description._getPointAttributeOffset(attributeIndex) - 2 @@ -645,9 +642,9 @@ public void copyTo(Geometry dst) { Segment segDst = (Segment) dst; segDst.m_description = m_description; - segDst._resizeAttributes(m_description._getTotalComponents() - 2); + segDst._resizeAttributes(m_description.getTotalComponentCount() - 2); _attributeCopy(m_attributes, 0, segDst.m_attributes, 0, - (m_description._getTotalComponents() - 2) * 2); + (m_description.getTotalComponentCount() - 2) * 2); segDst.m_xStart = m_xStart; segDst.m_yStart = m_yStart; segDst.m_xEnd = m_xEnd; @@ -691,7 +688,7 @@ boolean _equalsImpl(Segment other) { if (m_xStart != other.m_xStart || m_xEnd != other.m_xEnd || m_yStart != other.m_yStart || m_yEnd != other.m_yEnd) return false; - for (int i = 0; i < (m_description._getTotalComponents() - 2) * 2; i++) + for (int i = 0; i < (m_description.getTotalComponentCount() - 2) * 2; i++) if (m_attributes[i] != other.m_attributes[i]) return false; @@ -711,10 +708,6 @@ boolean isClosed() { void reverse() { _reverseImpl(); - // because java doesn't support passing value types - // by reference numberutils swap won't work - // NumberUtils.swap(m_xStart, m_xEnd); - // NumberUtils.swap(m_yStart, m_yEnd); double origxStart = m_xStart; double origxEnd = m_xEnd; m_xStart = origxEnd; @@ -778,14 +771,14 @@ int _intersect(Segment other, Point2D[] intersectionPoints, abstract double _calculateArea2DHelper(double xorg, double yorg); static int _getEndPointOffset(VertexDescription vd, int endPoint) { - return endPoint * (vd._getTotalComponents() - 2); + return endPoint * (vd.getTotalComponentCount() - 2); } /** * Returns the coordinate of the point on this segment for the given * parameter value. */ - Point2D getCoord2D(double t) { + public Point2D getCoord2D(double t) { Point2D pt = new Point2D(); getCoord2D(t, pt); return pt; @@ -801,7 +794,7 @@ Point2D getCoord2D(double t) { * @param dst * the coordinate where result will be placed. */ - abstract void getCoord2D(double t, Point2D dst); + public abstract void getCoord2D(double t, Point2D dst); /** * Finds a closest coordinate on this segment. @@ -817,7 +810,7 @@ Point2D getCoord2D(double t) { * obtain the 2D coordinate on the segment from t. To find the * distance, call (inputPoint.sub(seg.getCoord2D(t))).length(); */ - abstract double getClosestCoordinate(Point2D inputPoint, + public abstract double getClosestCoordinate(Point2D inputPoint, boolean bExtrapolate); /** @@ -887,7 +880,7 @@ void _reverseImpl() { * Returns subsegment between parameters t1 and t2. The attributes are * interpolated along the length of the curve. */ - abstract Segment cut(double t1, double t2); + public abstract Segment cut(double t1, double t2); /** * Calculates the subsegment between parameters t1 and t2, and stores the @@ -927,8 +920,8 @@ abstract boolean _isIntersectingPoint(Point2D pt, double tolerance, abstract double lengthToT(double len); - double distance(/* const */Segment otherSegment, - boolean bSegmentsKnownDisjoint) /* const */ + public double distance(/* const */Segment otherSegment, + boolean bSegmentsKnownDisjoint) { // if the segments are not known to be disjoint, and // the segments are found to touch in any way, then return 0.0 diff --git a/src/main/java/com/esri/core/geometry/SegmentIterator.java b/src/main/java/com/esri/core/geometry/SegmentIterator.java index 8312e028..089f93d8 100644 --- a/src/main/java/com/esri/core/geometry/SegmentIterator.java +++ b/src/main/java/com/esri/core/geometry/SegmentIterator.java @@ -25,7 +25,17 @@ package com.esri.core.geometry; /** - * This class provides functionality to iterate over multipath segments. + * This class provides functionality to iterate over MultiPath segments. + * + * Example: + *


+ * SegmentIterator iterator = polygon.querySegmentIterator();
+ * while (iterator.nextPath()) {
+ *   while (iterator.hasNextSegment()) {
+ *     Segment segment = iterator.nextSegment();
+ *   }
+ * }
+ * 
*/ public class SegmentIterator { private SegmentIteratorImpl m_impl; diff --git a/src/main/java/com/esri/core/geometry/SimpleRasterizer.java b/src/main/java/com/esri/core/geometry/SimpleRasterizer.java index 783b8a8f..de817443 100644 --- a/src/main/java/com/esri/core/geometry/SimpleRasterizer.java +++ b/src/main/java/com/esri/core/geometry/SimpleRasterizer.java @@ -295,6 +295,50 @@ public final void fillEnvelope(Envelope2D envIn) { } } + final boolean addSegmentStroke(double x1, double y1, double x2, double y2, double half_width, boolean skip_short, double[] helper_xy_10_elm) + { + double vec_x = x2 - x1; + double vec_y = y2 - y1; + double len = Math.sqrt(vec_x * vec_x + vec_y * vec_y); + if (skip_short && len < 0.5) + return false; + + boolean bshort = len < 0.00001; + if (bshort) + { + len = 0.00001; + vec_x = len; + vec_y = 0.0; + } + + double f = half_width / len; + vec_x *= f; vec_y *= f; + double vecA_x = -vec_y; + double vecA_y = vec_x; + double vecB_x = vec_y; + double vecB_y = -vec_x; + //extend by half width + x1 -= vec_x; + y1 -= vec_y; + x2 += vec_x; + y2 += vec_y; + //create rotated rectangle + double[] fan = helper_xy_10_elm; + assert(fan.length == 10); + fan[0] = x1 + vecA_x; + fan[1] = y1 + vecA_y;//fan[0].add(pt_start, vecA); + fan[2] = x1 + vecB_x; + fan[3] = y1 + vecB_y;//fan[1].add(pt_start, vecB); + fan[4] = x2 + vecB_x; + fan[5] = y2 + vecB_y;//fan[2].add(pt_end, vecB) + fan[6] = x2 + vecA_x; + fan[7] = y2 + vecA_y;//fan[3].add(pt_end, vecA) + fan[8] = fan[0]; + fan[9] = fan[1]; + addRing(fan); + return true; + } + public final ScanCallback getScanCallback() { return callback_; } diff --git a/src/main/java/com/esri/core/geometry/SpatialReferenceImpl.java b/src/main/java/com/esri/core/geometry/SpatialReferenceImpl.java index 77520c40..1aa5886a 100644 --- a/src/main/java/com/esri/core/geometry/SpatialReferenceImpl.java +++ b/src/main/java/com/esri/core/geometry/SpatialReferenceImpl.java @@ -24,6 +24,7 @@ package com.esri.core.geometry; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantLock; @@ -230,10 +231,77 @@ static double geodesicDistanceOnWGS84Impl(Point ptFrom, Point ptTo) { double e2 = 0.0066943799901413165; // ellipticity for WGS_1984 double rpu = Math.PI / 180.0; PeDouble answer = new PeDouble(); - GeoDist.geodesic_distance_ngs(a, e2, ptFrom.getXY().x * rpu, - ptFrom.getXY().y * rpu, ptTo.getXY().x * rpu, ptTo.getXY().y + GeoDist.geodesic_distance_ngs(a, e2, ptFrom.getX() * rpu, + ptFrom.getY() * rpu, ptTo.getX() * rpu, ptTo.getY() * rpu, answer, null, null); return answer.val; } + public String getAuthority() { + int latestWKID = getLatestID(); + if (latestWKID <= 0) + return new String(""); + + return getAuthority_(latestWKID); + } + + private String getAuthority_(int latestWKID) { + String authority; + + if (latestWKID >= 1024 && latestWKID <= 32767) { + + int index = Arrays.binarySearch(m_esri_codes, latestWKID); + + if (index >= 0) + authority = new String("ESRI"); + else + authority = new String("EPSG"); + } else { + authority = new String("ESRI"); + } + + return authority; + } + + private static final int[] m_esri_codes = { + 2181, // ED_1950_Turkey_9 + 2182, // ED_1950_Turkey_10 + 2183, // ED_1950_Turkey_11 + 2184, // ED_1950_Turkey_12 + 2185, // ED_1950_Turkey_13 + 2186, // ED_1950_Turkey_14 + 2187, // ED_1950_Turkey_15 + 4305, // GCS_Voirol_Unifie_1960 + 4812, // GCS_Voirol_Unifie_1960_Paris + 20002, // Pulkovo_1995_GK_Zone_2 + 20003, // Pulkovo_1995_GK_Zone_3 + 20062, // Pulkovo_1995_GK_Zone_2N + 20063, // Pulkovo_1995_GK_Zone_3N + 24721, // La_Canoa_UTM_Zone_21N + 26761, // NAD_1927_StatePlane_Hawaii_1_FIPS_5101 + 26762, // NAD_1927_StatePlane_Hawaii_2_FIPS_5102 + 26763, // NAD_1927_StatePlane_Hawaii_3_FIPS_5103 + 26764, // NAD_1927_StatePlane_Hawaii_4_FIPS_5104 + 26765, // NAD_1927_StatePlane_Hawaii_5_FIPS_5105 + 26788, // NAD_1927_StatePlane_Michigan_North_FIPS_2111 + 26789, // NAD_1927_StatePlane_Michigan_Central_FIPS_2112 + 26790, // NAD_1927_StatePlane_Michigan_South_FIPS_2113 + 30591, // Nord_Algerie + 30592, // Sud_Algerie + 31491, // Germany_Zone_1 + 31492, // Germany_Zone_2 + 31493, // Germany_Zone_3 + 31494, // Germany_Zone_4 + 31495, // Germany_Zone_5 + 32059, // NAD_1927_StatePlane_Puerto_Rico_FIPS_5201 + 32060, // NAD_1927_StatePlane_Virgin_Islands_St_Croix_FIPS_5202 + }; + + @Override + public int hashCode() { + if (m_userWkid != 0) + return NumberUtils.hash(m_userWkid); + + return m_userWkt.hashCode(); + } } diff --git a/src/main/java/com/esri/core/geometry/Transformation2D.java b/src/main/java/com/esri/core/geometry/Transformation2D.java index d1472399..99ea7cde 100644 --- a/src/main/java/com/esri/core/geometry/Transformation2D.java +++ b/src/main/java/com/esri/core/geometry/Transformation2D.java @@ -25,8 +25,10 @@ package com.esri.core.geometry; /** - * The affine transformation class for 2D.
- * Vector is a row: + * The affine transformation class for 2D. + * + * Vector is a row: + * *
|m11 m12 0| *
| x y 1| * |m21 m22 0| = |m11 * x + m21 * y + m31 m12 * x + m22 * y + m32 1| *
|m31 m32 1| @@ -456,9 +458,9 @@ public boolean isIdentity() { * The tolerance value. */ public boolean isIdentity(double tol) { - Point2D pt = Point2D.construct(0.0, 1.0); + Point2D pt = Point2D.construct(0., 1.); transform(pt, pt); - pt.sub(Point2D.construct(0.0, 1.0)); + pt.sub(Point2D.construct(0., 1.)); if (pt.sqrLength() > tol * tol) return false; @@ -469,7 +471,7 @@ public boolean isIdentity(double tol) { pt.setCoords(1.0, 0.0); transform(pt, pt); - pt.sub(Point2D.construct(1.0, 0)); + pt.sub(Point2D.construct(1.0, 0.0)); return pt.sqrLength() <= tol * tol; } diff --git a/src/main/java/com/esri/core/geometry/Transformation3D.java b/src/main/java/com/esri/core/geometry/Transformation3D.java index 02719a3c..99702706 100644 --- a/src/main/java/com/esri/core/geometry/Transformation3D.java +++ b/src/main/java/com/esri/core/geometry/Transformation3D.java @@ -25,18 +25,13 @@ package com.esri.core.geometry; /** - * @brief The 3D affine transformation Vector is a row: |m11 m12 0| | x y 1| * - * |m21 m22 0| = |m11 * x + m21 * y + m31 m12 * x + m22 * y + m32 1| |m31 - * m32 1| Then elements of the Transformation2D are as follows: |xx yx 0| - * | x y 1| * |xy yy 0| = |xx * x + xy * y + xd yx * x + yy * y + yd 1| - * |xd yd 1| + * The 3D affine transformation class. * - * We use matrices for transformations of the vectors as rows (case 2). - * That means the math expressions on the Geometry matrix operations - * should be writen like this: v' = v * M1 * M2 * M3 = ( (v * M1) * M2 ) - * * M3, where v is a vector, Mn are the matrices. This is equivalent to - * the following line of code: ResultVector = - * (M1.Mul(M2).Mul(M3)).Transform(Vector) + * We use matrices for transformations of the vectors as rows. That means the + * math expressions on the Geometry matrix operations should be writen like + * this: v' = v * M1 * M2 * M3 = ( (v * M1) * M2 ) * M3, where v is a vector, Mn + * are the matrices. This is equivalent to the following line of code: + * ResultVector = (M1.Mul(M2).Mul(M3)).Transform(Vector) */ final class Transformation3D { @@ -201,13 +196,11 @@ public static void multiply(Transformation3D a, Transformation3D b, * * @param src * The input transformation. - * @param dst - * The inverse of the input transformation. - * @throws Throws - * the GeometryException("math_singularity") exception if the - * Inverse can not be calculated. + * @param result + * The inverse of the input transformation. Throws the + * GeometryException("math singularity") exception if the Inverse + * can not be calculated. */ - // static public static void inverse(Transformation3D src, Transformation3D result) { double det = src.xx * (src.yy * src.zz - src.zy * src.yz) - src.yx * (src.xy * src.zz - src.zy * src.xz) + src.zx diff --git a/src/main/java/com/esri/core/geometry/VertexDescription.java b/src/main/java/com/esri/core/geometry/VertexDescription.java index 1fc1a6f6..07e7630b 100644 --- a/src/main/java/com/esri/core/geometry/VertexDescription.java +++ b/src/main/java/com/esri/core/geometry/VertexDescription.java @@ -25,6 +25,8 @@ package com.esri.core.geometry; +import java.util.Arrays; + /** * Describes the vertex format of a Geometry. * @@ -40,67 +42,39 @@ * table. You may look the vertices of a Geometry as if they are stored in a * database table, and the VertexDescription defines the fields of the table. */ -public class VertexDescription { - - private double[] m_defaultPointAttributes; - private int[] m_pointAttributeOffsets; - int m_attributeCount; - int m_total_component_count; - - int[] m_semantics; - - int[] m_semanticsToIndexMap; - - int m_hash; +public final class VertexDescription { + /** + * Describes the attribute and, in case of predefined attributes, provides a + * hint of the attribute use. + */ + public interface Semantics { + static final int POSITION = 0; // xy coordinates of a point (2D + // vector of double, linear + // interpolation) - static double[] _defaultValues = { 0, 0, NumberUtils.NaN(), 0, 0, 0, 0, 0, - 0 }; + static final int Z = 1; // z coordinates of a point (double, + // linear interpolation) - static int[] _interpolation = { Interpolation.LINEAR, Interpolation.LINEAR, - Interpolation.LINEAR, Interpolation.NONE, Interpolation.ANGULAR, - Interpolation.LINEAR, Interpolation.LINEAR, Interpolation.LINEAR, - Interpolation.NONE, - }; + static final int M = 2; // m attribute (double, linear + // interpolation) - static int[] _persistence = { Persistence.enumDouble, - Persistence.enumDouble, Persistence.enumDouble, - Persistence.enumInt32, Persistence.enumFloat, - Persistence.enumFloat, Persistence.enumFloat, - Persistence.enumFloat, Persistence.enumInt32, - }; + static final int ID = 3; // id (int, no interpolation) - static int[] _persistencesize = { 4, 8, 4, 8, 1, 2 }; + static final int NORMAL = 4; // xyz coordinates of normal vector + // (float, angular interpolation) - static int[] _components = { 2, 1, 1, 1, 3, 1, 2, 3, 2, }; + static final int TEXTURE1D = 5; // u coordinates of texture + // (float, linear interpolation) - VertexDescription() { - m_attributeCount = 0; - m_total_component_count = 0; + static final int TEXTURE2D = 6; // uv coordinates of texture + // (float, linear interpolation) - } + static final int TEXTURE3D = 7; // uvw coordinates of texture + // (float, linear interpolation) - VertexDescription(int hashValue, VertexDescription other) { - m_attributeCount = other.m_attributeCount; - m_total_component_count = other.m_total_component_count; - m_semantics = other.m_semantics.clone(); - m_semanticsToIndexMap = other.m_semanticsToIndexMap.clone(); - m_hash = other.m_hash; + static final int ID2 = 8; // two component ID - // Prepare default values for the Point geometry. - m_pointAttributeOffsets = new int[getAttributeCount()]; - int offset = 0; - for (int i = 0; i < getAttributeCount(); i++) { - m_pointAttributeOffsets[i] = offset; - offset += getComponentCount(m_semantics[i]); - } - m_total_component_count = offset; - m_defaultPointAttributes = new double[offset]; - for (int i = 0; i < getAttributeCount(); i++) { - int components = getComponentCount(getSemantics(i)); - double dv = getDefaultValue(getSemantics(i)); - for (int icomp = 0; icomp < components; icomp++) - m_defaultPointAttributes[m_pointAttributeOffsets[i] + icomp] = dv; - } + static final int MAXSEMANTICS = 8; // the max semantics value } /** @@ -134,40 +108,6 @@ interface Persistence { public static final int enumInt16 = 5; }; - /** - * Describes the attribute and, in case of predefined attributes, provides a - * hint of the attribute use. - */ - public interface Semantics { - static final int POSITION = 0; // xy coordinates of a point (2D - // vector of double, linear - // interpolation) - - static final int Z = 1; // z coordinates of a point (double, - // linear interpolation) - - static final int M = 2; // m attribute (double, linear - // interpolation) - - static final int ID = 3; // id (int, no interpolation) - - static final int NORMAL = 4; // xyz coordinates of normal vector - // (float, angular interpolation) - - static final int TEXTURE1D = 5; // u coordinates of texture - // (float, linear interpolation) - - static final int TEXTURE2D = 6; // uv coordinates of texture - // (float, linear interpolation) - - static final int TEXTURE3D = 7; // uvw coordinates of texture - // (float, linear interpolation) - - static final int ID2 = 8; // two component ID - - static final int MAXSEMANTICS = 10; // the max semantics value - } - /** * Returns the attribute count of this description. The value is always * greater or equal to 1. The first attribute is always a POSITION. @@ -181,13 +121,10 @@ public final int getAttributeCount() { * * @param attributeIndex * The index of the attribute in the description. Max value is - * GetAttributeCount() - 1. + * getAttributeCount() - 1. */ public final int getSemantics(int attributeIndex) { - if (attributeIndex < 0 || attributeIndex > m_attributeCount) - throw new IllegalArgumentException(); - - return m_semantics[attributeIndex]; + return m_indexToSemantics[attributeIndex]; } /** @@ -249,20 +186,6 @@ public static int getComponentCount(int semantics) { return _components[semantics]; } - /** - * Returns True for integer persistence type. - */ - static boolean isIntegerPersistence(int persistence) { - return persistence < Persistence.enumInt32; - } - - /** - * Returns True for integer semantics type. - */ - static boolean isIntegerSemantics(int semantics) { - return isIntegerPersistence(getPersistence(semantics)); - } - /** * Returns True if the attribute with the given name and given set exists. * @@ -270,27 +193,40 @@ static boolean isIntegerSemantics(int semantics) { * The semantics of the attribute. */ public boolean hasAttribute(int semantics) { - return m_semanticsToIndexMap[semantics] >= 0; + return (m_semanticsBitArray & (1 << semantics)) != 0; + } + + /** + * Returns True if this vertex description includes all attributes from the + * src. + * + * @param src + * The Vertex_description to compare with. + * @return The function returns false, only when this description does not + * have some of the attribute that src has. + */ + public final boolean hasAttributesFrom(VertexDescription src) { + return (m_semanticsBitArray & src.m_semanticsBitArray) == src.m_semanticsBitArray; } /** * Returns True, if the vertex has Z attribute. */ - public boolean hasZ() { + public final boolean hasZ() { return hasAttribute(Semantics.Z); } /** * Returns True, if the vertex has M attribute. */ - public boolean hasM() { + public final boolean hasM() { return hasAttribute(Semantics.M); } /** * Returns True, if the vertex has ID attribute. */ - public boolean hasID() { + public final boolean hasID() { return hasAttribute(Semantics.ID); } @@ -302,15 +238,15 @@ public static double getDefaultValue(int semantics) { return _defaultValues[semantics]; } - int getPointAttributeOffset_(int attribute_index) { - return m_pointAttributeOffsets[attribute_index]; + int getPointAttributeOffset_(int attributeIndex) { + return m_pointAttributeOffsets[attributeIndex]; } /** * Returns the total component count. */ public int getTotalComponentCount() { - return m_total_component_count; + return m_totalComponentCount; } /** @@ -323,28 +259,19 @@ public static boolean isDefaultValue(int semantics, double v) { .doubleToInt64Bits(v); } - static int getPersistenceFromInt(int size) { - if (size == 4) - return Persistence.enumInt32; - else if (size == 8) - return Persistence.enumInt64; - else - throw new IllegalArgumentException(); + static boolean isIntegerPersistence(int persistence) { + return persistence >= Persistence.enumInt32; } + static boolean isIntegerSemantics(int semantics) { + return isIntegerPersistence(getPersistence(semantics)); + } + @Override public boolean equals(Object _other) { return (Object) this == _other; } - int calculateHashImpl() { - int v = NumberUtils.hash(m_semantics[0]); - for (int i = 1; i < m_attributeCount; i++) - v = NumberUtils.hash(v, m_semantics[i]); - - return v; // if attribute size is 1, it returns 0 - } - /** * * Returns a packed array of double representation of all ordinates of @@ -373,19 +300,81 @@ int _getPointAttributeOffsetFromSemantics(int semantics) { return m_pointAttributeOffsets[getAttributeIndex(semantics)]; } - int _getTotalComponents() { - return m_defaultPointAttributes.length; - } - @Override public int hashCode() { return m_hash; } int _getSemanticsImpl(int attributeIndex) { - return m_semantics[attributeIndex]; + return m_indexToSemantics[attributeIndex]; + } + + VertexDescription(int bitMask) { + m_semanticsBitArray = bitMask; + m_attributeCount = 0; + m_totalComponentCount = 0; + m_semanticsToIndexMap = new int[Semantics.MAXSEMANTICS + 1]; + Arrays.fill(m_semanticsToIndexMap, -1); + for (int i = 0, flag = 1, n = Semantics.MAXSEMANTICS + 1; i < n; i++) { + if ((bitMask & flag) != 0) { + m_semanticsToIndexMap[i] = m_attributeCount; + m_attributeCount++; + int comps = getComponentCount(i); + m_totalComponentCount += comps; + } + + flag <<= 1; + } + + m_indexToSemantics = new int[m_attributeCount]; + for (int i = 0, n = Semantics.MAXSEMANTICS + 1; i < n; i++) { + int attrib = m_semanticsToIndexMap[i]; + if (attrib >= 0) + m_indexToSemantics[attrib] = i; + } + + m_defaultPointAttributes = new double[m_totalComponentCount]; + m_pointAttributeOffsets = new int[m_attributeCount]; + int offset = 0; + for (int i = 0, n = m_attributeCount; i < n; i++) { + int semantics = getSemantics(i); + int comps = getComponentCount(semantics); + double v = getDefaultValue(semantics); + m_pointAttributeOffsets[i] = offset; + for (int icomp = 0; icomp < comps; icomp++) { + m_defaultPointAttributes[offset] = v; + offset++; + } + } + + m_hash = NumberUtils.hash(m_semanticsBitArray); } - // TODO: clone, equald, hashcode - whats really needed? + private int m_attributeCount; + int m_semanticsBitArray; //the main component + private int m_totalComponentCount; + private int m_hash; + + private int[] m_semanticsToIndexMap; + private int[] m_indexToSemantics; + private int[] m_pointAttributeOffsets; + private double[] m_defaultPointAttributes; + + static final double[] _defaultValues = { 0, 0, NumberUtils.NaN(), 0, 0, 0, + 0, 0, 0 }; + + static final int[] _interpolation = { Interpolation.LINEAR, + Interpolation.LINEAR, Interpolation.LINEAR, Interpolation.NONE, + Interpolation.ANGULAR, Interpolation.LINEAR, Interpolation.LINEAR, + Interpolation.LINEAR, Interpolation.NONE, }; + + static final int[] _persistence = { Persistence.enumDouble, + Persistence.enumDouble, Persistence.enumDouble, + Persistence.enumInt32, Persistence.enumFloat, + Persistence.enumFloat, Persistence.enumFloat, + Persistence.enumFloat, Persistence.enumInt32, }; + + static final int[] _persistencesize = { 4, 8, 4, 8, 1, 2 }; + static final int[] _components = { 2, 1, 1, 1, 3, 1, 2, 3, 2, }; } diff --git a/src/main/java/com/esri/core/geometry/VertexDescriptionDesignerImpl.java b/src/main/java/com/esri/core/geometry/VertexDescriptionDesignerImpl.java index 268b75bb..c6d69b15 100644 --- a/src/main/java/com/esri/core/geometry/VertexDescriptionDesignerImpl.java +++ b/src/main/java/com/esri/core/geometry/VertexDescriptionDesignerImpl.java @@ -32,188 +32,53 @@ * This factory class allows to describe and create a VertexDescription * instance. */ -class VertexDescriptionDesignerImpl extends VertexDescription { - - /** - * Designer default constructor produces XY vertex description (POSITION - * semantics only). - */ - public VertexDescriptionDesignerImpl() { - super(); - m_semantics = new int[Semantics.MAXSEMANTICS]; - m_semantics[0] = Semantics.POSITION; - m_attributeCount = 1; - - m_semanticsToIndexMap = new int[Semantics.MAXSEMANTICS]; - - for (int i = 0; i < Semantics.MAXSEMANTICS; i++) - m_semanticsToIndexMap[i] = -1; - - m_semanticsToIndexMap[m_semantics[0]] = 0; - - m_bModified = true; - } - - /** - * Creates description designer and initializes it from the given - * description. Use this to add or remove attributes from the description. - */ - public VertexDescriptionDesignerImpl(VertexDescription other) { - super(other.hashCode(), other); - m_bModified = true; - } - - /** - * Adds a new attribute to the VertexDescription. - * - * @param semantics - * Attribute semantics. - */ - public void addAttribute(int semantics) { - if (hasAttribute(semantics)) - return; +final class VertexDescriptionDesignerImpl { + static VertexDescription getVertexDescription(int descriptionBitMask) { + return VertexDescriptionHash.getInstance() + .FindOrAdd(descriptionBitMask); + } + + static VertexDescription getMergedVertexDescription( + VertexDescription descr1, VertexDescription descr2) { + int mask = descr1.m_semanticsBitArray | descr2.m_semanticsBitArray; + if ((mask & descr1.m_semanticsBitArray) == mask) { + return descr1; + } else if ((mask & descr2.m_semanticsBitArray) == mask) { + return descr2; + } - m_semanticsToIndexMap[semantics] = 0;// assign a value >= 0 to mark it - // as existing - _initMapping(); + return getVertexDescription(mask); } - /** - * Removes given attribute. - * - * @param semantics - * Attribute semantics. - */ - void removeAttribute(int semantics) { - - if (semantics == Semantics.POSITION) - throw new IllegalArgumentException( - "Position attribue cannot be removed");// not allowed to - // remove the xy - - if (!hasAttribute(semantics)) - return; + static VertexDescription getMergedVertexDescription( + VertexDescription descr, int semantics) { + int mask = descr.m_semanticsBitArray | (1 << semantics); + if ((mask & descr.m_semanticsBitArray) == mask) { + return descr; + } - m_semanticsToIndexMap[semantics] = -1;// assign a value < 0 to mark it - // as removed - _initMapping(); + return getVertexDescription(mask); } - /** - * Removes all attributes from the designer with exception of the POSITION - * attribute. - */ - public void reset() { - m_semantics[0] = Semantics.POSITION; - m_attributeCount = 1; - - for (int i : m_semanticsToIndexMap) - m_semanticsToIndexMap[i] = -1; - - m_semanticsToIndexMap[m_semantics[0]] = 0; - m_bModified = true; - } + static VertexDescription removeSemanticsFromVertexDescription( + VertexDescription descr, int semanticsToRemove) { + int mask = (descr.m_semanticsBitArray | (1 << (int) semanticsToRemove)) + - (1 << (int) semanticsToRemove); + if (mask == descr.m_semanticsBitArray) { + return descr; + } - /** - * Returns a VertexDescription corresponding to the vertex design.
- * Note: the same instance of VertexDescription will be returned each time - * for the same same set of attributes and attribute properties.
- * The method searches for the VertexDescription in a global hash table. If - * found, it is returned. Else, a new instance of the VertexDescription is - * added to the has table and returned. - */ - public VertexDescription getDescription() { - VertexDescriptionHash vdhash = VertexDescriptionHash.getInstance(); - VertexDescriptionDesignerImpl vdd = this; - return vdhash.add(vdd); + return getVertexDescription(mask); } - /** - * Returns a default VertexDescription that has X and Y coordinates only. - */ static VertexDescription getDefaultDescriptor2D() { - VertexDescriptionHash vdhash = VertexDescriptionHash.getInstance(); - VertexDescription vd = vdhash.getVD2D(); - return vd; + return VertexDescriptionHash.getInstance().getVD2D(); } - /** - * Returns a default VertexDescription that has X, Y, and Z coordinates only - */ static VertexDescription getDefaultDescriptor3D() { - VertexDescriptionHash vdhash = VertexDescriptionHash.getInstance(); - VertexDescription vd = vdhash.getVD3D(); - return vd; - } - - VertexDescription _createInternal() { - int hash = hashCode(); - VertexDescription vd = new VertexDescription(hash, this); - return vd; - } - - protected boolean m_bModified; - - protected void _initMapping() { - m_attributeCount = 0; - for (int i = 0, j = 0; i < Semantics.MAXSEMANTICS; i++) { - if (m_semanticsToIndexMap[i] >= 0) { - m_semantics[j] = i; - m_semanticsToIndexMap[i] = j; - j++; - m_attributeCount++; - } - } - - m_bModified = true; - } - - @Override - public int hashCode() { - if (m_bModified) { - m_hash = calculateHashImpl(); - m_bModified = false; - } - - return m_hash; - } - - @Override - public boolean equals(Object _other) { - if (_other == null) - return false; - if (_other == this) - return true; - if (_other.getClass() != getClass()) - return false; - VertexDescriptionDesignerImpl other = (VertexDescriptionDesignerImpl) (_other); - if (other.getAttributeCount() != getAttributeCount()) - return false; - - for (int i = 0; i < m_attributeCount; i++) { - if (m_semantics[i] != other.m_semantics[i]) - return false; - } - if (m_bModified != other.m_bModified) - return false; - - return true; + return VertexDescriptionHash.getInstance().getVD3D(); } - public boolean isDesignerFor(VertexDescription vd) { - if (vd.getAttributeCount() != getAttributeCount()) - return false; - - for (int i = 0; i < m_attributeCount; i++) { - if (m_semantics[i] != vd.m_semantics[i]) - return false; - } - - return true; - } - - // returns a mapping from the source attribute indices to the destination - // attribute indices. static int[] mapAttributes(VertexDescription src, VertexDescription dest) { int[] srcToDst = new int[src.getAttributeCount()]; Arrays.fill(srcToDst, -1); @@ -222,41 +87,4 @@ static int[] mapAttributes(VertexDescription src, VertexDescription dest) { } return srcToDst; } - - static VertexDescription getMergedVertexDescription(VertexDescription src, - int semanticsToAdd) { - VertexDescriptionDesignerImpl vdd = new VertexDescriptionDesignerImpl( - src); - vdd.addAttribute(semanticsToAdd); - return vdd.getDescription(); - } - - static VertexDescription getMergedVertexDescription(VertexDescription d1, VertexDescription d2) { - VertexDescriptionDesignerImpl vdd = null; - for (int semantics = Semantics.POSITION; semantics < Semantics.MAXSEMANTICS; semantics++) { - if (!d1.hasAttribute(semantics) && d2.hasAttribute(semantics)) { - if (vdd == null) { - vdd = new VertexDescriptionDesignerImpl(d1); - } - - vdd.addAttribute(semantics); - } - } - - if (vdd != null) { - return vdd.getDescription(); - } - - return d1; - } - - static VertexDescription removeSemanticsFromVertexDescription( - VertexDescription src, int semanticsToRemove) { - VertexDescriptionDesignerImpl vdd = new VertexDescriptionDesignerImpl( - src); - vdd.removeAttribute(semanticsToRemove); - return vdd.getDescription(); - } - } - diff --git a/src/main/java/com/esri/core/geometry/VertexDescriptionHash.java b/src/main/java/com/esri/core/geometry/VertexDescriptionHash.java index dfe372aa..8e12dfec 100644 --- a/src/main/java/com/esri/core/geometry/VertexDescriptionHash.java +++ b/src/main/java/com/esri/core/geometry/VertexDescriptionHash.java @@ -25,9 +25,11 @@ package com.esri.core.geometry; import com.esri.core.geometry.VertexDescription.Semantics; + import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A hash object singleton that stores all VertexDescription instances via @@ -35,75 +37,45 @@ * VertexDescription instances to prevent duplicates. */ final class VertexDescriptionHash { - Map> map = new HashMap>(); - - private static VertexDescription m_vd2D; + HashMap m_map = new HashMap(); - private static VertexDescription m_vd3D; + private static VertexDescription m_vd2D = new VertexDescription(1); + private static VertexDescription m_vd3D = new VertexDescription(3); private static final VertexDescriptionHash INSTANCE = new VertexDescriptionHash(); private VertexDescriptionHash() { - VertexDescriptionDesignerImpl vdd2D = new VertexDescriptionDesignerImpl(); - add(vdd2D); - VertexDescriptionDesignerImpl vdd3D = new VertexDescriptionDesignerImpl(); - vdd3D.addAttribute(Semantics.Z); - add(vdd3D); + m_map.put(1, m_vd2D); + m_map.put(3, m_vd3D); } public static VertexDescriptionHash getInstance() { return INSTANCE; } - public VertexDescription getVD2D() { + public final VertexDescription getVD2D() { return m_vd2D; } - public VertexDescription getVD3D() { + public final VertexDescription getVD3D() { return m_vd3D; } - synchronized public VertexDescription add(VertexDescriptionDesignerImpl vdd) { - // Firstly quick test for 2D/3D descriptors. - int h = vdd.hashCode(); - - if ((m_vd2D != null) && m_vd2D.hashCode() == h) { - if (vdd.isDesignerFor(m_vd2D)) - return m_vd2D; - } - - if ((m_vd3D != null) && (m_vd3D.hashCode() == h)) { - if (vdd.isDesignerFor(m_vd3D)) - return m_vd3D; - } - - // Now search in the hash. - - VertexDescription vd = null; - if (map.containsKey(h)) { - WeakReference vdweak = map.get(h); - vd = vdweak.get(); - if (vd == null) // GC'd VertexDescription - map.remove(h); - } - - if (vd == null) { // either not in map to begin with, or has been GC'd - vd = vdd._createInternal(); - - if (vd.getAttributeCount() == 1) { - m_vd2D = vd; - } else if ((vd.getAttributeCount() == 2) - && (vd.getSemantics(1) == Semantics.Z)) { - m_vd3D = vd; - } else { - WeakReference vdweak = new WeakReference( - vd); - - map.put(h, vdweak); + public final VertexDescription FindOrAdd(int bitSet) { + if (bitSet == 1) + return m_vd2D; + if (bitSet == 3) + return m_vd3D; + + synchronized (this) { + VertexDescription vd = m_map.get(bitSet); + if (vd == null) { + vd = new VertexDescription(bitSet); + m_map.put(bitSet, vd); } + return vd; } - - return vd; } + } diff --git a/src/main/java/com/esri/core/geometry/WktParser.java b/src/main/java/com/esri/core/geometry/WktParser.java index c3b4894e..7f79ed5f 100644 --- a/src/main/java/com/esri/core/geometry/WktParser.java +++ b/src/main/java/com/esri/core/geometry/WktParser.java @@ -222,7 +222,7 @@ private void geometry_() { m_function_stack.removeLast(); if (m_start_token + 5 <= m_wkt_string.length() - && m_wkt_string.regionMatches(true, m_start_token, "points", 0, + && m_wkt_string.regionMatches(true, m_start_token, "point", 0, 5)) { m_end_token = m_start_token + 5; m_current_token_type = WktToken.point; diff --git a/src/main/java/com/esri/core/geometry/ogc/OGCConcreteGeometryCollection.java b/src/main/java/com/esri/core/geometry/ogc/OGCConcreteGeometryCollection.java index 4d91d7e2..1e4cb7be 100644 --- a/src/main/java/com/esri/core/geometry/ogc/OGCConcreteGeometryCollection.java +++ b/src/main/java/com/esri/core/geometry/ogc/OGCConcreteGeometryCollection.java @@ -3,8 +3,11 @@ import com.esri.core.geometry.Envelope; import com.esri.core.geometry.Geometry; import com.esri.core.geometry.GeometryCursor; +import com.esri.core.geometry.NumberUtils; import com.esri.core.geometry.Polygon; import com.esri.core.geometry.SpatialReference; +import com.esri.core.geometry.GeoJsonExportFlags; +import com.esri.core.geometry.OperatorExportToGeoJson; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; @@ -147,6 +150,40 @@ public ByteBuffer asBinary() { return wkbBuffer; } + @Override + public String asGeoJson() { + return asGeoJsonImpl(GeoJsonExportFlags.geoJsonExportDefaults); + } + + @Override + String asGeoJsonImpl(int export_flags) { + StringBuilder sb = new StringBuilder(); + + sb.append("{\"type\":\"GeometryCollection\",\"geometries\":"); + + sb.append("["); + for (int i = 0, n = numGeometries(); i < n; i++) { + if (i > 0) + sb.append(","); + + if (geometryN(i) != null) + sb.append(geometryN(i).asGeoJsonImpl(GeoJsonExportFlags.geoJsonExportSkipCRS)); + } + + sb.append("],\"crs\":"); + + if (esriSR != null) { + String crs_value = OperatorExportToGeoJson.local().exportSpatialReference(0, esriSR); + sb.append(crs_value); + } else { + sb.append("\"null\""); + } + + sb.append("}"); + + return sb.toString(); + } + @Override public boolean isEmpty() { return numGeometries() == 0; @@ -314,8 +351,7 @@ public void setSpatialReference(SpatialReference esriSR_) { } @Override - public OGCGeometry convertToMulti() - { + public OGCGeometry convertToMulti() { return this; } @@ -323,4 +359,46 @@ public OGCGeometry convertToMulti() public String asJson() { throw new UnsupportedOperationException(); } + + @Override + public boolean equals(Object other) { + if (other == null) + return false; + + if (other == this) + return true; + + if (other.getClass() != getClass()) + return false; + + OGCConcreteGeometryCollection another = (OGCConcreteGeometryCollection)other; + if (geometries != null) { + if (!geometries.equals(another.geometries)) + return false; + } + else if (another.geometries != null) + return false; + + if (esriSR == another.esriSR) { + return true; + } + + if (esriSR != null && another.esriSR != null) { + return esriSR.equals(another.esriSR); + } + + return false; + } + + @Override + public int hashCode() { + int hash = 1; + if (geometries != null) + hash = geometries.hashCode(); + + if (esriSR != null) + hash = NumberUtils.hashCombine(hash, esriSR.hashCode()); + + return hash; + } } diff --git a/src/main/java/com/esri/core/geometry/ogc/OGCGeometry.java b/src/main/java/com/esri/core/geometry/ogc/OGCGeometry.java index 9783bb94..c0a93ce2 100644 --- a/src/main/java/com/esri/core/geometry/ogc/OGCGeometry.java +++ b/src/main/java/com/esri/core/geometry/ogc/OGCGeometry.java @@ -12,6 +12,7 @@ import com.esri.core.geometry.Envelope; import com.esri.core.geometry.Envelope1D; +import com.esri.core.geometry.GeoJsonExportFlags; import com.esri.core.geometry.Geometry; import com.esri.core.geometry.GeometryCursor; import com.esri.core.geometry.GeometryCursorAppend; @@ -19,6 +20,7 @@ import com.esri.core.geometry.MapGeometry; import com.esri.core.geometry.MapOGCStructure; import com.esri.core.geometry.MultiPoint; +import com.esri.core.geometry.NumberUtils; import com.esri.core.geometry.OGCStructure; import com.esri.core.geometry.Operator; import com.esri.core.geometry.OperatorBuffer; @@ -93,7 +95,12 @@ public ByteBuffer asBinary() { public String asGeoJson() { OperatorExportToGeoJson op = (OperatorExportToGeoJson) OperatorFactoryLocal .getInstance().getOperator(Operator.Type.ExportToGeoJson); - return op.execute(getEsriGeometry()); + return op.execute(esriSR, getEsriGeometry()); + } + + String asGeoJsonImpl(int export_flags) { + OperatorExportToGeoJson op = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + return op.execute(export_flags, esriSR, getEsriGeometry()); } /** @@ -489,7 +496,7 @@ public static OGCGeometry fromEsriShape(ByteBuffer buffer) { } public static OGCGeometry fromJson(String string) - throws JsonParseException, IOException { + throws Exception { JsonFactory factory = new JsonFactory(); JsonParser jsonParserPt = factory.createJsonParser(string); jsonParserPt.nextToken(); @@ -498,7 +505,8 @@ public static OGCGeometry fromJson(String string) mapGeom.getSpatialReference()); } - public static OGCGeometry fromGeoJson(String string) throws JSONException { + public static OGCGeometry fromGeoJson(String string) + throws Exception { OperatorImportFromGeoJson op = (OperatorImportFromGeoJson) OperatorFactoryLocal .getInstance().getOperator(Operator.Type.ImportFromGeoJson); MapOGCStructure mapOGCStructure = op.executeOGC(0, string, null); @@ -679,4 +687,51 @@ public String toString() { return String .format("%s: %s", this.getClass().getSimpleName(), snippet); } -} \ No newline at end of file + + @Override + public boolean equals(Object other) { + if (other == null) + return false; + + if (other == this) + return true; + + if (other.getClass() != getClass()) + return false; + + OGCGeometry another = (OGCGeometry)other; + com.esri.core.geometry.Geometry geom1 = getEsriGeometry(); + com.esri.core.geometry.Geometry geom2 = another.getEsriGeometry(); + + if (geom1 == null) { + if (geom2 != null) + return false; + } + else if (!geom1.equals(geom2)) { + return false; + } + + if (esriSR == another.esriSR) { + return true; + } + + if (esriSR != null && another.esriSR != null) { + return esriSR.equals(another.esriSR); + } + + return false; + } + + @Override + public int hashCode() { + int hash = 1; + com.esri.core.geometry.Geometry geom1 = getEsriGeometry(); + if (geom1 != null) + hash = geom1.hashCode(); + + if (esriSR != null) + hash = NumberUtils.hashCombine(hash, esriSR.hashCode()); + + return hash; + } +} diff --git a/src/main/resources/com/esri/core/geometry/new_to_old_wkid.txt b/src/main/resources/com/esri/core/geometry/new_to_old_wkid.txt index 7cb8d997..86477d47 100644 --- a/src/main/resources/com/esri/core/geometry/new_to_old_wkid.txt +++ b/src/main/resources/com/esri/core/geometry/new_to_old_wkid.txt @@ -284,6 +284,7 @@ 3775 102186 3776 102187 3777 102188 +3785 102113 3800 102183 3801 102189 3812 102199 diff --git a/src/main/resources/com/esri/core/geometry/pcs_id_to_tolerance.txt b/src/main/resources/com/esri/core/geometry/pcs_id_to_tolerance.txt index f38609e5..e4386fc5 100644 --- a/src/main/resources/com/esri/core/geometry/pcs_id_to_tolerance.txt +++ b/src/main/resources/com/esri/core/geometry/pcs_id_to_tolerance.txt @@ -3126,6 +3126,7 @@ 102110 3 102111 3 102112 3 +102113 3 102114 3 102115 3 102116 3 diff --git a/src/test/java/com/esri/core/geometry/TestClip.java b/src/test/java/com/esri/core/geometry/TestClip.java index 0149d482..9653e5fa 100644 --- a/src/test/java/com/esri/core/geometry/TestClip.java +++ b/src/test/java/com/esri/core/geometry/TestClip.java @@ -107,7 +107,7 @@ public static void testClipGeometries() { } } - public static Polygon makePolygon() { + static Polygon makePolygon() { Polygon poly = new Polygon(); poly.startPath(0, 0); poly.lineTo(10, 10); @@ -116,7 +116,7 @@ public static Polygon makePolygon() { return poly; } - public static Polyline makePolyline() { + static Polyline makePolyline() { Polyline poly = new Polyline(); poly.startPath(0, 0); poly.lineTo(10, 10); @@ -183,7 +183,7 @@ public static void testArcObjectsFailureCR196492() { // ((MultiPathImpl::SPtr)clippedPolygon._GetImpl()).SaveToTextFileDbg("c:\\temp\\test_ArcObjects_failure_CR196492.txt"); } - public static Polyline makePolylineCR() { + static Polyline makePolylineCR() { Polyline polyline = new Polyline(); polyline.startPath(-200, -90); @@ -197,7 +197,7 @@ public static Polyline makePolylineCR() { return polyline; } - public static MultiPoint makeMultiPoint() { + static MultiPoint makeMultiPoint() { MultiPoint mpoint = new MultiPoint(); Point2D pt1 = new Point2D(); @@ -219,7 +219,7 @@ public static MultiPoint makeMultiPoint() { return mpoint; } - public static Point makePoint() { + static Point makePoint() { Point point = new Point(); Point2D pt = new Point2D(); diff --git a/src/test/java/com/esri/core/geometry/TestConvexHull.java b/src/test/java/com/esri/core/geometry/TestConvexHull.java index 72b6198a..62c59c2f 100644 --- a/src/test/java/com/esri/core/geometry/TestConvexHull.java +++ b/src/test/java/com/esri/core/geometry/TestConvexHull.java @@ -14,12 +14,45 @@ protected void tearDown() throws Exception { super.tearDown(); } + @Test + public static void testFewPoints() { + { + Polygon polygon = new Polygon(); + polygon.addPath((Point2D[]) null, 0, true); + polygon.insertPoint(0, -1, Point2D.construct(5, 5)); + + Point convex_hull = (Point) OperatorConvexHull.local().execute(polygon, null); + assertTrue(convex_hull.getXY().equals(Point2D.construct(5, 5))); + } + + { + Point2D[] pts = new Point2D[3]; + + pts[0] = Point2D.construct(0, 0); + pts[1] = Point2D.construct(0, 0); + pts[2] = Point2D.construct(0, 0); + + int[] out_pts = new int[3]; + int res = ConvexHull.construct(pts, 3, out_pts); + assertTrue(res == 1); + assertTrue(out_pts[0] == 0); + } + + { + Point2D[] pts = new Point2D[1]; + pts[0] = Point2D.construct(0, 0); + + int[] out_pts = new int[1]; + int res = ConvexHull.construct(pts, 1, out_pts); + assertTrue(res == 1); + assertTrue(out_pts[0] == 0); + } + } + @Test public static void testDegenerate() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); { Polygon polygon = new Polygon(); @@ -38,7 +71,7 @@ public static void testDegenerate() { polygon.lineTo(3, 0); Polygon densified = (Polygon) (densify.execute(polygon, .5, null)); - Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); + Polyline convex_hull = (Polyline) (bounding.execute(densified, null)); assertTrue(bounding.isConvex(convex_hull, null)); assertTrue(convex_hull.calculateArea2D() == 0.0); @@ -232,21 +265,19 @@ public static void testDegenerate() { mpoint.add(4, 4); mpoint.add(4, 4); - Polygon convex_hull = (Polygon) (bounding.execute(mpoint, null)); - assertTrue(convex_hull.getPointCount() == 2); + Point convex_hull = (Point) bounding.execute(mpoint, null); assertTrue(convex_hull.calculateArea2D() == 0.0); assertTrue(bounding.isConvex(convex_hull, null)); + assertTrue(convex_hull.getXY().equals(Point2D.construct(4, 4))); } { MultiPoint mpoint = new MultiPoint(); mpoint.add(4, 4); - MultiPoint convex_hull = (MultiPoint) (bounding.execute(mpoint, - null)); - assertTrue(convex_hull.getPointCount() == 1); + Point convex_hull = (Point) bounding.execute(mpoint, null); assertTrue(bounding.isConvex(convex_hull, null)); - assertTrue(convex_hull == mpoint); + assertTrue(convex_hull.getXY().equals(Point2D.construct(4, 4))); } { @@ -254,7 +285,7 @@ public static void testDegenerate() { mpoint.add(4, 4); mpoint.add(4, 5); - Polyline convex_hull = (Polyline) (bounding.execute(mpoint, null)); + Polyline convex_hull = (Polyline) bounding.execute(mpoint, null); assertTrue(convex_hull.getPointCount() == 2); assertTrue(bounding.isConvex(convex_hull, null)); assertTrue(convex_hull.calculateLength2D() == 1.0); @@ -265,7 +296,7 @@ public static void testDegenerate() { line.setStartXY(0, 0); line.setEndXY(0, 1); - Polyline convex_hull = (Polyline) (bounding.execute(line, null)); + Polyline convex_hull = (Polyline) bounding.execute(line, null); assertTrue(convex_hull.getPointCount() == 2); assertTrue(bounding.isConvex(convex_hull, null)); assertTrue(convex_hull.calculateLength2D() == 1.0); @@ -276,7 +307,7 @@ public static void testDegenerate() { polyline.startPath(0, 0); polyline.lineTo(0, 1); - Polyline convex_hull = (Polyline) (bounding.execute(polyline, null)); + Polyline convex_hull = (Polyline) bounding.execute(polyline, null); assertTrue(convex_hull.getPointCount() == 2); assertTrue(bounding.isConvex(convex_hull, null)); assertTrue(polyline == convex_hull); @@ -285,23 +316,81 @@ public static void testDegenerate() { { Envelope env = new Envelope(0, 0, 10, 10); + assertTrue(OperatorConvexHull.local().isConvex(env, null)); + + Polygon convex_hull = (Polygon) bounding.execute(env, null); + assertTrue(bounding.isConvex(convex_hull, null)); + assertTrue(convex_hull.getPointCount() == 4); + assertTrue(convex_hull.getXY(0).equals(Point2D.construct(0, 0))); + assertTrue(convex_hull.getXY(1).equals(Point2D.construct(0, 10))); + assertTrue(convex_hull.getXY(2).equals(Point2D.construct(10, 10))); + assertTrue(convex_hull.getXY(3).equals(Point2D.construct(10, 0))); + } - Envelope convex_hull = (Envelope) (bounding.execute(env, null)); + { + Envelope env = new Envelope(0, 0, 0, 10); + assertTrue(!OperatorConvexHull.local().isConvex(env, null)); + + Polyline convex_hull = (Polyline) bounding.execute(env, null); assertTrue(bounding.isConvex(convex_hull, null)); - assertTrue(env == convex_hull); + assertTrue(convex_hull.getPointCount() == 2); + assertTrue(convex_hull.getXY(0).equals(Point2D.construct(0, 0))); + assertTrue(convex_hull.getXY(1).equals(Point2D.construct(0, 10))); + } + + { + Envelope env = new Envelope(0, 0, 0, 10); + assertTrue(!OperatorConvexHull.local().isConvex(env, null)); + + Polyline convex_hull = (Polyline) bounding.execute(env, null); + assertTrue(bounding.isConvex(convex_hull, null)); + assertTrue(convex_hull.getPointCount() == 2); + assertTrue(convex_hull.getXY(0).equals(Point2D.construct(0, 0))); + assertTrue(convex_hull.getXY(1).equals(Point2D.construct(0, 10))); + } + + { + Envelope env = new Envelope(5, 5, 5, 5); + assertTrue(!OperatorConvexHull.local().isConvex(env, null)); + + Point convex_hull = (Point) bounding.execute(env, null); + assertTrue(bounding.isConvex(convex_hull, null)); + assertTrue(convex_hull.getXY().equals(Point2D.construct(5, 5))); } } + @Test + public static void testSegment() { + { + Line line = new Line(); + line.setStartXY(5, 5); + line.setEndXY(5, 5); + + assertTrue(!OperatorConvexHull.local().isConvex(line, null)); + Point point = (Point) OperatorConvexHull.local().execute(line, null); + assertTrue(point.getXY().equals(Point2D.construct(5, 5))); + } + + { + Line line = new Line(); + line.setStartXY(5, 5); + line.setEndXY(5, 6); + + assertTrue(OperatorConvexHull.local().isConvex(line, null)); + Polyline polyline = (Polyline) OperatorConvexHull.local().execute(line, null); + assertTrue(polyline.getPointCount() == 2); + assertTrue(polyline.getXY(0).equals(Point2D.construct(5, 5))); + assertTrue(polyline.getXY(1).equals(Point2D.construct(5, 6))); + } + } + + @Test public static void testSquare() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); - OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Difference); - OperatorContains contains = (OperatorContains) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Contains); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Difference); + OperatorContains contains = (OperatorContains) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Contains); Polygon square = new Polygon(); square.startPath(0, 0); @@ -330,9 +419,13 @@ public static void testSquare() { square.lineTo(2, 1); Polygon densified = (Polygon) (densify.execute(square, 1.0, null)); + + densified.addAttribute(VertexDescription.Semantics.ID); + for (int i = 0; i < densified.getPointCount(); i++) + densified.setAttribute(VertexDescription.Semantics.ID, i, 0, i); + Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polygon differenced = (Polygon) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); @@ -340,32 +433,23 @@ public static void testSquare() { @Test public static void testPolygons() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); - OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Difference); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Difference); { - Polygon shape = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[1.3734426370715553,-90],[-24.377532184629967,-63.428606327053856],[-25.684686546621553,90],[-24.260574484321914,80.526315789473699],[-25.414389575040037,90],[-23.851448513708718,90],[-23.100135788742072,87.435887853000679],[5.6085096351011448,-48.713222410606306],[1.3734426370715553,-90]]]}") - .getGeometry()); + Polygon shape = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[1.3734426370715553,-90],[-24.377532184629967,-63.428606327053856],[-25.684686546621553,90],[-24.260574484321914,80.526315789473699],[-25.414389575040037,90],[-23.851448513708718,90],[-23.100135788742072,87.435887853000679],[5.6085096351011448,-48.713222410606306],[1.3734426370715553,-90]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(shape, null)); - Polygon differenced = (Polygon) (difference.execute(shape, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(shape, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-179.64843749999693,-43.3535476539993],[-179.99999999999696,-43.430006211999284],[-179.99999999999696,39.329644416999436],[-179.64843749999693,38.98862638799943],[-89.99999999999838,29.008084980999506],[-112.8515624999981,-16.20113770599962],[-115.66406249999804,-18.882554574999688],[-124.80468749999788,-23.7925511709996],[-138.86718749999767,-30.6635901109995],[-157.49999999999736,-38.468358112999354],[-162.42187499999724,-39.56498442199932],[-179.64843749999693,-43.3535476539993]],[[179.99999999999696,-43.430006211999284],[179.64843749999693,-43.50646476999926],[162.0703124999973,-42.36267115399919],[160.3124999999973,-42.24790485699929],[143.78906249999756,-41.1680427339993],[138.16406249999767,-39.64744846799925],[98.43749999999845,-28.523889212999524],[78.39843749999878,-5.1644422999998705],[75.9374999999988,19.738611663999766],[88.2421874999986,33.51651305599954],[108.63281249999815,44.160795160999356],[138.16406249999767,51.02062617799914],[140.9765624999976,51.68129673399923],[160.3124999999973,52.8064856429991],[162.0703124999973,52.908902047999206],[163.12499999999727,52.97036560499911],[165.93749999999716,52.97036560499911],[179.99999999999696,39.329644416999436],[179.99999999999696,-43.430006211999284]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-179.64843749999693,-43.3535476539993],[-179.99999999999696,-43.430006211999284],[-179.99999999999696,39.329644416999436],[-179.64843749999693,38.98862638799943],[-89.99999999999838,29.008084980999506],[-112.8515624999981,-16.20113770599962],[-115.66406249999804,-18.882554574999688],[-124.80468749999788,-23.7925511709996],[-138.86718749999767,-30.6635901109995],[-157.49999999999736,-38.468358112999354],[-162.42187499999724,-39.56498442199932],[-179.64843749999693,-43.3535476539993]],[[179.99999999999696,-43.430006211999284],[179.64843749999693,-43.50646476999926],[162.0703124999973,-42.36267115399919],[160.3124999999973,-42.24790485699929],[143.78906249999756,-41.1680427339993],[138.16406249999767,-39.64744846799925],[98.43749999999845,-28.523889212999524],[78.39843749999878,-5.1644422999998705],[75.9374999999988,19.738611663999766],[88.2421874999986,33.51651305599954],[108.63281249999815,44.160795160999356],[138.16406249999767,51.02062617799914],[140.9765624999976,51.68129673399923],[160.3124999999973,52.8064856429991],[162.0703124999973,52.908902047999206],[163.12499999999727,52.97036560499911],[165.93749999999716,52.97036560499911],[179.99999999999696,39.329644416999436],[179.99999999999696,-43.430006211999284]]]}").getGeometry()); Polygon densified = (Polygon) (densify.execute(polygon, 10.0, null)); Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polygon differenced = (Polygon) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } @@ -376,196 +460,144 @@ public static void testPolygons() { polygon.lineTo(-1, 0); polygon.lineTo(0, -1); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-38.554566833914528,21.902000764339238],[-30.516168471666138,90],[-38.554566833914528,21.902000764339238]],[[-43.013227444613932,28.423410187206883],[-43.632473335895916,90],[-42.342268693420237,62.208637129146894],[-37.218731802058755,63.685357222187029],[-32.522681335230686,47.080307818055296],[-40.537308829621097,-21.881392019745604],[-47.59510451722663,18.812521648505964],[-53.25344489340366,30.362745244224911],[-46.629060462410138,90],[-50.069277433245119,18.254168921734287],[-42.171214434397982,72.623347387008081],[-43.000844452530551,90],[-46.162281544954659,90],[-39.462049205071331,90],[-47.434856316742902,38.662565208814371],[-52.13115779642537,-19.952586632199857],[-56.025328966335081,90],[-60.056846215416158,-44.023645282268355],[-60.12338894192289,50.374596189881942],[-35.787508034048379,-7.8839007676038513],[-60.880218074135605,-46.447995750907815],[-67.782542852117956,-85.106300958016107],[-65.053131764313761,-0.96651520578494665],[-72.375821140304154,90],[-78.561502106749245,90],[-83.809168672565946,33.234498214085811],[-60.880218054506344,-46.447995733653201],[-75.637095425108981,59.886574792622838],[-71.364085965028096,31.976373491332097],[-67.89968380886117,90],[-67.544349171474749,8.8435794458927504],[-70.780047377934707,80.683454463576624],[-64.996733940204948,34.349882797035313],[-56.631753638680905,39.815838152456926],[-60.392350183516896,52.75446132093407],[-58.51633728692137,90],[-64.646972065627097,41.444197803942579],[-73.355591244695518,-0.15370205145035776],[-43.013227444613932,28.423410187206883]],[[-69.646471076946,-85.716191379686904],[-62.854465128320491,-45.739046580967972],[-71.377481570643141,-90],[-66.613495837251435,-90],[-66.9765142407159,-90],[-66.870099169607329,-90],[-67.23180828626819,-61.248439074609649],[-58.889775875438851,-90],[-53.391995883729322,-69.476385967096491],[-69.646471076946,-85.716191379686904]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-38.554566833914528,21.902000764339238],[-30.516168471666138,90],[-38.554566833914528,21.902000764339238]],[[-43.013227444613932,28.423410187206883],[-43.632473335895916,90],[-42.342268693420237,62.208637129146894],[-37.218731802058755,63.685357222187029],[-32.522681335230686,47.080307818055296],[-40.537308829621097,-21.881392019745604],[-47.59510451722663,18.812521648505964],[-53.25344489340366,30.362745244224911],[-46.629060462410138,90],[-50.069277433245119,18.254168921734287],[-42.171214434397982,72.623347387008081],[-43.000844452530551,90],[-46.162281544954659,90],[-39.462049205071331,90],[-47.434856316742902,38.662565208814371],[-52.13115779642537,-19.952586632199857],[-56.025328966335081,90],[-60.056846215416158,-44.023645282268355],[-60.12338894192289,50.374596189881942],[-35.787508034048379,-7.8839007676038513],[-60.880218074135605,-46.447995750907815],[-67.782542852117956,-85.106300958016107],[-65.053131764313761,-0.96651520578494665],[-72.375821140304154,90],[-78.561502106749245,90],[-83.809168672565946,33.234498214085811],[-60.880218054506344,-46.447995733653201],[-75.637095425108981,59.886574792622838],[-71.364085965028096,31.976373491332097],[-67.89968380886117,90],[-67.544349171474749,8.8435794458927504],[-70.780047377934707,80.683454463576624],[-64.996733940204948,34.349882797035313],[-56.631753638680905,39.815838152456926],[-60.392350183516896,52.75446132093407],[-58.51633728692137,90],[-64.646972065627097,41.444197803942579],[-73.355591244695518,-0.15370205145035776],[-43.013227444613932,28.423410187206883]],[[-69.646471076946,-85.716191379686904],[-62.854465128320491,-45.739046580967972],[-71.377481570643141,-90],[-66.613495837251435,-90],[-66.9765142407159,-90],[-66.870099169607329,-90],[-67.23180828626819,-61.248439074609649],[-58.889775875438851,-90],[-53.391995883729322,-69.476385967096491],[-69.646471076946,-85.716191379686904]]]}").getGeometry()); Polygon densified = (Polygon) (densify.execute(polygon, 10.0, null)); Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polygon differenced = (Polygon) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); // assertTrue(bounding.isConvex(*convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-38.554566833914528,21.902000764339238],[-30.516168471666138,90],[-38.554566833914528,21.902000764339238]],[[-43.013227444613932,28.423410187206883],[-43.632473335895916,90],[-42.342268693420237,62.208637129146894],[-37.218731802058755,63.685357222187029],[-32.522681335230686,47.080307818055296],[-40.537308829621097,-21.881392019745604],[-47.59510451722663,18.812521648505964],[-53.25344489340366,30.362745244224911],[-46.629060462410138,90],[-50.069277433245119,18.254168921734287],[-42.171214434397982,72.623347387008081],[-43.000844452530551,90],[-46.162281544954659,90],[-39.462049205071331,90],[-47.434856316742902,38.662565208814371],[-52.13115779642537,-19.952586632199857],[-56.025328966335081,90],[-60.056846215416158,-44.023645282268355],[-60.12338894192289,50.374596189881942],[-35.787508034048379,-7.8839007676038513],[-60.880218074135605,-46.447995750907815],[-67.782542852117956,-85.106300958016107],[-65.053131764313761,-0.96651520578494665],[-72.375821140304154,90],[-78.561502106749245,90],[-83.809168672565946,33.234498214085811],[-60.880218054506344,-46.447995733653201],[-75.637095425108981,59.886574792622838],[-71.364085965028096,31.976373491332097],[-67.89968380886117,90],[-67.544349171474749,8.8435794458927504],[-70.780047377934707,80.683454463576624],[-64.996733940204948,34.349882797035313],[-56.631753638680905,39.815838152456926],[-60.392350183516896,52.75446132093407],[-58.51633728692137,90],[-64.646972065627097,41.444197803942579],[-73.355591244695518,-0.15370205145035776],[-43.013227444613932,28.423410187206883]],[[-69.646471076946,-85.716191379686904],[-62.854465128320491,-45.739046580967972],[-71.377481570643141,-90],[-66.613495837251435,-90],[-66.9765142407159,-90],[-66.870099169607329,-90],[-67.23180828626819,-61.248439074609649],[-58.889775875438851,-90],[-53.391995883729322,-69.476385967096491],[-69.646471076946,-85.716191379686904]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-38.554566833914528,21.902000764339238],[-30.516168471666138,90],[-38.554566833914528,21.902000764339238]],[[-43.013227444613932,28.423410187206883],[-43.632473335895916,90],[-42.342268693420237,62.208637129146894],[-37.218731802058755,63.685357222187029],[-32.522681335230686,47.080307818055296],[-40.537308829621097,-21.881392019745604],[-47.59510451722663,18.812521648505964],[-53.25344489340366,30.362745244224911],[-46.629060462410138,90],[-50.069277433245119,18.254168921734287],[-42.171214434397982,72.623347387008081],[-43.000844452530551,90],[-46.162281544954659,90],[-39.462049205071331,90],[-47.434856316742902,38.662565208814371],[-52.13115779642537,-19.952586632199857],[-56.025328966335081,90],[-60.056846215416158,-44.023645282268355],[-60.12338894192289,50.374596189881942],[-35.787508034048379,-7.8839007676038513],[-60.880218074135605,-46.447995750907815],[-67.782542852117956,-85.106300958016107],[-65.053131764313761,-0.96651520578494665],[-72.375821140304154,90],[-78.561502106749245,90],[-83.809168672565946,33.234498214085811],[-60.880218054506344,-46.447995733653201],[-75.637095425108981,59.886574792622838],[-71.364085965028096,31.976373491332097],[-67.89968380886117,90],[-67.544349171474749,8.8435794458927504],[-70.780047377934707,80.683454463576624],[-64.996733940204948,34.349882797035313],[-56.631753638680905,39.815838152456926],[-60.392350183516896,52.75446132093407],[-58.51633728692137,90],[-64.646972065627097,41.444197803942579],[-73.355591244695518,-0.15370205145035776],[-43.013227444613932,28.423410187206883]],[[-69.646471076946,-85.716191379686904],[-62.854465128320491,-45.739046580967972],[-71.377481570643141,-90],[-66.613495837251435,-90],[-66.9765142407159,-90],[-66.870099169607329,-90],[-67.23180828626819,-61.248439074609649],[-58.889775875438851,-90],[-53.391995883729322,-69.476385967096491],[-69.646471076946,-85.716191379686904]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-36.269498017702901,-26.37490682626369],[-49.146436641060951,-49.881862499696126],[-37.560006446488146,-45.592052597656789],[-39.13770692863632,-69.085816352131204],[-65.415587331361877,-90],[-51.462290812033373,-16.760787566546721],[-28.454456182408332,90],[-36.269498017702901,-26.37490682626369]],[[-40.542178258552283,-90],[-39.13770692863632,-69.085816352131204],[-16.295804332590937,-50.906277575066262],[-40.542178258552283,-90]],[[-16.295804332590937,-50.906277575066262],[-5.6790432913971927,-33.788307256548933],[14.686101893282586,-26.248228042967728],[-16.295804332590937,-50.906277575066262]],[[-37.560006446488146,-45.592052597656789],[-36.269498017702901,-26.37490682626369],[27.479825940672225,90],[71.095881152477034,90],[-5.6790432913971927,-33.788307256548933],[-37.560006446488146,-45.592052597656789]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-36.269498017702901,-26.37490682626369],[-49.146436641060951,-49.881862499696126],[-37.560006446488146,-45.592052597656789],[-39.13770692863632,-69.085816352131204],[-65.415587331361877,-90],[-51.462290812033373,-16.760787566546721],[-28.454456182408332,90],[-36.269498017702901,-26.37490682626369]],[[-40.542178258552283,-90],[-39.13770692863632,-69.085816352131204],[-16.295804332590937,-50.906277575066262],[-40.542178258552283,-90]],[[-16.295804332590937,-50.906277575066262],[-5.6790432913971927,-33.788307256548933],[14.686101893282586,-26.248228042967728],[-16.295804332590937,-50.906277575066262]],[[-37.560006446488146,-45.592052597656789],[-36.269498017702901,-26.37490682626369],[27.479825940672225,90],[71.095881152477034,90],[-5.6790432913971927,-33.788307256548933],[-37.560006446488146,-45.592052597656789]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-77.020281185856106,-80.085419699581706],[-77.328568930885723,-83.404479889897416],[-80.495259564600545,-90],[-77.020281185856106,-80.085419699581706]],[[-77.941187535385211,-90],[-77.328568930885723,-83.404479889897416],[-39.252034383972621,-4.0994329574862469],[-39.29471328421063,-6.5269494453154593],[-77.941187535385211,-90]],[[-77.020281185856106,-80.085419699581706],[-62.688864277996522,74.208210509833052],[-38.108861278327581,80.371071656873013],[-37.597643844595929,90],[-38.663943358642484,29.350366647752089],[-77.020281185856106,-80.085419699581706]],[[-40.265125886194951,-61.722668598742551],[-39.29471328421063,-6.5269494453154593],[-15.554402498931253,44.750073899273843],[-8.4447006412989474,13.127318978368956],[-5.310206313296316,-4.5170390491918795],[-40.265125886194951,-61.722668598742551]],[[-39.252034383972621,-4.0994329574862469],[-38.663943358642484,29.350366647752089],[-22.476078360563164,75.536520897660651],[-15.632105532320049,45.095683888365997],[-39.252034383972621,-4.0994329574862469]],[[-15.554402498931253,44.750073899273843],[-15.632105532320049,45.095683888365997],[-8.9755856576261941,58.959750756602595],[-15.554402498931253,44.750073899273843]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-77.020281185856106,-80.085419699581706],[-77.328568930885723,-83.404479889897416],[-80.495259564600545,-90],[-77.020281185856106,-80.085419699581706]],[[-77.941187535385211,-90],[-77.328568930885723,-83.404479889897416],[-39.252034383972621,-4.0994329574862469],[-39.29471328421063,-6.5269494453154593],[-77.941187535385211,-90]],[[-77.020281185856106,-80.085419699581706],[-62.688864277996522,74.208210509833052],[-38.108861278327581,80.371071656873013],[-37.597643844595929,90],[-38.663943358642484,29.350366647752089],[-77.020281185856106,-80.085419699581706]],[[-40.265125886194951,-61.722668598742551],[-39.29471328421063,-6.5269494453154593],[-15.554402498931253,44.750073899273843],[-8.4447006412989474,13.127318978368956],[-5.310206313296316,-4.5170390491918795],[-40.265125886194951,-61.722668598742551]],[[-39.252034383972621,-4.0994329574862469],[-38.663943358642484,29.350366647752089],[-22.476078360563164,75.536520897660651],[-15.632105532320049,45.095683888365997],[-39.252034383972621,-4.0994329574862469]],[[-15.554402498931253,44.750073899273843],[-15.632105532320049,45.095683888365997],[-8.9755856576261941,58.959750756602595],[-15.554402498931253,44.750073899273843]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-68.840007952128175,37.060080998089632],[-68.145986924561413,31.114096694815196],[-69.187773850176768,30.693518246958952],[-68.840007952128175,37.060080998089632]],[[-75.780513355389928,-90],[-69.21567111077384,30.182802098042274],[-50.875629803516389,37.146119571446704],[-75.780513355389928,-90]],[[4.2911006174797457,-1.144569312564311],[-66.484019915251849,80.191238371060038],[-65.948228008382316,90],[4.2911006174797457,-1.144569312564311]],[[-90,22.291441435181515],[-69.187773850176768,30.693518246958952],[-69.21567111077384,30.182802098042274],[-90,22.291441435181515]],[[-68.840007952128175,37.060080998089632],[-75.019206401201359,90],[-66.484019915251849,80.191238371060038],[-68.840007952128175,37.060080998089632]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-68.840007952128175,37.060080998089632],[-68.145986924561413,31.114096694815196],[-69.187773850176768,30.693518246958952],[-68.840007952128175,37.060080998089632]],[[-75.780513355389928,-90],[-69.21567111077384,30.182802098042274],[-50.875629803516389,37.146119571446704],[-75.780513355389928,-90]],[[4.2911006174797457,-1.144569312564311],[-66.484019915251849,80.191238371060038],[-65.948228008382316,90],[4.2911006174797457,-1.144569312564311]],[[-90,22.291441435181515],[-69.187773850176768,30.693518246958952],[-69.21567111077384,30.182802098042274],[-90,22.291441435181515]],[[-68.840007952128175,37.060080998089632],[-75.019206401201359,90],[-66.484019915251849,80.191238371060038],[-68.840007952128175,37.060080998089632]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[27.570109889215438,22.850616190228489],[75.703105729477357,-90],[2.1548000876241362,-15.817792950796967],[27.570109889215438,22.850616190228489]],[[-0.069915984436478951,-90],[-46.602410662754053,-89.999999998014729],[-14.977190481820156,-41.883452819243004],[-0.069915984436478951,-90]],[[-14.977190481820156,-41.883452819243004],[-34.509989609682322,21.163004866431177],[2.1548000876241362,-15.817792950796967],[-14.977190481820156,-41.883452819243004]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[27.570109889215438,22.850616190228489],[75.703105729477357,-90],[2.1548000876241362,-15.817792950796967],[27.570109889215438,22.850616190228489]],[[-0.069915984436478951,-90],[-46.602410662754053,-89.999999998014729],[-14.977190481820156,-41.883452819243004],[-0.069915984436478951,-90]],[[-14.977190481820156,-41.883452819243004],[-34.509989609682322,21.163004866431177],[2.1548000876241362,-15.817792950796967],[-14.977190481820156,-41.883452819243004]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[28.865673900286581,33.379551302126075],[39.505669183485338,-34.957993133630559],[7.152466542048213,-90],[28.865673900286581,33.379551302126075]],[[-64.597291313620858,2.4515644574812248],[20.050002923927103,90],[24.375150856531356,62.220853377417541],[-64.597291313620858,2.4515644574812248]],[[28.865673900286581,33.379551302126075],[24.375150856531356,62.220853377417541],[35.223952527956932,69.508785974507163],[28.865673900286581,33.379551302126075]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[28.865673900286581,33.379551302126075],[39.505669183485338,-34.957993133630559],[7.152466542048213,-90],[28.865673900286581,33.379551302126075]],[[-64.597291313620858,2.4515644574812248],[20.050002923927103,90],[24.375150856531356,62.220853377417541],[-64.597291313620858,2.4515644574812248]],[[28.865673900286581,33.379551302126075],[24.375150856531356,62.220853377417541],[35.223952527956932,69.508785974507163],[28.865673900286581,33.379551302126075]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-66.582505384413167,-51.305212522944061],[-60.169897093348865,-90],[-90,-90],[-66.582505384413167,-51.305212522944061]],[[20.858462934004656,-90],[-35.056287147954386,0.78833269359179781],[18.933251883215579,90],[20.858462934004656,-90]],[[-66.582505384413167,-51.305212522944061],[-90,90],[-35.056287147954386,0.78833269359179781],[-66.582505384413167,-51.305212522944061]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-66.582505384413167,-51.305212522944061],[-60.169897093348865,-90],[-90,-90],[-66.582505384413167,-51.305212522944061]],[[20.858462934004656,-90],[-35.056287147954386,0.78833269359179781],[18.933251883215579,90],[20.858462934004656,-90]],[[-66.582505384413167,-51.305212522944061],[-90,90],[-35.056287147954386,0.78833269359179781],[-66.582505384413167,-51.305212522944061]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[36.692916710279974,-90],[-31.182443792600079,6.434474852744998],[-90,90],[52.245260790065387,57.329280208760991],[36.692916710279974,-90]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[36.692916710279974,-90],[-31.182443792600079,6.434474852744998],[-90,90],[52.245260790065387,57.329280208760991],[36.692916710279974,-90]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-17.959089916602533,-4.3577640799248218],[-29.181784251472308,-90],[-65.493717350127127,15.053615507086979],[-17.959089916602533,-4.3577640799248218]],[[-21.884657435973146,-34.517617672142393],[-17.94005076020704,-4.3655389655558539],[9.3768748358343359,-15.520758655380195],[-21.884657435973146,-34.517617672142393]],[[-17.94005076020704,-4.3655389655558539],[-17.959089916602533,-4.3577640799248218],[-5.8963967801936494,87.694641571893939],[-17.94005076020704,-4.3655389655558539]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-17.959089916602533,-4.3577640799248218],[-29.181784251472308,-90],[-65.493717350127127,15.053615507086979],[-17.959089916602533,-4.3577640799248218]],[[-21.884657435973146,-34.517617672142393],[-17.94005076020704,-4.3655389655558539],[9.3768748358343359,-15.520758655380195],[-21.884657435973146,-34.517617672142393]],[[-17.94005076020704,-4.3655389655558539],[-17.959089916602533,-4.3577640799248218],[-5.8963967801936494,87.694641571893939],[-17.94005076020704,-4.3655389655558539]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[17.198360589495572,-77.168667157542373],[-24.835678541343281,-83.717338556412017],[-30.259244993378722,61.914816728303791],[17.198360589495572,-77.168667157542373]],[[-8.3544985146710644,-90],[17.979891823366039,-79.459092168186686],[21.576625471325329,-90],[-8.3544985146710644,-90]],[[17.979891823366039,-79.459092168186686],[17.198360589495572,-77.168667157542373],[27.846596597209441,-75.509730732825361],[17.979891823366039,-79.459092168186686]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[17.198360589495572,-77.168667157542373],[-24.835678541343281,-83.717338556412017],[-30.259244993378722,61.914816728303791],[17.198360589495572,-77.168667157542373]],[[-8.3544985146710644,-90],[17.979891823366039,-79.459092168186686],[21.576625471325329,-90],[-8.3544985146710644,-90]],[[17.979891823366039,-79.459092168186686],[17.198360589495572,-77.168667157542373],[27.846596597209441,-75.509730732825361],[17.979891823366039,-79.459092168186686]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-1.2588613620456419,13.607321860624268],[61.845346679259052,48.415944386573557],[15.226225965240992,-5.3702891526017318],[0.92681706095183469,1.6819284384951441],[3.8469417404317623,-14.250715301799051],[7.2615297628459139,-14.559458820527061],[4.4896578086498238,-17.757471781424698],[14.589138845678622,-72.861774161244625],[-10.508572009494033,-35.06149380752737],[-58.12642296329372,-90],[-15.260062192400673,90],[-1.2588613620456419,13.607321860624268]],[[0.92681706095183469,1.6819284384951441],[-1.2588613620456419,13.607321860624268],[-11.641308877525201,7.8803076458946304],[0.92681706095183469,1.6819284384951441]],[[-10.508572009494033,-35.06149380752737],[4.4896578086498238,-17.757471781424698],[3.8469417404317623,-14.250715301799051],[-26.125369947914503,-11.54064986657559],[-10.508572009494033,-35.06149380752737]],[[39.829571435268129,-17.504227477249202],[7.2615297628459139,-14.559458820527061],[15.226225965240992,-5.3702891526017318],[39.829571435268129,-17.504227477249202]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-1.2588613620456419,13.607321860624268],[61.845346679259052,48.415944386573557],[15.226225965240992,-5.3702891526017318],[0.92681706095183469,1.6819284384951441],[3.8469417404317623,-14.250715301799051],[7.2615297628459139,-14.559458820527061],[4.4896578086498238,-17.757471781424698],[14.589138845678622,-72.861774161244625],[-10.508572009494033,-35.06149380752737],[-58.12642296329372,-90],[-15.260062192400673,90],[-1.2588613620456419,13.607321860624268]],[[0.92681706095183469,1.6819284384951441],[-1.2588613620456419,13.607321860624268],[-11.641308877525201,7.8803076458946304],[0.92681706095183469,1.6819284384951441]],[[-10.508572009494033,-35.06149380752737],[4.4896578086498238,-17.757471781424698],[3.8469417404317623,-14.250715301799051],[-26.125369947914503,-11.54064986657559],[-10.508572009494033,-35.06149380752737]],[[39.829571435268129,-17.504227477249202],[7.2615297628459139,-14.559458820527061],[15.226225965240992,-5.3702891526017318],[39.829571435268129,-17.504227477249202]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-19.681975166855118,-34.10344217707847],[-90,89.999999998418275],[53.036316534501381,90],[-19.681975166855118,-34.10344217707847]],[[-52.434509065706855,-90],[-29.2339442498794,-50.405148598356135],[-2.8515119199232331,-90],[-52.434509065706855,-90]],[[18.310881874573923,-90],[-25.473718245381271,-43.987822508814972],[-19.681975166855118,-34.10344217707847],[-15.406194071963924,-41.649717163101563],[18.310881874573923,-90]],[[-29.2339442498794,-50.405148598356135],[-52.27954259799813,-15.81822990020261],[-25.473718245381271,-43.987822508814972],[-29.2339442498794,-50.405148598356135]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-19.681975166855118,-34.10344217707847],[-90,89.999999998418275],[53.036316534501381,90],[-19.681975166855118,-34.10344217707847]],[[-52.434509065706855,-90],[-29.2339442498794,-50.405148598356135],[-2.8515119199232331,-90],[-52.434509065706855,-90]],[[18.310881874573923,-90],[-25.473718245381271,-43.987822508814972],[-19.681975166855118,-34.10344217707847],[-15.406194071963924,-41.649717163101563],[18.310881874573923,-90]],[[-29.2339442498794,-50.405148598356135],[-52.27954259799813,-15.81822990020261],[-25.473718245381271,-43.987822508814972],[-29.2339442498794,-50.405148598356135]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[49.939516827702498,-90],[-20.470128740962011,-68.102019032647391],[-20.124197553433845,-67.213968219799824],[34.438329237618149,-61.893901496061034],[49.939516827702498,-90]],[[-82.380918375714089,-73.284249936115529],[-4.7432060543229699,9.1484031048644194],[-11.790524932251525,21.926303986370414],[-3.4862200343039369,10.483021157965428],[19.753975453441285,35.158541777575607],[5.5896897290794696,-1.2030408273476854],[73.839023528563189,-58.052174675157325],[34.438329237618149,-61.893901496061034],[3.6757233436274213,-6.1164440290327313],[-20.124197553433845,-67.213968219799824],[-82.380918375714089,-73.284249936115529]],[[5.5896897290794696,-1.2030408273476854],[4.0842948437219349,0.050896618883412792],[-3.4862200343039369,10.483021157965428],[-4.7432060543229699,9.1484031048644194],[3.6757233436274213,-6.1164440290327313],[5.5896897290794696,-1.2030408273476854]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[49.939516827702498,-90],[-20.470128740962011,-68.102019032647391],[-20.124197553433845,-67.213968219799824],[34.438329237618149,-61.893901496061034],[49.939516827702498,-90]],[[-82.380918375714089,-73.284249936115529],[-4.7432060543229699,9.1484031048644194],[-11.790524932251525,21.926303986370414],[-3.4862200343039369,10.483021157965428],[19.753975453441285,35.158541777575607],[5.5896897290794696,-1.2030408273476854],[73.839023528563189,-58.052174675157325],[34.438329237618149,-61.893901496061034],[3.6757233436274213,-6.1164440290327313],[-20.124197553433845,-67.213968219799824],[-82.380918375714089,-73.284249936115529]],[[5.5896897290794696,-1.2030408273476854],[4.0842948437219349,0.050896618883412792],[-3.4862200343039369,10.483021157965428],[-4.7432060543229699,9.1484031048644194],[3.6757233436274213,-6.1164440290327313],[5.5896897290794696,-1.2030408273476854]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[4.7618727814345867,-14.245890151885444],[-7.1675180359486266,-90],[-83.232840716292529,40.620187389409224],[-29.219286930421923,6.9418934044755334],[-29.378277853968513,6.9629531745072839],[-28.933835455648254,6.7639099538036529],[4.7618727814345867,-14.245890151885444]],[[51.056303527367277,-43.111190419066219],[4.7618727814345867,-14.245890151885444],[5.632592229367642,-8.716640778187827],[-28.933835455648254,6.7639099538036529],[-29.219286930421923,6.9418934044755334],[2.700964609629902,2.7137705544807242],[12.385960896403816,0.48342578457646468],[51.056303527367277,-43.111190419066219]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[4.7618727814345867,-14.245890151885444],[-7.1675180359486266,-90],[-83.232840716292529,40.620187389409224],[-29.219286930421923,6.9418934044755334],[-29.378277853968513,6.9629531745072839],[-28.933835455648254,6.7639099538036529],[4.7618727814345867,-14.245890151885444]],[[51.056303527367277,-43.111190419066219],[4.7618727814345867,-14.245890151885444],[5.632592229367642,-8.716640778187827],[-28.933835455648254,6.7639099538036529],[-29.219286930421923,6.9418934044755334],[2.700964609629902,2.7137705544807242],[12.385960896403816,0.48342578457646468],[51.056303527367277,-43.111190419066219]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-21.426619830983732,-89.379667629404466],[-72.784461583687971,-88.999754827814016],[-81.94289434769162,25.456737039611831],[-38.382426191605546,-57.204127144336077],[-41.663734179022256,-78.439084394036513],[-29.749353943865881,-73.586348060869426],[-21.426619830983732,-89.379667629404466]],[[-21.09971823441461,-90],[-21.426619830983732,-89.379667629404466],[-21.080965849893449,-89.382224558742934],[-21.09971823441461,-90]],[[62.431917153693021,-90],[-21.080965849893449,-89.382224558742934],[-20.486971473666468,-69.813772479288062],[19.166418765782844,-53.662915804391695],[63.671046682728601,-90],[62.431917153693021,-90]],[[-29.749353943865881,-73.586348060869426],[-38.382426191605546,-57.204127144336077],[-31.449272112025476,-12.336278393150847],[-41.028899505665962,-4.5147159296945967],[-30.750049689226596,-7.8112663207986941],[-15.63587330244308,90],[-18.721998818789388,-11.66880646480822],[60.158611185675326,-36.966763960486929],[19.166418765782844,-53.662915804391695],[-19.049573405176112,-22.46036923493498],[-20.486971473666468,-69.813772479288062],[-29.749353943865881,-73.586348060869426]],[[-19.049573405176112,-22.46036923493498],[-18.721998818789388,-11.66880646480822],[-30.750049689226596,-7.8112663207986941],[-31.449272112025476,-12.336278393150847],[-19.049573405176112,-22.46036923493498]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-21.426619830983732,-89.379667629404466],[-72.784461583687971,-88.999754827814016],[-81.94289434769162,25.456737039611831],[-38.382426191605546,-57.204127144336077],[-41.663734179022256,-78.439084394036513],[-29.749353943865881,-73.586348060869426],[-21.426619830983732,-89.379667629404466]],[[-21.09971823441461,-90],[-21.426619830983732,-89.379667629404466],[-21.080965849893449,-89.382224558742934],[-21.09971823441461,-90]],[[62.431917153693021,-90],[-21.080965849893449,-89.382224558742934],[-20.486971473666468,-69.813772479288062],[19.166418765782844,-53.662915804391695],[63.671046682728601,-90],[62.431917153693021,-90]],[[-29.749353943865881,-73.586348060869426],[-38.382426191605546,-57.204127144336077],[-31.449272112025476,-12.336278393150847],[-41.028899505665962,-4.5147159296945967],[-30.750049689226596,-7.8112663207986941],[-15.63587330244308,90],[-18.721998818789388,-11.66880646480822],[60.158611185675326,-36.966763960486929],[19.166418765782844,-53.662915804391695],[-19.049573405176112,-22.46036923493498],[-20.486971473666468,-69.813772479288062],[-29.749353943865881,-73.586348060869426]],[[-19.049573405176112,-22.46036923493498],[-18.721998818789388,-11.66880646480822],[-30.750049689226596,-7.8112663207986941],[-31.449272112025476,-12.336278393150847],[-19.049573405176112,-22.46036923493498]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon polygon = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-17.906614911617162,-53.670186894017093],[-72.687715727164573,-90],[-77.889582483879749,90],[-47.149885004784061,16.372797801863811],[-58.874489264131405,8.3403055152440846],[-44.017112148517498,8.8692333739436133],[-43.760297522359615,8.2541153357643502],[-48.398890069305921,4.7201397602360009],[-38.665987052649818,-3.9476907252248874],[-17.906614911617162,-53.670186894017093]],[[-2.7387498969355368,-90],[-17.906614911617162,-53.670186894017093],[-6.8038688963847829,-46.30705103709559],[-2.7387498969355368,-90]],[[-6.8038688963847829,-46.30705103709559],[-8.2224486207861638,-31.0597897622158],[2.1962303277340673,-40.338351652092697],[-6.8038688963847829,-46.30705103709559]],[[-8.2224486207861638,-31.0597897622158],[-38.665987052649818,-3.9476907252248874],[-43.760297522359615,8.2541153357643502],[-42.90074612601282,8.9089763975751382],[-44.017112148517498,8.8692333739436133],[-47.149885004784061,16.372797801863811],[45.190674429223662,79.635046572817728],[40.490070954305672,72.441418146356597],[63.53694979672099,90],[75.056911135062407,13.108310545642606],[-0.027204347469059975,10.435289586728711],[-10.580480746811602,-5.715051428780245],[-8.2224486207861638,-31.0597897622158]],[[-42.90074612601282,8.9089763975751382],[-0.027204347469059975,10.435289586728711],[40.490070954305672,72.441418146356597],[-42.90074612601282,8.9089763975751382]]]}") - .getGeometry()); + Polygon polygon = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-17.906614911617162,-53.670186894017093],[-72.687715727164573,-90],[-77.889582483879749,90],[-47.149885004784061,16.372797801863811],[-58.874489264131405,8.3403055152440846],[-44.017112148517498,8.8692333739436133],[-43.760297522359615,8.2541153357643502],[-48.398890069305921,4.7201397602360009],[-38.665987052649818,-3.9476907252248874],[-17.906614911617162,-53.670186894017093]],[[-2.7387498969355368,-90],[-17.906614911617162,-53.670186894017093],[-6.8038688963847829,-46.30705103709559],[-2.7387498969355368,-90]],[[-6.8038688963847829,-46.30705103709559],[-8.2224486207861638,-31.0597897622158],[2.1962303277340673,-40.338351652092697],[-6.8038688963847829,-46.30705103709559]],[[-8.2224486207861638,-31.0597897622158],[-38.665987052649818,-3.9476907252248874],[-43.760297522359615,8.2541153357643502],[-42.90074612601282,8.9089763975751382],[-44.017112148517498,8.8692333739436133],[-47.149885004784061,16.372797801863811],[45.190674429223662,79.635046572817728],[40.490070954305672,72.441418146356597],[63.53694979672099,90],[75.056911135062407,13.108310545642606],[-0.027204347469059975,10.435289586728711],[-10.580480746811602,-5.715051428780245],[-8.2224486207861638,-31.0597897622158]],[[-42.90074612601282,8.9089763975751382],[-0.027204347469059975,10.435289586728711],[40.490070954305672,72.441418146356597],[-42.90074612601282,8.9089763975751382]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(polygon, null)); - Polygon differenced = (Polygon) (difference.execute(polygon, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(polygon, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } @@ -573,14 +605,10 @@ public static void testPolygons() { @Test public static void testPolylines() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); - OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Difference); - OperatorContains contains = (OperatorContains) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Contains); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Difference); + OperatorContains contains = (OperatorContains) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Contains); { Polyline poly = new Polyline(); @@ -591,9 +619,8 @@ public static void testPolylines() { poly.lineTo(0, 500); Polyline densified = (Polyline) (densify.execute(poly, 10.0, null)); - Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polyline differenced = (Polyline) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polyline convex_hull = (Polyline) (bounding.execute(densified, null)); + Polyline differenced = (Polyline) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } @@ -608,11 +635,9 @@ public static void testPolylines() { polyline.lineTo(170, 45); polyline.lineTo(225, 65); - Polyline densified = (Polyline) (densify.execute(polyline, 10.0, - null)); + Polyline densified = (Polyline) (densify.execute(polyline, 10.0, null)); Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - boolean bcontains = contains.execute(convex_hull, densified, - SpatialReference.create(4326), null); + boolean bcontains = contains.execute(convex_hull, densified, SpatialReference.create(4326), null); assertTrue(bcontains); assertTrue(bounding.isConvex(convex_hull, null)); @@ -621,46 +646,33 @@ public static void testPolylines() { @Test public static void testNonSimpleShape() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); - OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Difference); - OperatorContains contains = (OperatorContains) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.Contains); - - { - Polygon shape = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[6.7260599916745036,-90],[15.304037095218971,-18.924146439950675],[6.3163297788539232,-90],[5.2105387036445823,-59.980719950158637],[5.1217504663506981,-60.571174400735508],[8.2945138368731044,-27.967042187979146],[15.76606600357545,28.594953216378414],[8.4365340991447919,66.880924521951329],[10.115022726199774,65.247385313781265],[12.721206966604395,-23.793016208456883],[12.051875868947576,-11.368909618476637],[11.867118776841318,44.968896646914239],[7.5326099218274614,35.095776924526533],[8.86765609460479,90],[3.2036592678446922,55.507964789691712],[0.23585282258761486,-42.620591380394039],[-1.2660432762142744,90],[5.5580612840503001,-9.4879902323389196],[12.258387597532487,-35.945231749575591],[-48.746716054894101,90],[7.2294405148356846,-15.719232058488402],[13.798313011339591,-10.467172541381753],[7.4430022048746718,6.3951685161785656],[6.4876332898327815,31.10016146737189],[9.3645424359058911,47.123308099298804],[13.398605254542668,-6.4398318586014325],[-90,90],[13.360786277212718,82.971274676174545],[7.9405631778693566,90],[10.512482079680538,90],[16.994982794293946,19.60673041736408],[16.723893839323615,22.728853852102926],[23.178783416627525,90],[6.7260599916745036,-90]],[[26.768777234301993,90],[20.949797955126346,90],[11.967758262201434,-0.45048849056049711],[17.535751576687339,52.767528591651441],[26.768777234301993,90]],[[18.677765775891793,12.559680067559942],[19.060218406331451,90],[17.123595624401705,90],[-2.3805299720687887,-90],[-11.882782057881979,-90],[21.640575461689693,90],[11.368255808198477,85.501555553904794],[17.390084032215348,90],[23.999392897519989,78.255909006554603],[-6.8860811786563101,69.49189433189926],[29.232578855788898,90],[25.951412073846683,90],[-5.5572284181160772,-16.763772082849457],[18.677765775891793,12.559680067559942]]]}") - .getGeometry()); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorDifference difference = (OperatorDifference) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Difference); + OperatorContains contains = (OperatorContains) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Contains); + + { + Polygon shape = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[6.7260599916745036,-90],[15.304037095218971,-18.924146439950675],[6.3163297788539232,-90],[5.2105387036445823,-59.980719950158637],[5.1217504663506981,-60.571174400735508],[8.2945138368731044,-27.967042187979146],[15.76606600357545,28.594953216378414],[8.4365340991447919,66.880924521951329],[10.115022726199774,65.247385313781265],[12.721206966604395,-23.793016208456883],[12.051875868947576,-11.368909618476637],[11.867118776841318,44.968896646914239],[7.5326099218274614,35.095776924526533],[8.86765609460479,90],[3.2036592678446922,55.507964789691712],[0.23585282258761486,-42.620591380394039],[-1.2660432762142744,90],[5.5580612840503001,-9.4879902323389196],[12.258387597532487,-35.945231749575591],[-48.746716054894101,90],[7.2294405148356846,-15.719232058488402],[13.798313011339591,-10.467172541381753],[7.4430022048746718,6.3951685161785656],[6.4876332898327815,31.10016146737189],[9.3645424359058911,47.123308099298804],[13.398605254542668,-6.4398318586014325],[-90,90],[13.360786277212718,82.971274676174545],[7.9405631778693566,90],[10.512482079680538,90],[16.994982794293946,19.60673041736408],[16.723893839323615,22.728853852102926],[23.178783416627525,90],[6.7260599916745036,-90]],[[26.768777234301993,90],[20.949797955126346,90],[11.967758262201434,-0.45048849056049711],[17.535751576687339,52.767528591651441],[26.768777234301993,90]],[[18.677765775891793,12.559680067559942],[19.060218406331451,90],[17.123595624401705,90],[-2.3805299720687887,-90],[-11.882782057881979,-90],[21.640575461689693,90],[11.368255808198477,85.501555553904794],[17.390084032215348,90],[23.999392897519989,78.255909006554603],[-6.8860811786563101,69.49189433189926],[29.232578855788898,90],[25.951412073846683,90],[-5.5572284181160772,-16.763772082849457],[18.677765775891793,12.559680067559942]]]}").getGeometry()); Polygon densified = (Polygon) (densify.execute(shape, 10.0, null)); Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polygon differenced = (Polygon) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon shape = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-13.630596027421603,3.6796011190640709],[-10.617275202716886,-28.133054337834409],[-81.617315194491695,90],[-4.0763362539824293,-90],[2.8213537979804073,-90],[-5.1515857979973365,-11.605767592612787],[43.477754021411123,35.507543731267589],[-45.818261267516704,-90],[-4.8545715514870018,-64.204371906322223],[-1.744951154293144,-30.257848194381509],[-7.8524748309267149,15.513561279453089],[-10.657563385538953,-81.785061432086309],[-6.3487369893289411,-31.849779201388415],[-14.768278524737962,-12.004393281111058],[-27.001635582579841,90],[-14.967554248940855,-78.970629918591811],[-12.999635147475825,-38.584472796107939],[-13.630596027421603,3.6796011190640709]],[[-16.338143621861352,-37.415690513288375],[-21.553879270366266,-90],[-18.649338100909404,-90],[-24.880584966233631,1.3133858590648728],[-16.483464632078249,-53.979692212288882],[-24.836979215403964,-68.69859399640147],[-29.708282990385214,-90],[-27.469962102507036,-1.6392995673644872],[-20.405051753708271,61.943199597870034],[-18.242567838912599,24.405109362934219],[-66.334547696572528,-52.678390155566603],[-13.471083255903507,-33.782708412943229],[-7.092757068096085,33.673785662500464],[-2.7427100969018205,74.386868339212668],[-8.2174861339989675,90],[-15.699459164009667,90],[-9.5910045204059156,90],[-8.4504603287557369,90],[-1.5498862802092637,2.5144190340747681],[-6.5326327868410639,-17.428029961128306],[-10.947786354404593,31.516236387466538],[-7.4777936485986354,12.486727826508769],[-13.89052186883092,12.397126427870356],[-10.530672679779606,-55.463541447339118],[-8.7161833631330374,-90],[-4.7231067612639519,-90],[-3.9692500849117041,-32.204677519048822],[3.740804266163555,32.88191805391007],[6.2021313886056246,76.617541950091564],[6.1183997672398194,90],[0.59730820015390673,90],[7.3242950674530753,18.030401540676614],[1.8252371571535342,90],[-16.338143621861352,-37.415690513288375]]]}") - .getGeometry()); + Polygon shape = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-13.630596027421603,3.6796011190640709],[-10.617275202716886,-28.133054337834409],[-81.617315194491695,90],[-4.0763362539824293,-90],[2.8213537979804073,-90],[-5.1515857979973365,-11.605767592612787],[43.477754021411123,35.507543731267589],[-45.818261267516704,-90],[-4.8545715514870018,-64.204371906322223],[-1.744951154293144,-30.257848194381509],[-7.8524748309267149,15.513561279453089],[-10.657563385538953,-81.785061432086309],[-6.3487369893289411,-31.849779201388415],[-14.768278524737962,-12.004393281111058],[-27.001635582579841,90],[-14.967554248940855,-78.970629918591811],[-12.999635147475825,-38.584472796107939],[-13.630596027421603,3.6796011190640709]],[[-16.338143621861352,-37.415690513288375],[-21.553879270366266,-90],[-18.649338100909404,-90],[-24.880584966233631,1.3133858590648728],[-16.483464632078249,-53.979692212288882],[-24.836979215403964,-68.69859399640147],[-29.708282990385214,-90],[-27.469962102507036,-1.6392995673644872],[-20.405051753708271,61.943199597870034],[-18.242567838912599,24.405109362934219],[-66.334547696572528,-52.678390155566603],[-13.471083255903507,-33.782708412943229],[-7.092757068096085,33.673785662500464],[-2.7427100969018205,74.386868339212668],[-8.2174861339989675,90],[-15.699459164009667,90],[-9.5910045204059156,90],[-8.4504603287557369,90],[-1.5498862802092637,2.5144190340747681],[-6.5326327868410639,-17.428029961128306],[-10.947786354404593,31.516236387466538],[-7.4777936485986354,12.486727826508769],[-13.89052186883092,12.397126427870356],[-10.530672679779606,-55.463541447339118],[-8.7161833631330374,-90],[-4.7231067612639519,-90],[-3.9692500849117041,-32.204677519048822],[3.740804266163555,32.88191805391007],[6.2021313886056246,76.617541950091564],[6.1183997672398194,90],[0.59730820015390673,90],[7.3242950674530753,18.030401540676614],[1.8252371571535342,90],[-16.338143621861352,-37.415690513288375]]]}").getGeometry()); Polygon densified = (Polygon) (densify.execute(shape, 10.0, null)); Polygon convex_hull = (Polygon) (bounding.execute(densified, null)); - Polygon differenced = (Polygon) (difference.execute(densified, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(densified, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } { - Polygon shape = (Polygon) (TestCommonMethods - .fromJson("{\"rings\":[[[-11.752662474672046,-90],[-76.236530072126513,7.3247326417920817],[18.933251883215579,90],[51.538924439116798,90],[52.253017336758049,80.352482145105284],[41.767201918260639,51.890297432229289],[21.697252770910882,-1.3185641048567049],[45.112193442818935,60.758441021743636],[48.457184967377231,69.626584611257954],[49.531808284502759,70.202152706968036],[52.394797054144334,71.533541126234581],[ 52.9671102343993,70.704964290210626],[58.527850348069251,16.670036266565845],[62.310807912773328,-34.249918700039238],[62.775020703241523,-43.541598916699364],[64.631871865114277,-80.708319783339874],[65.096084655582459,-90],[-11.752662474672046,-90]]]}") - .getGeometry()); + Polygon shape = (Polygon) (TestCommonMethods.fromJson("{\"rings\":[[[-11.752662474672046,-90],[-76.236530072126513,7.3247326417920817],[18.933251883215579,90],[51.538924439116798,90],[52.253017336758049,80.352482145105284],[41.767201918260639,51.890297432229289],[21.697252770910882,-1.3185641048567049],[45.112193442818935,60.758441021743636],[48.457184967377231,69.626584611257954],[49.531808284502759,70.202152706968036],[52.394797054144334,71.533541126234581],[ 52.9671102343993,70.704964290210626],[58.527850348069251,16.670036266565845],[62.310807912773328,-34.249918700039238],[62.775020703241523,-43.541598916699364],[64.631871865114277,-80.708319783339874],[65.096084655582459,-90],[-11.752662474672046,-90]]]}").getGeometry()); Polygon convex_hull = (Polygon) (bounding.execute(shape, null)); - Polygon differenced = (Polygon) (difference.execute(shape, - convex_hull, SpatialReference.create(4326), null)); + Polygon differenced = (Polygon) (difference.execute(shape, convex_hull, SpatialReference.create(4326), null)); assertTrue(differenced.isEmpty()); assertTrue(bounding.isConvex(convex_hull, null)); } @@ -725,10 +737,8 @@ public static void testNonSimpleShape() { @Test public static void testStar() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); - OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.DensifyByLength); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); + OperatorDensifyByLength densify = (OperatorDensifyByLength) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.DensifyByLength); Polygon star = new Polygon(); star.startPath(0, 0); @@ -797,8 +807,7 @@ public static void testPointsArray() { @Test public static void testMergeCursor() { - OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ConvexHull); + OperatorConvexHull bounding = (OperatorConvexHull) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ConvexHull); Polygon geom1 = new Polygon(); Polygon geom2 = new Polygon(); @@ -890,7 +899,7 @@ public void testHullTickTock() { Polygon geom1 = new Polygon(); Polygon geom2 = new Polygon(); Point geom3 = new Point(); - Line geom4= new Line(); + Line geom4 = new Line(); Envelope geom5 = new Envelope(); MultiPoint geom6 = new MultiPoint(); @@ -943,7 +952,7 @@ public void testHullTickTock() { ticktock.tock(); // Get the result Geometry result = ticktock.next(); - Polygon convex_hull = (Polygon)result; + Polygon convex_hull = (Polygon) result; assertTrue(OperatorConvexHull.local().isConvex(convex_hull, null)); Point2D p1 = convex_hull.getXY(0); @@ -959,5 +968,5 @@ public void testHullTickTock() { assertTrue(p5.x == -5.0 && p5.y == 1.25); assertTrue(p6.x == 0.0 && p6.y == 10.0); } - + } diff --git a/src/test/java/com/esri/core/geometry/TestDifference.java b/src/test/java/com/esri/core/geometry/TestDifference.java index ca7bc3d4..455877e6 100644 --- a/src/test/java/com/esri/core/geometry/TestDifference.java +++ b/src/test/java/com/esri/core/geometry/TestDifference.java @@ -1,6 +1,10 @@ package com.esri.core.geometry; +import java.util.ArrayList; +import java.util.List; + import junit.framework.TestCase; + import org.junit.Test; public class TestDifference extends TestCase { diff --git a/src/test/java/com/esri/core/geometry/TestGeodetic.java b/src/test/java/com/esri/core/geometry/TestGeodetic.java index 0cdbf6f7..8777ec19 100644 --- a/src/test/java/com/esri/core/geometry/TestGeodetic.java +++ b/src/test/java/com/esri/core/geometry/TestGeodetic.java @@ -24,7 +24,7 @@ public void testTriangleLength() { length += GeometryEngine.geodesicDistanceOnWGS84(pt_0, pt_1); length += GeometryEngine.geodesicDistanceOnWGS84(pt_1, pt_2); length += GeometryEngine.geodesicDistanceOnWGS84(pt_2, pt_0); - assertTrue(Math.abs(length - 3744719.4094597572) < 1e-13 * 3744719.4094597572); + assertTrue(Math.abs(length - 3744719.4094597572) < 1e-12 * 3744719.4094597572); } @Test @@ -36,7 +36,7 @@ public void testRotationInvariance() { length += GeometryEngine.geodesicDistanceOnWGS84(pt_0, pt_1); length += GeometryEngine.geodesicDistanceOnWGS84(pt_1, pt_2); length += GeometryEngine.geodesicDistanceOnWGS84(pt_2, pt_0); - assertTrue(Math.abs(length - 5409156.3896271614) < 1e-13 * 5409156.3896271614); + assertTrue(Math.abs(length - 5409156.3896271614) < 1e-12 * 5409156.3896271614); for (int i = -540; i < 540; i += 5) { pt_0.setXY(i + 10, 40); @@ -46,10 +46,41 @@ public void testRotationInvariance() { length += GeometryEngine.geodesicDistanceOnWGS84(pt_0, pt_1); length += GeometryEngine.geodesicDistanceOnWGS84(pt_1, pt_2); length += GeometryEngine.geodesicDistanceOnWGS84(pt_2, pt_0); - assertTrue(Math.abs(length - 5409156.3896271614) < 1e-13 * 5409156.3896271614); + assertTrue(Math.abs(length - 5409156.3896271614) < 1e-12 * 5409156.3896271614); } } + @Test + public void testDistanceFailure() { + { + Point p1 = new Point(-60.668485, -31.996013333333334); + Point p2 = new Point(119.13731666666666, 32.251583333333336); + double d = GeometryEngine.geodesicDistanceOnWGS84(p1, p2); + assertTrue(Math.abs(d - 19973410.50579736) < 1e-13 * 19973410.50579736); + } + + { + Point p1 = new Point(121.27343833333333, 27.467438333333334); + Point p2 = new Point(-58.55804833333333, -27.035613333333334); + double d = GeometryEngine.geodesicDistanceOnWGS84(p1, p2); + assertTrue(Math.abs(d - 19954707.428360686) < 1e-13 * 19954707.428360686); + } + + { + Point p1 = new Point(-53.329865, -36.08110166666667); + Point p2 = new Point(126.52895166666667, 35.97385); + double d = GeometryEngine.geodesicDistanceOnWGS84(p1, p2); + assertTrue(Math.abs(d - 19990586.700431127) < 1e-13 * 19990586.700431127); + } + + { + Point p1 = new Point(-4.7181166667, 36.1160166667); + Point p2 = new Point(175.248925, -35.7606716667); + double d = GeometryEngine.geodesicDistanceOnWGS84(p1, p2); + assertTrue(Math.abs(d - 19964450.206594173) < 1e-12 * 19964450.206594173); + } + } + @Test public void testLengthAccurateCR191313() { /* diff --git a/src/test/java/com/esri/core/geometry/TestGeomToGeoJson.java b/src/test/java/com/esri/core/geometry/TestGeomToGeoJson.java index 80099069..5ef3157e 100644 --- a/src/test/java/com/esri/core/geometry/TestGeomToGeoJson.java +++ b/src/test/java/com/esri/core/geometry/TestGeomToGeoJson.java @@ -3,7 +3,7 @@ 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 + 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, @@ -27,335 +27,445 @@ import com.esri.core.geometry.ogc.OGCMultiPoint; import com.esri.core.geometry.ogc.OGCLineString; import com.esri.core.geometry.ogc.OGCPolygon; +import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; import junit.framework.TestCase; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParser; import org.json.JSONException; +import org.json.JSONObject; import org.junit.Test; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; public class TestGeomToGeoJson extends TestCase { - OperatorFactoryLocal factory = OperatorFactoryLocal.getInstance(); - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - @Test - public void testPoint() { - Point p = new Point(10.0, 20.0); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}", result); - } - - @Test - public void testEmptyPoint() { - Point p = new Point(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Point\",\"coordinates\":null}", result); - } - - @Test - public void testPointGeometryEngine() { - Point p = new Point(10.0, 20.0); - String result = GeometryEngine.geometryToGeoJson(p); - assertEquals("{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}", result); - } - - @Test - public void testOGCPoint() { - Point p = new Point(10.0, 20.0); - OGCGeometry ogcPoint = new OGCPoint(p, null); - String result = ogcPoint.asGeoJson(); - assertEquals("{\"type\":\"Point\",\"coordinates\":[10.0,20.0]}", result); - } - - @Test - public void testMultiPoint() { - MultiPoint mp = new MultiPoint(); - mp.add(10.0, 20.0); - mp.add(20.0, 30.0); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(mp); - assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[20.0,30.0]]}", result); - } - - @Test - public void testEmptyMultiPoint() { - MultiPoint mp = new MultiPoint(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(mp); - assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":null}", result); - } - - @Test - public void testMultiPointGeometryEngine() { - MultiPoint mp = new MultiPoint(); - mp.add(10.0, 20.0); - mp.add(20.0, 30.0); - String result = GeometryEngine.geometryToGeoJson(mp); - assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[20.0,30.0]]}", result); - } - - @Test - public void testOGCMultiPoint() { - MultiPoint mp = new MultiPoint(); - mp.add(10.0, 20.0); - mp.add(20.0, 30.0); - OGCMultiPoint ogcMultiPoint = new OGCMultiPoint(mp, null); - String result = ogcMultiPoint.asGeoJson(); - assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10.0,20.0],[20.0,30.0]]}", result); - } - - @Test - public void testPolyline() { - Polyline p = new Polyline(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0]]}", result); - } - - @Test - public void testEmptyPolyline() { - Polyline p = new Polyline(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"LineString\",\"coordinates\":null}", result); - } - - @Test - public void testPolylineGeometryEngine() { - Polyline p = new Polyline(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - String result = GeometryEngine.geometryToGeoJson(p); - assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0]]}", result); - } - - @Test - public void testOGCLineString() { - Polyline p = new Polyline(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - OGCLineString ogcLineString = new OGCLineString(p, 0, null); - String result = ogcLineString.asGeoJson(); - assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0]]}", result); - } - - @Test - public void testPolygon() { - Polygon p = new Polygon(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - p.closePathWithLine(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}", result); - } - - @Test - public void testPolygonWithHole() { - Polygon p = new Polygon(); - - //exterior ring - has to be clockwise for Esri - p.startPath(100.0, 0.0); - p.lineTo(100.0, 1.0); - p.lineTo(101.0, 1.0); - p.lineTo(101.0, 0.0); - p.closePathWithLine(); - - //hole - counterclockwise for Esri - p.startPath(100.2, 0.2); - p.lineTo(100.8, 0.2); - p.lineTo(100.8, 0.8); - p.lineTo(100.2, 0.8); - p.closePathWithLine(); - - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]}", result); - } - - @Test - public void testPolygonWithHoleReversed() { - Polygon p = new Polygon(); - - //exterior ring - has to be clockwise for Esri - p.startPath(100.0, 0.0); - p.lineTo(100.0, 1.0); - p.lineTo(101.0, 1.0); - p.lineTo(101.0, 0.0); - p.closePathWithLine(); - - //hole - counterclockwise for Esri - p.startPath(100.2, 0.2); - p.lineTo(100.8, 0.2); - p.lineTo(100.8, 0.8); - p.lineTo(100.2, 0.8); - p.closePathWithLine(); - - p.reverseAllPaths();//make it reversed. Exterior ring - ccw, hole - cw. - - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]],[[100.2,0.2],[100.2,0.8],[100.8,0.8],[100.8,0.2],[100.2,0.2]]]}", result); - } - - @Test - public void testMultiPolygon() throws IOException { - JsonFactory jsonFactory = new JsonFactory(); - - String geoJsonPolygon = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-100.0,-100.0],[-100.0,100.0],[100.0,100.0],[100.0,-100.0],[-100.0,-100.0]],[[-90.0,-90.0],[90.0,90.0],[-90.0,90.0],[90.0,-90.0],[-90.0,-90.0]]],[[[-10.0,-10.0],[-10.0,10.0],[10.0,10.0],[10.0,-10.0],[-10.0,-10.0]]]]}"; - String esriJsonPolygon = "{\"rings\": [[[-100, -100], [-100, 100], [100, 100], [100, -100], [-100, -100]], [[-90, -90], [90, 90], [-90, 90], [90, -90], [-90, -90]], [[-10, -10], [-10, 10], [10, 10], [10, -10], [-10, -10]]]}"; - - JsonParser parser = jsonFactory.createJsonParser(esriJsonPolygon); - MapGeometry parsedPoly = GeometryEngine.jsonToGeometry(parser); - //MapGeometry parsedPoly = GeometryEngine.geometryFromGeoJson(jsonPolygon, 0, Geometry.Type.Polygon); - - Polygon poly = (Polygon) parsedPoly.getGeometry(); - - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - //String result = exporter.execute(parsedPoly.getGeometry()); - String result = exporter.execute(poly); - assertEquals(geoJsonPolygon, result); - } - - - - @Test - public void testEmptyPolygon() throws JSONException { - Polygon p = new Polygon(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":null}", result); - - MapGeometry imported = OperatorImportFromGeoJson.local().execute(0, Geometry.Type.Unknown, result, null); - assertTrue(imported.getGeometry().isEmpty()); - assertTrue(imported.getGeometry().getType() == Geometry.Type.Polygon); - } - - @Test - public void testPolygonGeometryEngine() { - Polygon p = new Polygon(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - p.closePathWithLine(); - String result = GeometryEngine.geometryToGeoJson(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}", result); - } - - @Test - public void testOGCPolygon() { - Polygon p = new Polygon(); - p.startPath(100.0, 0.0); - p.lineTo(101.0, 0.0); - p.lineTo(101.0, 1.0); - p.lineTo(100.0, 1.0); - p.closePathWithLine(); - OGCPolygon ogcPolygon = new OGCPolygon(p, null); - String result = ogcPolygon.asGeoJson(); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[101.0,0.0],[101.0,1.0],[100.0,1.0],[100.0,0.0]]]}", result); - } - - @Test - public void testPolygonWithHoleGeometryEngine() { - Polygon p = new Polygon(); - - p.startPath(100.0, 0.0);//clockwise exterior - p.lineTo(100.0, 1.0); - p.lineTo(101.0, 1.0); - p.lineTo(101.0, 0.0); - p.closePathWithLine(); - - p.startPath(100.2, 0.2);//counterclockwise hole - p.lineTo(100.8, 0.2); - p.lineTo(100.8, 0.8); - p.lineTo(100.2, 0.8); - p.closePathWithLine(); - - String result = GeometryEngine.geometryToGeoJson(p); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]}", result); - } - - @Test - public void testPolylineWithTwoPaths() { - Polyline p = new Polyline(); - - p.startPath(100.0, 0.0); - p.lineTo(100.0, 1.0); - - p.startPath(100.2, 0.2); - p.lineTo(100.8, 0.2); - - String result = GeometryEngine.geometryToGeoJson(p); - assertEquals("{\"type\":\"MultiLineString\",\"coordinates\":[[[100.0,0.0],[100.0,1.0]],[[100.2,0.2],[100.8,0.2]]]}", result); - } - - @Test - public void testOGCPolygonWithHole() { - Polygon p = new Polygon(); - - p.startPath(100.0, 0.0); - p.lineTo(100.0, 1.0); - p.lineTo(101.0, 1.0); - p.lineTo(101.0, 0.0); - p.closePathWithLine(); - - p.startPath(100.2, 0.2); - p.lineTo(100.8, 0.2); - p.lineTo(100.8, 0.8); - p.lineTo(100.2, 0.8); - p.closePathWithLine(); - - OGCPolygon ogcPolygon = new OGCPolygon(p, null); - String result = ogcPolygon.asGeoJson(); - assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100.0,0.0],[100.0,1.0],[101.0,1.0],[101.0,0.0],[100.0,0.0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]}", result); - } - - @Test - public void testEnvelope() { - Envelope e = new Envelope(); - e.setCoords(-180.0, -90.0, 180.0, 90.0); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(e); - assertEquals("{\"bbox\":[-180.0,-90.0,180.0,90.0]}", result); - } - - @Test - public void testEmptyEnvelope() { - Envelope e = new Envelope(); - OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); - String result = exporter.execute(e); - assertEquals("{\"bbox\":null}", result); - } - - @Test - public void testEnvelopeGeometryEngine() { - Envelope e = new Envelope(); - e.setCoords(-180.0, -90.0, 180.0, 90.0); - String result = GeometryEngine.geometryToGeoJson(e); - assertEquals("{\"bbox\":[-180.0,-90.0,180.0,90.0]}", result); - } + OperatorFactoryLocal factory = OperatorFactoryLocal.getInstance(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @Test + public void testPoint() { + Point p = new Point(10.0, 20.0); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Point\",\"coordinates\":[10,20]}", result); + } + + @Test + public void testEmptyPoint() { + Point p = new Point(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Point\",\"coordinates\":[]}", result); + } + + @Test + public void testPointGeometryEngine() { + Point p = new Point(10.0, 20.0); + String result = GeometryEngine.geometryToGeoJson(p); + assertEquals("{\"type\":\"Point\",\"coordinates\":[10,20]}", result); + } + + @Test + public void testOGCPoint() { + Point p = new Point(10.0, 20.0); + OGCGeometry ogcPoint = new OGCPoint(p, null); + String result = ogcPoint.asGeoJson(); + assertEquals("{\"type\":\"Point\",\"coordinates\":[10,20],\"crs\":null}", result); + } + + @Test + public void testMultiPoint() { + MultiPoint mp = new MultiPoint(); + mp.add(10.0, 20.0); + mp.add(20.0, 30.0); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(mp); + assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10,20],[20,30]]}", result); + } + + @Test + public void testEmptyMultiPoint() { + MultiPoint mp = new MultiPoint(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(mp); + assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[]}", result); + } + + @Test + public void testMultiPointGeometryEngine() { + MultiPoint mp = new MultiPoint(); + mp.add(10.0, 20.0); + mp.add(20.0, 30.0); + String result = GeometryEngine.geometryToGeoJson(mp); + assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10,20],[20,30]]}", result); + } + + @Test + public void testOGCMultiPoint() { + MultiPoint mp = new MultiPoint(); + mp.add(10.0, 20.0); + mp.add(20.0, 30.0); + OGCMultiPoint ogcMultiPoint = new OGCMultiPoint(mp, null); + String result = ogcMultiPoint.asGeoJson(); + assertEquals("{\"type\":\"MultiPoint\",\"coordinates\":[[10,20],[20,30]],\"crs\":null}", result); + } + + @Test + public void testPolyline() { + Polyline p = new Polyline(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100,0],[101,0],[101,1],[100,1]]}", result); + } + + @Test + public void testEmptyPolyline() { + Polyline p = new Polyline(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"LineString\",\"coordinates\":[]}", result); + } + + @Test + public void testPolylineGeometryEngine() { + Polyline p = new Polyline(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + String result = GeometryEngine.geometryToGeoJson(p); + assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100,0],[101,0],[101,1],[100,1]]}", result); + } + + @Test + public void testOGCLineString() { + Polyline p = new Polyline(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + OGCLineString ogcLineString = new OGCLineString(p, 0, null); + String result = ogcLineString.asGeoJson(); + assertEquals("{\"type\":\"LineString\",\"coordinates\":[[100,0],[101,0],[101,1],[100,1]],\"crs\":null}", result); + } + + @Test + public void testPolygon() { + Polygon p = new Polygon(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + p.closePathWithLine(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[100,1],[101,1],[101,0],[100,0]]]}", result); + } + + @Test + public void testPolygonWithHole() { + Polygon p = new Polygon(); + + //exterior ring - has to be clockwise for Esri + p.startPath(100.0, 0.0); + p.lineTo(100.0, 1.0); + p.lineTo(101.0, 1.0); + p.lineTo(101.0, 0.0); + p.closePathWithLine(); + + //hole - counterclockwise for Esri + p.startPath(100.2, 0.2); + p.lineTo(100.8, 0.2); + p.lineTo(100.8, 0.8); + p.lineTo(100.2, 0.8); + p.closePathWithLine(); + + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.2,0.8],[100.8,0.8],[100.8,0.2],[100.2,0.2]]]}", result); + } + + @Test + public void testPolygonWithHoleReversed() { + Polygon p = new Polygon(); + + //exterior ring - has to be clockwise for Esri + p.startPath(100.0, 0.0); + p.lineTo(100.0, 1.0); + p.lineTo(101.0, 1.0); + p.lineTo(101.0, 0.0); + p.closePathWithLine(); + + //hole - counterclockwise for Esri + p.startPath(100.2, 0.2); + p.lineTo(100.8, 0.2); + p.lineTo(100.8, 0.8); + p.lineTo(100.2, 0.8); + p.closePathWithLine(); + + p.reverseAllPaths();//make it reversed. Exterior ring - ccw, hole - cw. + + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[100,1],[101,1],[101,0],[100,0]],[[100.2,0.2],[100.8,0.2],[100.8,0.8],[100.2,0.8],[100.2,0.2]]]}", result); + } + + @Test + public void testMultiPolygon() throws IOException { + JsonFactory jsonFactory = new JsonFactory(); + + String geoJsonPolygon = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-100,-100],[-100,100],[100,100],[100,-100],[-100,-100]],[[-90,-90],[90,90],[-90,90],[90,-90],[-90,-90]]],[[[-10.0,-10.0],[-10.0,10.0],[10.0,10.0],[10.0,-10.0],[-10.0,-10.0]]]]}"; + String esriJsonPolygon = "{\"rings\": [[[-100, -100], [-100, 100], [100, 100], [100, -100], [-100, -100]], [[-90, -90], [90, 90], [-90, 90], [90, -90], [-90, -90]], [[-10, -10], [-10, 10], [10, 10], [10, -10], [-10, -10]]]}"; + + JsonParser parser = jsonFactory.createJsonParser(esriJsonPolygon); + MapGeometry parsedPoly = GeometryEngine.jsonToGeometry(parser); + //MapGeometry parsedPoly = GeometryEngine.geometryFromGeoJson(jsonPolygon, 0, Geometry.Type.Polygon); + + Polygon poly = (Polygon) parsedPoly.getGeometry(); + + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + //String result = exporter.execute(parsedPoly.getGeometry()); + String result = exporter.execute(poly); + assertEquals("{\"type\":\"MultiPolygon\",\"coordinates\":[[[[-100,-100],[100,-100],[100,100],[-100,100],[-100,-100]],[[-90,-90],[90,-90],[-90,90],[90,90],[-90,-90]]],[[[-10,-10],[10,-10],[10,10],[-10,10],[-10,-10]]]]}", result); + } + + + @Deprecated + @Test + public void testEmptyPolygon() throws JSONException { + Polygon p = new Polygon(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[]}", result); + + MapGeometry imported = OperatorImportFromGeoJson.local().execute(0, Geometry.Type.Unknown, result, null); + assertTrue(imported.getGeometry().isEmpty()); + assertTrue(imported.getGeometry().getType() == Geometry.Type.Polygon); + } + + @Test + public void testPolygonGeometryEngine() { + Polygon p = new Polygon(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + p.closePathWithLine(); + String result = GeometryEngine.geometryToGeoJson(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[100,1],[101,1],[101,0],[100,0]]]}", result); + } + + @Test + public void testOGCPolygon() { + Polygon p = new Polygon(); + p.startPath(100.0, 0.0); + p.lineTo(101.0, 0.0); + p.lineTo(101.0, 1.0); + p.lineTo(100.0, 1.0); + p.closePathWithLine(); + OGCPolygon ogcPolygon = new OGCPolygon(p, null); + String result = ogcPolygon.asGeoJson(); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[100,1],[101,1],[101,0],[100,0]]],\"crs\":null}", result); + } + + @Test + public void testPolygonWithHoleGeometryEngine() { + Polygon p = new Polygon(); + + p.startPath(100.0, 0.0);//clockwise exterior + p.lineTo(100.0, 1.0); + p.lineTo(101.0, 1.0); + p.lineTo(101.0, 0.0); + p.closePathWithLine(); + + p.startPath(100.2, 0.2);//counterclockwise hole + p.lineTo(100.8, 0.2); + p.lineTo(100.8, 0.8); + p.lineTo(100.2, 0.8); + p.closePathWithLine(); + + String result = GeometryEngine.geometryToGeoJson(p); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.2,0.8],[100.8,0.8],[100.8,0.2],[100.2,0.2]]]}", result); + } + + @Test + public void testPolylineWithTwoPaths() { + Polyline p = new Polyline(); + + p.startPath(100.0, 0.0); + p.lineTo(100.0, 1.0); + + p.startPath(100.2, 0.2); + p.lineTo(100.8, 0.2); + + String result = GeometryEngine.geometryToGeoJson(p); + assertEquals("{\"type\":\"MultiLineString\",\"coordinates\":[[[100,0],[100,1]],[[100.2,0.2],[100.8,0.2]]]}", result); + } + + @Test + public void testOGCPolygonWithHole() { + Polygon p = new Polygon(); + + p.startPath(100.0, 0.0); + p.lineTo(100.0, 1.0); + p.lineTo(101.0, 1.0); + p.lineTo(101.0, 0.0); + p.closePathWithLine(); + + p.startPath(100.2, 0.2); + p.lineTo(100.8, 0.2); + p.lineTo(100.8, 0.8); + p.lineTo(100.2, 0.8); + p.closePathWithLine(); + + OGCPolygon ogcPolygon = new OGCPolygon(p, null); + String result = ogcPolygon.asGeoJson(); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[100,0],[101,0],[101,1],[100,1],[100,0]],[[100.2,0.2],[100.2,0.8],[100.8,0.8],[100.8,0.2],[100.2,0.2]]],\"crs\":null}", result); + } + + @Test + public void testGeometryCollection() { + SpatialReference sr = SpatialReference.create(4326); + + StringBuilder geometrySb = new StringBuilder(); + geometrySb + .append("{\"type\" : \"GeometryCollection\", \"geometries\" : ["); + + OGCPoint point = new OGCPoint(new Point(1.0, 1.0), sr); + assertEquals("{\"x\":1,\"y\":1,\"spatialReference\":{\"wkid\":4326}}", + point.asJson()); + assertEquals( + "{\"type\":\"Point\",\"coordinates\":[1,1],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}", + point.asGeoJson()); + geometrySb.append(point.asGeoJson()).append(", "); + + OGCLineString line = new OGCLineString(new Polyline( + new Point(1.0, 1.0), new Point(2.0, 2.0)), 0, sr); + assertEquals( + "{\"paths\":[[[1,1],[2,2]]],\"spatialReference\":{\"wkid\":4326}}", + line.asJson()); + assertEquals( + "{\"type\":\"LineString\",\"coordinates\":[[1,1],[2,2]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}", + line.asGeoJson()); + geometrySb.append(line.asGeoJson()).append(", "); + + Polygon p = new Polygon(); + p.startPath(1.0, 1.0); + p.lineTo(2.0, 2.0); + p.lineTo(3.0, 1.0); + p.lineTo(2.0, 0.0); + + OGCPolygon polygon = new OGCPolygon(p, sr); + assertEquals( + "{\"rings\":[[[1,1],[2,2],[3,1],[2,0],[1,1]]],\"spatialReference\":{\"wkid\":4326}}", + polygon.asJson()); + assertEquals( + "{\"type\":\"Polygon\",\"coordinates\":[[[1,1],[2,0],[3,1],[2,2],[1,1]]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}", + polygon.asGeoJson()); + geometrySb.append(polygon.asGeoJson()).append("]}"); + + List geoms = new ArrayList(3); + geoms.add(point); + geoms.add(line); + geoms.add(polygon); + OGCConcreteGeometryCollection collection = new OGCConcreteGeometryCollection( + geoms, sr); + String s2 = collection.asGeoJson(); + + JSONObject json = null; + boolean valid = false; + try { + json = new JSONObject(s2); + valid = true; + } catch (Exception e) { + } + + assertTrue(valid); + + assertEquals("{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Point\",\"coordinates\":[1,1]},{\"type\":\"LineString\",\"coordinates\":[[1,1],[2,2]]},{\"type\":\"Polygon\",\"coordinates\":[[[1,1],[2,0],[3,1],[2,2],[1,1]]]}],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}", collection.asGeoJson()); + } + + @Test + public void testEmptyGeometryCollection() { + SpatialReference sr = SpatialReference.create(4326); + OGCConcreteGeometryCollection collection = new OGCConcreteGeometryCollection( + new ArrayList(), sr); + String s2 = collection.asGeoJson(); + assertEquals( + "{\"type\":\"GeometryCollection\",\"geometries\":[],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}", + collection.asGeoJson()); + } + + //Envelope is exported as a polygon (we don't support bbox, as it is not a GeoJson geometry, but simply a field)! + @Test + public void testEnvelope() { + Envelope e = new Envelope(); + e.setCoords(-180.0, -90.0, 180.0, 90.0); + String result = OperatorExportToGeoJson.local().execute(e); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}", result); + } + + @Test + public void testEmptyEnvelope() { + Envelope e = new Envelope(); + String result = OperatorExportToGeoJson.local().execute(e); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[]}", result); + } + + @Test + public void testEnvelopeGeometryEngine() { + Envelope e = new Envelope(); + e.setCoords(-180.0, -90.0, 180.0, 90.0); + String result = GeometryEngine.geometryToGeoJson(e); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]]}", result); + } + + @Test + public void testOldCRS() throws JSONException { + String inputStr = "{\"type\":\"Polygon\",\"coordinates\":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]], \"crs\":\"EPSG:4267\"}"; + MapGeometry mg = OperatorImportFromGeoJson.local().execute(GeoJsonImportFlags.geoJsonImportDefaults, Geometry.Type.Unknown, inputStr, null); + String result = GeometryEngine.geometryToGeoJson(mg.getSpatialReference(), mg.getGeometry()); + assertEquals("{\"type\":\"Polygon\",\"coordinates\":[[[-180,-90],[180,-90],[180,90],[-180,90],[-180,-90]]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4267\"}}}", result); + } + + // bbox is not supported anymore. + // @Test + // public void testEnvelope() { + // Envelope e = new Envelope(); + // e.setCoords(-180.0, -90.0, 180.0, 90.0); + // OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + // String result = exporter.execute(e); + // assertEquals("{\"bbox\":[-180.0,-90.0,180.0,90.0]}", result); + // } + // + // @Test + // public void testEmptyEnvelope() { + // Envelope e = new Envelope(); + // OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory.getOperator(Operator.Type.ExportToGeoJson); + // String result = exporter.execute(e); + // assertEquals("{\"bbox\":null}", result); + // } + // + // @Test + // public void testEnvelopeGeometryEngine() { + // Envelope e = new Envelope(); + // e.setCoords(-180.0, -90.0, 180.0, 90.0); + // String result = GeometryEngine.geometryToGeoJson(e); + // assertEquals("{\"bbox\":[-180.0,-90.0,180.0,90.0]}", result); + // } } diff --git a/src/test/java/com/esri/core/geometry/TestImportExport.java b/src/test/java/com/esri/core/geometry/TestImportExport.java index 99adee7a..1138c082 100644 --- a/src/test/java/com/esri/core/geometry/TestImportExport.java +++ b/src/test/java/com/esri/core/geometry/TestImportExport.java @@ -2,11 +2,14 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; + import junit.framework.TestCase; import org.json.JSONException; +import org.json.JSONObject; import org.junit.Test; public class TestImportExport extends TestCase { + @Override protected void setUp() throws Exception { super.setUp(); @@ -19,33 +22,35 @@ protected void tearDown() throws Exception { @Test public static void testImportExportShapePolygon() { - OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToESRIShape); - OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromESRIShape); +// { +// String s = "MULTIPOLYGON (((-1.4337158203098852 53.42590083930004, -1.4346462383651897 53.42590083930004, -1.4349713164114632 53.42426406667512, -1.4344808816770183 53.42391134176576, -1.4337158203098852 53.424339319373516, -1.4337158203098852 53.42590083930004, -1.4282226562499147 53.42590083930004, -1.4282226562499147 53.42262754610009, -1.423659941537096 53.42262754610009, -1.4227294921872726 53.42418897437618, -1.4199829101572732 53.42265258737483, -1.4172363281222147 53.42418897437334, -1.4144897460898278 53.42265258737625, -1.4144897460898278 53.42099079900008, -1.4117431640598568 53.42099079712516, -1.4117431640598568 53.41849780932388, -1.4112778948070286 53.41771711805022, -1.4114404909237805 53.41689867267529, -1.411277890108579 53.416080187950215, -1.4117431640598568 53.4152995338453, -1.4117431657531654 53.40953184824072, -1.41723632610001 53.40953184402311, -1.4172363281199125 53.406257299700044, -1.4227294921899158 53.406257299700044, -1.4227294921899158 53.40789459668797, -1.4254760767598498 53.40789460061099, -1.4262193642339867 53.40914148401417, -1.4273828468095076 53.409531853100034, -1.4337158203098852 53.409531790075235, -1.4337158203098852 53.41280609140024, -1.4392089843723568 53.41280609140024, -1.439208984371362 53.41608014067522, -1.441160015802268 53.41935368587538, -1.4427511170075604 53.41935368587538, -1.4447021484373863 53.42099064750012, -1.4501953124999432 53.42099064750012, -1.4501953124999432 53.43214683850347, -1.4513643355446106 53.434108816701794, -1.4502702625278232 53.43636597733034, -1.4494587195580948 53.437354845300334, -1.4431075935937656 53.437354845300334, -1.4372459179209045 53.43244635455021, -1.433996276212838 53.42917388040006, -1.4337158203098852 53.42917388040006, -1.4337158203098852 53.42590083930004)))"; +// Geometry g = OperatorImportFromWkt.local().execute(0, Geometry.Type.Unknown, s, null); +// boolean result1 = OperatorSimplify.local().isSimpleAsFeature(g, null, null); +// boolean result2 = OperatorSimplifyOGC.local().isSimpleOGC(g, null, true, null, null); +// Geometry simple = OperatorSimplifyOGC.local().execute(g, null, true, null); +// OperatorFactoryLocal.saveToWKTFileDbg("c:/temp/simplifiedeeee", simple, null); +// int i = 0; +// } + OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToESRIShape); + OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromESRIShape); Polygon polygon = makePolygon(); byte[] esriShape = GeometryEngine.geometryToEsriShape(polygon); - Geometry imported = GeometryEngine.geometryFromEsriShape(esriShape, - Geometry.Type.Unknown); + Geometry imported = GeometryEngine.geometryFromEsriShape(esriShape, Geometry.Type.Unknown); TestCommonMethods.compareGeometryContent((MultiPath) imported, polygon); // Test Import Polygon from Polygon ByteBuffer polygonShapeBuffer = exporterShape.execute(0, polygon); - Geometry polygonShapeGeometry = importerShape.execute(0, - Geometry.Type.Polygon, polygonShapeBuffer); + Geometry polygonShapeGeometry = importerShape.execute(0, Geometry.Type.Polygon, polygonShapeBuffer); - TestCommonMethods.compareGeometryContent( - (MultiPath) polygonShapeGeometry, polygon); + TestCommonMethods.compareGeometryContent((MultiPath) polygonShapeGeometry, polygon); // Test Import Envelope from Polygon - Geometry envelopeShapeGeometry = importerShape.execute(0, - Geometry.Type.Envelope, polygonShapeBuffer); + Geometry envelopeShapeGeometry = importerShape.execute(0, Geometry.Type.Envelope, polygonShapeBuffer); Envelope envelope = (Envelope) envelopeShapeGeometry; - @SuppressWarnings("unused") - Envelope env = new Envelope(), otherenv = new Envelope(); + @SuppressWarnings("unused") Envelope env = new Envelope(), otherenv = new Envelope(); polygon.queryEnvelope(otherenv); assertTrue(envelope.getXMin() == otherenv.getXMin()); assertTrue(envelope.getXMax() == otherenv.getXMax()); @@ -61,25 +66,20 @@ public static void testImportExportShapePolygon() { @Test public static void testImportExportShapePolyline() { - OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToESRIShape); - OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromESRIShape); + OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToESRIShape); + OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromESRIShape); Polyline polyline = makePolyline(); // Test Import Polyline from Polyline ByteBuffer polylineShapeBuffer = exporterShape.execute(0, polyline); - Geometry polylineShapeGeometry = importerShape.execute(0, - Geometry.Type.Polyline, polylineShapeBuffer); + Geometry polylineShapeGeometry = importerShape.execute(0, Geometry.Type.Polyline, polylineShapeBuffer); // TODO test this - TestCommonMethods.compareGeometryContent( - (MultiPath) polylineShapeGeometry, polyline); + TestCommonMethods.compareGeometryContent((MultiPath) polylineShapeGeometry, polyline); // Test Import Envelope from Polyline; - Geometry envelopeShapeGeometry = importerShape.execute(0, - Geometry.Type.Envelope, polylineShapeBuffer); + Geometry envelopeShapeGeometry = importerShape.execute(0, Geometry.Type.Envelope, polylineShapeBuffer); Envelope envelope = (Envelope) envelopeShapeGeometry; Envelope env = new Envelope(), otherenv = new Envelope(); @@ -92,32 +92,26 @@ public static void testImportExportShapePolyline() { Envelope1D interval, otherinterval; interval = envelope.queryInterval(VertexDescription.Semantics.Z, 0); - otherinterval = polyline - .queryInterval(VertexDescription.Semantics.Z, 0); + otherinterval = polyline.queryInterval(VertexDescription.Semantics.Z, 0); assertTrue(interval.vmin == otherinterval.vmin); assertTrue(interval.vmax == otherinterval.vmax); } @Test public static void testImportExportShapeMultiPoint() { - OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToESRIShape); - OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromESRIShape); + OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToESRIShape); + OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromESRIShape); MultiPoint multipoint = makeMultiPoint(); // Test Import MultiPoint from MultiPoint ByteBuffer multipointShapeBuffer = exporterShape.execute(0, multipoint); - MultiPoint multipointShapeGeometry = (MultiPoint) importerShape - .execute(0, Geometry.Type.MultiPoint, multipointShapeBuffer); + MultiPoint multipointShapeGeometry = (MultiPoint) importerShape.execute(0, Geometry.Type.MultiPoint, multipointShapeBuffer); - TestCommonMethods.compareGeometryContent( - (MultiPoint) multipointShapeGeometry, multipoint); + TestCommonMethods.compareGeometryContent((MultiPoint) multipointShapeGeometry, multipoint); // Test Import Envelope from MultiPoint - Geometry envelopeShapeGeometry = importerShape.execute(0, - Geometry.Type.Envelope, multipointShapeBuffer); + Geometry envelopeShapeGeometry = importerShape.execute(0, Geometry.Type.Envelope, multipointShapeBuffer); Envelope envelope = (Envelope) envelopeShapeGeometry; Envelope env = new Envelope(), otherenv = new Envelope(); @@ -130,32 +124,27 @@ public static void testImportExportShapeMultiPoint() { Envelope1D interval, otherinterval; interval = envelope.queryInterval(VertexDescription.Semantics.Z, 0); - otherinterval = multipoint.queryInterval(VertexDescription.Semantics.Z, - 0); + otherinterval = multipoint.queryInterval(VertexDescription.Semantics.Z, 0); assertTrue(interval.vmin == otherinterval.vmin); assertTrue(interval.vmax == otherinterval.vmax); interval = envelope.queryInterval(VertexDescription.Semantics.ID, 0); - otherinterval = multipoint.queryInterval( - VertexDescription.Semantics.ID, 0); + otherinterval = multipoint.queryInterval(VertexDescription.Semantics.ID, 0); assertTrue(interval.vmin == otherinterval.vmin); assertTrue(interval.vmax == otherinterval.vmax); } @Test public static void testImportExportShapePoint() { - OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToESRIShape); - OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromESRIShape); + OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToESRIShape); + OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromESRIShape); // Point Point point = makePoint(); // Test Import Point from Point ByteBuffer pointShapeBuffer = exporterShape.execute(0, point); - Point pointShapeGeometry = (Point) importerShape.execute(0, - Geometry.Type.Point, pointShapeBuffer); + Point pointShapeGeometry = (Point) importerShape.execute(0, Geometry.Type.Point, pointShapeBuffer); double x1 = point.getX(); double x2 = pointShapeGeometry.getX(); @@ -178,29 +167,24 @@ public static void testImportExportShapePoint() { assertTrue(id1 == id2); // Test Import Multipoint from Point - MultiPoint multipointShapeGeometry = (MultiPoint) importerShape - .execute(0, Geometry.Type.MultiPoint, pointShapeBuffer); + MultiPoint multipointShapeGeometry = (MultiPoint) importerShape.execute(0, Geometry.Type.MultiPoint, pointShapeBuffer); Point point2d = multipointShapeGeometry.getPoint(0); assertTrue(x1 == point2d.getX() && y1 == point2d.getY()); int pointCount = multipointShapeGeometry.getPointCount(); assertTrue(pointCount == 1); - z2 = multipointShapeGeometry.getAttributeAsDbl( - VertexDescription.Semantics.Z, 0, 0); + z2 = multipointShapeGeometry.getAttributeAsDbl(VertexDescription.Semantics.Z, 0, 0); assertTrue(z1 == z2); - m2 = multipointShapeGeometry.getAttributeAsDbl( - VertexDescription.Semantics.M, 0, 0); + m2 = multipointShapeGeometry.getAttributeAsDbl(VertexDescription.Semantics.M, 0, 0); assertTrue(m1 == m2); - id2 = multipointShapeGeometry.getAttributeAsInt( - VertexDescription.Semantics.ID, 0, 0); + id2 = multipointShapeGeometry.getAttributeAsInt(VertexDescription.Semantics.ID, 0, 0); assertTrue(id1 == id2); // Test Import Envelope from Point - Geometry envelopeShapeGeometry = importerShape.execute(0, - Geometry.Type.Envelope, pointShapeBuffer); + Geometry envelopeShapeGeometry = importerShape.execute(0, Geometry.Type.Envelope, pointShapeBuffer); Envelope envelope = (Envelope) envelopeShapeGeometry; Envelope env = new Envelope(), otherenv = new Envelope(); @@ -225,17 +209,14 @@ public static void testImportExportShapePoint() { @Test public static void testImportExportShapeEnvelope() { - OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToESRIShape); - OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromESRIShape); + OperatorExportToESRIShape exporterShape = (OperatorExportToESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToESRIShape); + OperatorImportFromESRIShape importerShape = (OperatorImportFromESRIShape) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromESRIShape); // Test Export Envelope to Polygon Envelope envelope = makeEnvelope(); ByteBuffer polygonShapeBuffer = exporterShape.execute(0, envelope); - Polygon polygon = (Polygon) importerShape.execute(0, - Geometry.Type.Polygon, polygonShapeBuffer); + Polygon polygon = (Polygon) importerShape.execute(0, Geometry.Type.Polygon, polygonShapeBuffer); int pointCount = polygon.getPointCount(); assertTrue(pointCount == 4); @@ -245,26 +226,21 @@ public static void testImportExportShapeEnvelope() { // interval = envelope.queryInterval(VertexDescription.Semantics.Z, 0); Point point3d; point3d = polygon.getPoint(0); - assertTrue(point3d.getX() == env.getXMin() - && point3d.getY() == env.getYMin());// && point3d.z == - // interval.vmin); + assertTrue(point3d.getX() == env.getXMin() && point3d.getY() == env.getYMin());// && point3d.z == + // interval.vmin); point3d = polygon.getPoint(1); - assertTrue(point3d.getX() == env.getXMin() - && point3d.getY() == env.getYMax());// && point3d.z == - // interval.vmax); + assertTrue(point3d.getX() == env.getXMin() && point3d.getY() == env.getYMax());// && point3d.z == + // interval.vmax); point3d = polygon.getPoint(2); - assertTrue(point3d.getX() == env.getXMax() - && point3d.getY() == env.getYMax());// && point3d.z == - // interval.vmin); + assertTrue(point3d.getX() == env.getXMax() && point3d.getY() == env.getYMax());// && point3d.z == + // interval.vmin); point3d = polygon.getPoint(3); - assertTrue(point3d.getX() == env.getXMax() - && point3d.getY() == env.getYMin());// && point3d.z == - // interval.vmax); + assertTrue(point3d.getX() == env.getXMax() && point3d.getY() == env.getYMin());// && point3d.z == + // interval.vmax); Envelope1D interval; interval = envelope.queryInterval(VertexDescription.Semantics.M, 0); - double m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 0, - 0); + double m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 0, 0); assertTrue(m == interval.vmin); m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 1, 0); assertTrue(m == interval.vmax); @@ -274,8 +250,7 @@ public static void testImportExportShapeEnvelope() { assertTrue(m == interval.vmax); interval = envelope.queryInterval(VertexDescription.Semantics.ID, 0); - double id = polygon.getAttributeAsDbl(VertexDescription.Semantics.ID, - 0, 0); + double id = polygon.getAttributeAsDbl(VertexDescription.Semantics.ID, 0, 0); assertTrue(id == interval.vmin); id = polygon.getAttributeAsDbl(VertexDescription.Semantics.ID, 1, 0); assertTrue(id == interval.vmax); @@ -287,12 +262,10 @@ public static void testImportExportShapeEnvelope() { @Test public static void testImportExportWkbGeometryCollection() { - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); int offset = 0; - ByteBuffer wkbBuffer = ByteBuffer.allocate(600).order( - ByteOrder.nativeOrder()); + ByteBuffer wkbBuffer = ByteBuffer.allocate(600).order(ByteOrder.nativeOrder()); wkbBuffer.put(offset, (byte) WkbByteOrder.wkbNDR); offset += 1; // byte order wkbBuffer.putInt(offset, WkbGeometryType.wkbGeometryCollection); @@ -342,7 +315,7 @@ public static void testImportExportWkbGeometryCollection() { wkbBuffer.putInt(offset, WkbGeometryType.wkbGeometryCollection); offset += 4; wkbBuffer.putInt(offset, 0); // 0 geometries, for empty - // geometrycollection + // geometrycollection offset += 4; wkbBuffer.put(offset, (byte) WkbByteOrder.wkbNDR); offset += 1; @@ -368,8 +341,7 @@ public static void testImportExportWkbGeometryCollection() { offset += 8; // "GeometryCollection( Point (0 0), GeometryCollection( LineString empty, Polygon empty, MultiPolygon empty, MultiLineString empty, MultiPoint empty ), Point (13 17) )"; - OGCStructure structure = importerWKB.executeOGC(0, wkbBuffer, null).m_structures - .get(0); + OGCStructure structure = importerWKB.executeOGC(0, wkbBuffer, null).m_structures.get(0); assertTrue(structure.m_type == 7); assertTrue(structure.m_structures.get(0).m_type == 1); @@ -395,17 +367,13 @@ public static void testImportExportWkbGeometryCollection() { @Test public static void testImportExportWKBPolygon() { - OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkb); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); // Test Import Polygon with bad rings int offset = 0; - ByteBuffer wkbBuffer = ByteBuffer.allocate(500).order( - ByteOrder.nativeOrder()); + ByteBuffer wkbBuffer = ByteBuffer.allocate(500).order(ByteOrder.nativeOrder()); wkbBuffer.put(offset, (byte) WkbByteOrder.wkbNDR); offset += 1; // byte order wkbBuffer.putInt(offset, WkbGeometryType.wkbPolygon); @@ -505,17 +473,13 @@ public static void testImportExportWKBPolygon() { wkbBuffer.putDouble(offset, 67.0); offset += 8; // y - Geometry p = importerWKB.execute(0, Geometry.Type.Polygon, wkbBuffer, - null); + Geometry p = importerWKB.execute(0, Geometry.Type.Polygon, wkbBuffer, null); int pc = ((Polygon) p).getPathCount(); String wktString = exporterWKT.execute(0, p, null); - assertTrue(wktString - .equals("MULTIPOLYGON (((0 0, 10 10, 0 10, 0 0), (36 17, 36 17, 36 17), (19 19, -19 -19, 19 19), (23 88, 83 87, 59 79, 13 43, 23 88), (23 88, 67 79, 88 43, 23 88), (23 88, 67 88, 88 43, 23 88), (23 67, 43 67, 23 67)))")); + assertTrue(wktString.equals("MULTIPOLYGON (((0 0, 10 10, 0 10, 0 0), (36 17, 36 17, 36 17), (19 19, -19 -19, 19 19), (23 88, 83 87, 59 79, 13 43, 23 88), (23 88, 67 79, 88 43, 23 88), (23 88, 67 88, 88 43, 23 88), (23 67, 43 67, 23 67)))")); - wktString = exporterWKT.execute(WktExportFlags.wktExportPolygon, p, - null); - assertTrue(wktString - .equals("POLYGON ((0 0, 10 10, 0 10, 0 0), (36 17, 36 17, 36 17), (19 19, -19 -19, 19 19), (23 88, 83 87, 59 79, 13 43, 23 88), (23 88, 67 79, 88 43, 23 88), (23 88, 67 88, 88 43, 23 88), (23 67, 43 67, 23 67))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportPolygon, p, null); + assertTrue(wktString.equals("POLYGON ((0 0, 10 10, 0 10, 0 0), (36 17, 36 17, 36 17), (19 19, -19 -19, 19 19), (23 88, 83 87, 59 79, 13 43, 23 88), (23 88, 67 79, 88 43, 23 88), (23 88, 67 88, 88 43, 23 88), (23 67, 43 67, 23 67))")); Polygon polygon = makePolygon(); @@ -523,48 +487,37 @@ public static void testImportExportWKBPolygon() { ByteBuffer polygonWKBBuffer = exporterWKB.execute(0, polygon, null); int wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPolygonZM); - Geometry polygonWKBGeometry = importerWKB.execute(0, - Geometry.Type.Polygon, polygonWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polygonWKBGeometry, polygon); + Geometry polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polygonWKBGeometry, polygon); // Test WKB_export_multi_polygon on nonempty single part polygon Polygon polygon2 = makePolygon2(); assertTrue(polygon2.getPathCount() == 1); - polygonWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportMultiPolygon, polygon2, null); - polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polygonWKBGeometry, polygon2); + polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportMultiPolygon, polygon2, null); + polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polygonWKBGeometry, polygon2); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPolygonZM); // Test WKB_export_polygon on nonempty single part polygon assertTrue(polygon2.getPathCount() == 1); - polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, - polygon2, null); - polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polygonWKBGeometry, polygon2); + polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, polygon2, null); + polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polygonWKBGeometry, polygon2); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPolygonZM); // Test WKB_export_polygon on empty polygon Polygon polygon3 = new Polygon(); - polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, - polygon3, null); - polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null); + polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, polygon3, null); + polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null); assertTrue(polygonWKBGeometry.isEmpty() == true); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPolygon); // Test WKB_export_defaults on empty polygon polygonWKBBuffer = exporterWKB.execute(0, polygon3, null); - polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null); + polygonWKBGeometry = importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null); assertTrue(polygonWKBGeometry.isEmpty() == true); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPolygon); @@ -572,18 +525,14 @@ public static void testImportExportWKBPolygon() { @Test public static void testImportExportWKBPolyline() { - OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkb); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); // Test Import Polyline with bad paths (i.e. paths with one point or // zero points) int offset = 0; - ByteBuffer wkbBuffer = ByteBuffer.allocate(500).order( - ByteOrder.nativeOrder()); + ByteBuffer wkbBuffer = ByteBuffer.allocate(500).order(ByteOrder.nativeOrder()); wkbBuffer.put(offset, (byte) WkbByteOrder.wkbNDR); offset += 1; // byte order wkbBuffer.putInt(offset, WkbGeometryType.wkbMultiLineString); @@ -635,16 +584,14 @@ public static void testImportExportWKBPolyline() { wkbBuffer.putDouble(offset, 88); offset += 8; // y - Polyline p = (Polyline) (importerWKB.execute(0, Geometry.Type.Polyline, - wkbBuffer, null)); + Polyline p = (Polyline) (importerWKB.execute(0, Geometry.Type.Polyline, wkbBuffer, null)); int pc = p.getPointCount(); int pac = p.getPathCount(); assertTrue(p.getPointCount() == 7); assertTrue(p.getPathCount() == 3); String wktString = exporterWKT.execute(0, p, null); - assertTrue(wktString - .equals("MULTILINESTRING ((36 17, 36 17), (19 19, 19 19), (88 29, 13 43, 59 88))")); + assertTrue(wktString.equals("MULTILINESTRING ((36 17, 36 17), (19 19, 19 19), (88 29, 13 43, 59 88))")); Polyline polyline = makePolyline(); polyline.dropAttribute(VertexDescription.Semantics.ID); @@ -653,48 +600,37 @@ public static void testImportExportWKBPolyline() { ByteBuffer polylineWKBBuffer = exporterWKB.execute(0, polyline, null); int wkbType = polylineWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiLineStringZM); - Geometry polylineWKBGeometry = importerWKB.execute(0, - Geometry.Type.Polyline, polylineWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polylineWKBGeometry, polyline); + Geometry polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, polylineWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polylineWKBGeometry, polyline); // Test wkbExportMultiPolyline on nonempty single part polyline Polyline polyline2 = makePolyline2(); assertTrue(polyline2.getPathCount() == 1); - polylineWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportMultiLineString, polyline2, null); - polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, - polylineWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polylineWKBGeometry, polyline2); + polylineWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportMultiLineString, polyline2, null); + polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, polylineWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polylineWKBGeometry, polyline2); wkbType = polylineWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiLineStringZM); // Test wkbExportPolyline on nonempty single part polyline assertTrue(polyline2.getPathCount() == 1); - polylineWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportLineString, polyline2, null); - polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, - polylineWKBBuffer, null); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) polylineWKBGeometry, polyline2); + polylineWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportLineString, polyline2, null); + polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, polylineWKBBuffer, null); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) polylineWKBGeometry, polyline2); wkbType = polylineWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbLineStringZM); // Test wkbExportPolyline on empty polyline Polyline polyline3 = new Polyline(); - polylineWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportLineString, polyline3, null); - polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, - polylineWKBBuffer, null); + polylineWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportLineString, polyline3, null); + polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, polylineWKBBuffer, null); assertTrue(polylineWKBGeometry.isEmpty() == true); wkbType = polylineWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbLineString); // Test WKB_export_defaults on empty polyline polylineWKBBuffer = exporterWKB.execute(0, polyline3, null); - polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, - polylineWKBBuffer, null); + polylineWKBGeometry = importerWKB.execute(0, Geometry.Type.Polyline, polylineWKBBuffer, null); assertTrue(polylineWKBGeometry.isEmpty() == true); wkbType = polylineWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiLineString); @@ -702,53 +638,42 @@ public static void testImportExportWKBPolyline() { @Test public static void testImportExportWKBMultiPoint() { - OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkb); - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); MultiPoint multipoint = makeMultiPoint(); multipoint.dropAttribute(VertexDescription.Semantics.ID); // Test Import Multi_point from Multi_point - ByteBuffer multipointWKBBuffer = exporterWKB.execute(0, multipoint, - null); + ByteBuffer multipointWKBBuffer = exporterWKB.execute(0, multipoint, null); int wkbType = multipointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPointZ); - MultiPoint multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, - Geometry.Type.MultiPoint, multipointWKBBuffer, null)); - TestCommonMethods.compareGeometryContent( - (MultiVertexGeometry) multipointWKBGeometry, multipoint); + MultiPoint multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, Geometry.Type.MultiPoint, multipointWKBBuffer, null)); + TestCommonMethods.compareGeometryContent((MultiVertexGeometry) multipointWKBGeometry, multipoint); // Test WKB_export_point on nonempty single point Multi_point MultiPoint multipoint2 = makeMultiPoint2(); assertTrue(multipoint2.getPointCount() == 1); - ByteBuffer pointWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportPoint, multipoint2, null); - Point pointWKBGeometry = (Point) (importerWKB.execute(0, - Geometry.Type.Point, pointWKBBuffer, null)); + ByteBuffer pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, multipoint2, null); + Point pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); Point3D point3d, mpoint3d; point3d = pointWKBGeometry.getXYZ(); mpoint3d = multipoint2.getXYZ(0); - assertTrue(point3d.x == mpoint3d.x && point3d.y == mpoint3d.y - && point3d.z == mpoint3d.z); + assertTrue(point3d.x == mpoint3d.x && point3d.y == mpoint3d.y && point3d.z == mpoint3d.z); wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPointZ); // Test WKB_export_point on empty Multi_point MultiPoint multipoint3 = new MultiPoint(); - pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, - multipoint3, null); - pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, - pointWKBBuffer, null)); + pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, multipoint3, null); + pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); assertTrue(pointWKBGeometry.isEmpty() == true); wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPoint); // Test WKB_export_defaults on empty Multi_point multipointWKBBuffer = exporterWKB.execute(0, multipoint3, null); - multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, - Geometry.Type.MultiPoint, multipointWKBBuffer, null)); + multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, Geometry.Type.MultiPoint, multipointWKBBuffer, null)); assertTrue(multipointWKBGeometry.isEmpty() == true); wkbType = multipointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPoint); @@ -756,10 +681,8 @@ public static void testImportExportWKBMultiPoint() { @Test public static void testImportExportWKBPoint() { - OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkb); - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); // Point Point point = makePoint(); @@ -768,8 +691,7 @@ public static void testImportExportWKBPoint() { ByteBuffer pointWKBBuffer = exporterWKB.execute(0, point, null); int wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPointZM); - Point pointWKBGeometry = (Point) (importerWKB.execute(0, - Geometry.Type.Point, pointWKBBuffer, null)); + Point pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); double x_1 = point.getX(); double x2 = pointWKBGeometry.getX(); @@ -790,27 +712,22 @@ public static void testImportExportWKBPoint() { // Test WKB_export_defaults on empty point Point point2 = new Point(); pointWKBBuffer = exporterWKB.execute(0, point2, null); - pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, - pointWKBBuffer, null)); + pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); assertTrue(pointWKBGeometry.isEmpty() == true); wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPoint); // Test WKB_export_point on empty point - pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, - point2, null); - pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, - pointWKBBuffer, null)); + pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, point2, null); + pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); assertTrue(pointWKBGeometry.isEmpty() == true); wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPoint); // Test WKB_export_multi_point on empty point MultiPoint multipoint = new MultiPoint(); - ByteBuffer multipointWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportMultiPoint, multipoint, null); - MultiPoint multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, - Geometry.Type.MultiPoint, multipointWKBBuffer, null)); + ByteBuffer multipointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportMultiPoint, multipoint, null); + MultiPoint multipointWKBGeometry = (MultiPoint) (importerWKB.execute(0, Geometry.Type.MultiPoint, multipointWKBBuffer, null)); assertTrue(multipointWKBGeometry.isEmpty() == true); wkbType = multipointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPoint); @@ -818,25 +735,20 @@ public static void testImportExportWKBPoint() { // Test WKB_export_point on nonempty single point Multi_point MultiPoint multipoint2 = makeMultiPoint2(); assertTrue(multipoint2.getPointCount() == 1); - pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, - multipoint2, null); - pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, - pointWKBBuffer, null)); + pointWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPoint, multipoint2, null); + pointWKBGeometry = (Point) (importerWKB.execute(0, Geometry.Type.Point, pointWKBBuffer, null)); Point3D point3d, mpoint3d; point3d = pointWKBGeometry.getXYZ(); mpoint3d = multipoint2.getXYZ(0); - assertTrue(point3d.x == mpoint3d.x && point3d.y == mpoint3d.y - && point3d.z == mpoint3d.z); + assertTrue(point3d.x == mpoint3d.x && point3d.y == mpoint3d.y && point3d.z == mpoint3d.z); wkbType = pointWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPointZ); } @Test public static void testImportExportWKBEnvelope() { - OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkb); - OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkb); + OperatorExportToWkb exporterWKB = (OperatorExportToWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkb); + OperatorImportFromWkb importerWKB = (OperatorImportFromWkb) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkb); // Test Export Envelope to Polygon (WKB_export_defaults) Envelope envelope = makeEnvelope(); @@ -845,8 +757,7 @@ public static void testImportExportWKBEnvelope() { ByteBuffer polygonWKBBuffer = exporterWKB.execute(0, envelope, null); int wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPolygonZM); - Polygon polygon = (Polygon) (importerWKB.execute(0, - Geometry.Type.Polygon, polygonWKBBuffer, null)); + Polygon polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null)); int point_count = polygon.getPointCount(); assertTrue(point_count == 4); @@ -857,21 +768,16 @@ public static void testImportExportWKBEnvelope() { interval = envelope.queryInterval(VertexDescription.Semantics.Z, 0); Point3D point3d; point3d = polygon.getXYZ(0); - assertTrue(point3d.x == env.xmin && point3d.y == env.ymin - && point3d.z == interval.vmin); + assertTrue(point3d.x == env.xmin && point3d.y == env.ymin && point3d.z == interval.vmin); point3d = polygon.getXYZ(1); - assertTrue(point3d.x == env.xmin && point3d.y == env.ymax - && point3d.z == interval.vmax); + assertTrue(point3d.x == env.xmin && point3d.y == env.ymax && point3d.z == interval.vmax); point3d = polygon.getXYZ(2); - assertTrue(point3d.x == env.xmax && point3d.y == env.ymax - && point3d.z == interval.vmin); + assertTrue(point3d.x == env.xmax && point3d.y == env.ymax && point3d.z == interval.vmin); point3d = polygon.getXYZ(3); - assertTrue(point3d.x == env.xmax && point3d.y == env.ymin - && point3d.z == interval.vmax); + assertTrue(point3d.x == env.xmax && point3d.y == env.ymin && point3d.z == interval.vmax); interval = envelope.queryInterval(VertexDescription.Semantics.M, 0); - double m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 0, - 0); + double m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 0, 0); assertTrue(m == interval.vmin); m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 1, 0); assertTrue(m == interval.vmax); @@ -881,29 +787,23 @@ public static void testImportExportWKBEnvelope() { assertTrue(m == interval.vmax); // Test WKB_export_multi_polygon on nonempty Envelope - polygonWKBBuffer = exporterWKB.execute( - WkbExportFlags.wkbExportMultiPolygon, envelope, null); + polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportMultiPolygon, envelope, null); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbMultiPolygonZM); - polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null)); + polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null)); point_count = polygon.getPointCount(); assertTrue(point_count == 4); envelope.queryEnvelope2D(env); interval = envelope.queryInterval(VertexDescription.Semantics.Z, 0); point3d = polygon.getXYZ(0); - assertTrue(point3d.x == env.xmin && point3d.y == env.ymin - && point3d.z == interval.vmin); + assertTrue(point3d.x == env.xmin && point3d.y == env.ymin && point3d.z == interval.vmin); point3d = polygon.getXYZ(1); - assertTrue(point3d.x == env.xmin && point3d.y == env.ymax - && point3d.z == interval.vmax); + assertTrue(point3d.x == env.xmin && point3d.y == env.ymax && point3d.z == interval.vmax); point3d = polygon.getXYZ(2); - assertTrue(point3d.x == env.xmax && point3d.y == env.ymax - && point3d.z == interval.vmin); + assertTrue(point3d.x == env.xmax && point3d.y == env.ymax && point3d.z == interval.vmin); point3d = polygon.getXYZ(3); - assertTrue(point3d.x == env.xmax && point3d.y == env.ymin - && point3d.z == interval.vmax); + assertTrue(point3d.x == env.xmax && point3d.y == env.ymin && point3d.z == interval.vmax); interval = envelope.queryInterval(VertexDescription.Semantics.M, 0); m = polygon.getAttributeAsDbl(VertexDescription.Semantics.M, 0, 0); @@ -920,34 +820,28 @@ public static void testImportExportWKBEnvelope() { polygonWKBBuffer = exporterWKB.execute(0, envelope2, null); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPolygon); - polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null)); + polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null)); assertTrue(polygon.isEmpty()); // Test WKB_export_polygon on empty Envelope - polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, - envelope2, null); + polygonWKBBuffer = exporterWKB.execute(WkbExportFlags.wkbExportPolygon, envelope2, null); wkbType = polygonWKBBuffer.getInt(1); assertTrue(wkbType == WkbGeometryType.wkbPolygon); - polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, - polygonWKBBuffer, null)); + polygon = (Polygon) (importerWKB.execute(0, Geometry.Type.Polygon, polygonWKBBuffer, null)); assertTrue(polygon.isEmpty()); } @Test public static void testImportExportWktGeometryCollection() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); String wktString; Envelope2D envelope = new Envelope2D(); WktParser wktParser = new WktParser(); wktString = "GeometryCollection( Point (0 0), GeometryCollection( Point (0 0) , Point (1 1) , Point (2 2), LineString empty ), Point (1 1), Point (2 2) )"; - OGCStructure structure = importerWKT.executeOGC(0, wktString, null).m_structures - .get(0); + OGCStructure structure = importerWKT.executeOGC(0, wktString, null).m_structures.get(0); assertTrue(structure.m_type == 7); assertTrue(structure.m_structures.get(0).m_type == 1); @@ -964,10 +858,8 @@ public static void testImportExportWktGeometryCollection() { @Test public static void testImportExportWktMultiPolygon() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); Polygon polygon; String wktString; @@ -975,16 +867,13 @@ public static void testImportExportWktMultiPolygon() { WktParser wktParser = new WktParser(); // Test Import from MultiPolygon - wktString = "Multipolygon M empty"; - polygon = (Polygon) importerWKT.execute(0, Geometry.Type.Polygon, - wktString, null); + polygon = (Polygon) importerWKT.execute(0, Geometry.Type.Polygon, wktString, null); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.M)); - polygon = (Polygon) GeometryEngine.geometryFromWkt(wktString, 0, - Geometry.Type.Unknown); + polygon = (Polygon) GeometryEngine.geometryFromWkt(wktString, 0, Geometry.Type.Unknown); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.M)); @@ -996,43 +885,36 @@ public static void testImportExportWktMultiPolygon() { assertTrue(wktString.equals("MULTIPOLYGON M EMPTY")); wktString = "Multipolygon Z (empty, (empty, (10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3), empty, (10 10 1, 12 12 1)), empty, ((90 90 88, 60 90 7, 60 60 7), empty, (70 70 7, 80 80 7, 70 80 7, 70 70 7)), empty)"; - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, - wktString, null)); + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, wktString, null)); assertTrue(polygon != null); polygon.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 90 - && envelope.ymin == 10 && envelope.ymax == 90); + assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && envelope.ymin == 10 && envelope.ymax == 90); assertTrue(polygon.getPointCount() == 14); assertTrue(polygon.getPathCount() == 5); // assertTrue(polygon.calculate_area_2D() > 0.0); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); - double z = polygon.getAttributeAsDbl(VertexDescription.Semantics.Z, 0, - 0); + double z = polygon.getAttributeAsDbl(VertexDescription.Semantics.Z, 0, 0); assertTrue(z == 5); // Test Export to WKT MultiPolygon wktString = exporterWKT.execute(0, polygon, null); - assertTrue(wktString - .equals("MULTIPOLYGON Z (((10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3, 12 12 3, 12 12 3), (10 10 1, 12 12 1, 10 10 1)), ((90 90 88, 60 90 7, 60 60 7, 90 90 88), (70 70 7, 70 80 7, 80 80 7, 70 70 7)))")); + assertTrue(wktString.equals("MULTIPOLYGON Z (((10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3, 12 12 3, 12 12 3), (10 10 1, 12 12 1, 10 10 1)), ((90 90 88, 60 90 7, 60 60 7, 90 90 88), (70 70 7, 70 80 7, 80 80 7, 70 70 7)))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } // Test import Polygon wktString = "POLYGON z (EMPTY, EMPTY, (10 10 5, 10 20 5, 20 20 5, 20 10 5), (12 12 3), EMPTY, (10 10 1, 12 12 1), EMPTY, (60 60 7, 60 90 7, 90 90 7, 60 60 7), EMPTY, (70 70 7, 70 80 7, 80 80 7), EMPTY)"; - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, - wktString, null)); + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, wktString, null)); assertTrue(polygon != null); assertTrue(polygon.getPointCount() == 14); assertTrue(polygon.getPathCount() == 5); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); // Test Export to WKT Polygon - wktString = exporterWKT.execute(WktExportFlags.wktExportPolygon, - polygon, null); - assertTrue(wktString - .equals("POLYGON Z ((10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3, 12 12 3, 12 12 3), (10 10 1, 12 12 1, 10 10 1), (60 60 7, 60 90 7, 90 90 7, 60 60 7), (70 70 7, 70 80 7, 80 80 7, 70 70 7))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportPolygon, polygon, null); + assertTrue(wktString.equals("POLYGON Z ((10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3, 12 12 3, 12 12 3), (10 10 1, 12 12 1, 10 10 1), (60 60 7, 60 90 7, 90 90 7, 60 60 7), (70 70 7, 70 80 7, 80 80 7, 70 70 7))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } @@ -1042,16 +924,13 @@ public static void testImportExportWktMultiPolygon() { polygon.queryEnvelope(env); wktString = exporterWKT.execute(0, env, null); - assertTrue(wktString - .equals("POLYGON Z ((10 10 1, 90 10 7, 90 90 1, 10 90 7, 10 10 1))")); + assertTrue(wktString.equals("POLYGON Z ((10 10 1, 90 10 7, 90 90 1, 10 90 7, 10 10 1))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } - wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, - env, null); - assertTrue(wktString - .equals("MULTIPOLYGON Z (((10 10 1, 90 10 7, 90 90 1, 10 90 7, 10 10 1)))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, env, null); + assertTrue(wktString.equals("MULTIPOLYGON Z (((10 10 1, 90 10 7, 90 90 1, 10 90 7, 10 10 1)))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } @@ -1064,44 +943,38 @@ public static void testImportExportWktMultiPolygon() { while (wktParser.nextToken() != WktParser.WktToken.not_available) { } - wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, - env, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, env, null); assertTrue(wktString.equals("MULTIPOLYGON Z EMPTY")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } wktString = "MULTIPOLYGON (((5 10, 8 10, 10 10, 10 0, 0 0, 0 10, 2 10, 5 10)))"; // ring - // is - // oriented - // clockwise - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, - wktString, null)); + // is + // oriented + // clockwise + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, wktString, null)); assertTrue(polygon != null); assertTrue(polygon.calculateArea2D() > 0); wktString = "MULTIPOLYGON Z (((90 10 7, 10 10 1, 10 90 7, 90 90 1, 90 10 7)))"; // ring - // is - // oriented - // clockwise - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, - wktString, null)); + // is + // oriented + // clockwise + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Polygon, wktString, null)); assertTrue(polygon != null); assertTrue(polygon.getPointCount() == 4); assertTrue(polygon.getPathCount() == 1); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(polygon.calculateArea2D() > 0); - wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, - polygon, null); - assertTrue(wktString - .equals("MULTIPOLYGON Z (((90 10 7, 90 90 1, 10 90 7, 10 10 1, 90 10 7)))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPolygon, polygon, null); + assertTrue(wktString.equals("MULTIPOLYGON Z (((90 10 7, 90 90 1, 10 90 7, 10 10 1, 90 10 7)))")); } @Test public static void testImportExportWktPolygon() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); // OperatorExportToWkt exporterWKT = // (OperatorExportToWkt)OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); @@ -1110,29 +983,24 @@ public static void testImportExportWktPolygon() { Envelope2D envelope = new Envelope2D(); // Test Import from Polygon - wktString = "Polygon ZM empty"; - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.M)); wktString = "Polygon z (empty, (10 10 5, 20 10 5, 20 20 5, 10 20 5, 10 10 5), (12 12 3), empty, (10 10 1, 12 12 1))"; - polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polygon = (Polygon) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polygon != null); polygon.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 20 - && envelope.ymin == 10 && envelope.ymax == 20); + assertTrue(envelope.xmin == 10 && envelope.xmax == 20 && envelope.ymin == 10 && envelope.ymax == 20); assertTrue(polygon.getPointCount() == 8); assertTrue(polygon.getPathCount() == 3); assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); wktString = "polygon ((35 10, 10 20, 15 40, 45 45, 35 10), (20 30, 35 35, 30 20, 20 30))"; - Polygon polygon2 = (Polygon) (importerWKT.execute(0, - Geometry.Type.Unknown, wktString, null)); + Polygon polygon2 = (Polygon) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polygon2 != null); // wktString = exporterWKT.execute(0, *polygon2, null); @@ -1140,8 +1008,7 @@ public static void testImportExportWktPolygon() { @Test public static void testImportExportWktLineString() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); // OperatorExportToWkt exporterWKT = // (OperatorExportToWkt)OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); @@ -1150,22 +1017,18 @@ public static void testImportExportWktLineString() { Envelope2D envelope = new Envelope2D(); // Test Import from LineString - wktString = "LineString ZM empty"; - polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polyline != null); assertTrue(polyline.isEmpty()); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); wktString = "LineString m (10 10 5, 10 20 5, 20 20 5, 20 10 5)"; - polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polyline != null); polyline.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 20 - && envelope.ymin == 10 && envelope.ymax == 20); + assertTrue(envelope.xmin == 10 && envelope.xmax == 20 && envelope.ymin == 10 && envelope.ymax == 20); assertTrue(polyline.getPointCount() == 4); assertTrue(polyline.getPathCount() == 1); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); @@ -1173,10 +1036,8 @@ public static void testImportExportWktLineString() { @Test public static void testImportExportWktMultiLineString() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); Polyline polyline; String wktString; @@ -1184,49 +1045,40 @@ public static void testImportExportWktMultiLineString() { WktParser wktParser = new WktParser(); // Test Import from MultiLineString - wktString = "MultiLineStringZMempty"; - polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polyline != null); assertTrue(polyline.isEmpty()); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); wktString = "MultiLineStringm(empty, empty, (10 10 5, 10 20 5, 20 88 5, 20 10 5), (12 88 3), empty, (10 10 1, 12 12 1), empty, (88 60 7, 60 90 7, 90 90 7), empty, (70 70 7, 70 80 7, 80 80 7), empty)"; - polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polyline != null); polyline.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 90 - && envelope.ymin == 10 && envelope.ymax == 90); + assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && envelope.ymin == 10 && envelope.ymax == 90); assertTrue(polyline.getPointCount() == 14); assertTrue(polyline.getPathCount() == 5); assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); wktString = exporterWKT.execute(0, polyline, null); - assertTrue(wktString - .equals("MULTILINESTRING M ((10 10 5, 10 20 5, 20 88 5, 20 10 5), (12 88 3, 12 88 3), (10 10 1, 12 12 1), (88 60 7, 60 90 7, 90 90 7), (70 70 7, 70 80 7, 80 80 7))")); + assertTrue(wktString.equals("MULTILINESTRING M ((10 10 5, 10 20 5, 20 88 5, 20 10 5), (12 88 3, 12 88 3), (10 10 1, 12 12 1), (88 60 7, 60 90 7, 90 90 7), (70 70 7, 70 80 7, 80 80 7))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } // Test Import LineString wktString = "Linestring Z(10 10 5, 10 20 5, 20 20 5, 20 10 5)"; - polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + polyline = (Polyline) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(polyline.getPointCount() == 4); - wktString = exporterWKT.execute(WktExportFlags.wktExportLineString, - polyline, null); - assertTrue(wktString - .equals("LINESTRING Z (10 10 5, 10 20 5, 20 20 5, 20 10 5)")); + wktString = exporterWKT.execute(WktExportFlags.wktExportLineString, polyline, null); + assertTrue(wktString.equals("LINESTRING Z (10 10 5, 10 20 5, 20 20 5, 20 10 5)")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } wktString = exporterWKT.execute(0, polyline, null); - assertTrue(wktString - .equals("MULTILINESTRING Z ((10 10 5, 10 20 5, 20 20 5, 20 10 5))")); + assertTrue(wktString.equals("MULTILINESTRING Z ((10 10 5, 10 20 5, 20 20 5, 20 10 5))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } @@ -1234,10 +1086,8 @@ public static void testImportExportWktMultiLineString() { @Test public static void testImportExportWktMultiPoint() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); MultiPoint multipoint; String wktString; @@ -1245,10 +1095,8 @@ public static void testImportExportWktMultiPoint() { WktParser wktParser = new WktParser(); // Test Import from Multi_point - wktString = " MultiPoint ZM empty"; - multipoint = (MultiPoint) (importerWKT.execute(0, - Geometry.Type.Unknown, wktString, null)); + multipoint = (MultiPoint) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(multipoint != null); assertTrue(multipoint.isEmpty()); assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); @@ -1260,8 +1108,7 @@ public static void testImportExportWktMultiPoint() { while (wktParser.nextToken() != WktParser.WktToken.not_available) { } - wktString = exporterWKT.execute(WktExportFlags.wktExportPoint, - multipoint, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportPoint, multipoint, null); assertTrue(wktString.equals("POINT ZM EMPTY")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { @@ -1271,10 +1118,8 @@ public static void testImportExportWktMultiPoint() { multipoint.add(118.15114354234563, 33.82234433423462345); multipoint.add(88, 88); - wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision10, - multipoint, null); - assertTrue(wktString - .equals("MULTIPOINT ((118.1511435 33.82234433), (88 88))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision10, multipoint, null); + assertTrue(wktString.equals("MULTIPOINT ((118.1511435 33.82234433), (88 88))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } @@ -1290,8 +1135,7 @@ public static void testImportExportWktMultiPoint() { } wktString = "Multipoint zm (empty, empty, (10 88 88 33), (10 20 5 33), (20 20 5 33), (20 10 5 33), (12 12 3 33), empty, (10 10 1 33), (12 12 1 33), empty, (60 60 7 33), (60 90.1 7 33), (90 90 7 33), empty, (70 70 7 33), (70 80 7 33), (80 80 7 33), empty)"; - multipoint = (MultiPoint) (importerWKT.execute(0, - Geometry.Type.Unknown, wktString, null)); + multipoint = (MultiPoint) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(multipoint != null); multipoint.queryEnvelope2D(envelope); // assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && @@ -1301,8 +1145,7 @@ public static void testImportExportWktMultiPoint() { assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); wktString = "Multipoint zm (10 88 88 33, 10 20 5 33, 20 20 5 33, 20 10 5 33, 12 12 3 33, 10 10 1 33, 12 12 1 33, 60 60 7 33, 60 90.1 7 33, 90 90 7 33, 70 70 7 33, 70 80 7 33, 80 80 7 33)"; - multipoint = (MultiPoint) (importerWKT.execute(0, - Geometry.Type.Unknown, wktString, null)); + multipoint = (MultiPoint) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(multipoint != null); // assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && // envelope.ymin == 10 && ::fabs(envelope.ymax - 90.1) <= 0.001); @@ -1310,20 +1153,16 @@ public static void testImportExportWktMultiPoint() { assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); - wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision15, - multipoint, null); - assertTrue(wktString - .equals("MULTIPOINT ZM ((10 88 88 33), (10 20 5 33), (20 20 5 33), (20 10 5 33), (12 12 3 33), (10 10 1 33), (12 12 1 33), (60 60 7 33), (60 90.1 7 33), (90 90 7 33), (70 70 7 33), (70 80 7 33), (80 80 7 33))")); + wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision15, multipoint, null); + assertTrue(wktString.equals("MULTIPOINT ZM ((10 88 88 33), (10 20 5 33), (20 20 5 33), (20 10 5 33), (12 12 3 33), (10 10 1 33), (12 12 1 33), (60 60 7 33), (60 90.1 7 33), (90 90 7 33), (70 70 7 33), (70 80 7 33), (80 80 7 33))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } wktString = "Multipoint zm (empty, empty, (10 10 5 33))"; - multipoint = (MultiPoint) (importerWKT.execute(0, - Geometry.Type.Unknown, wktString, null)); + multipoint = (MultiPoint) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); - wktString = exporterWKT.execute(WktExportFlags.wktExportPoint, - multipoint, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportPoint, multipoint, null); assertTrue(wktString.equals("POINT ZM (10 10 5 33)")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { @@ -1332,20 +1171,16 @@ public static void testImportExportWktMultiPoint() { @Test public static void testImportExportWktPoint() { - OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromWkt); - OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ExportToWkt); + OperatorImportFromWkt importerWKT = (OperatorImportFromWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromWkt); + OperatorExportToWkt exporterWKT = (OperatorExportToWkt) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToWkt); Point point; String wktString; WktParser wktParser = new WktParser(); // Test Import from Point - wktString = "Point ZM empty"; - point = (Point) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + point = (Point) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(point != null); assertTrue(point.isEmpty()); assertTrue(point.hasAttribute(VertexDescription.Semantics.Z)); @@ -1357,16 +1192,14 @@ public static void testImportExportWktPoint() { while (wktParser.nextToken() != WktParser.WktToken.not_available) { } - wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPoint, - point, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPoint, point, null); assertTrue(wktString.equals("MULTIPOINT ZM EMPTY")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } wktString = "Point zm (30.1 10.6 5.1 33.1)"; - point = (Point) (importerWKT.execute(0, Geometry.Type.Unknown, - wktString, null)); + point = (Point) (importerWKT.execute(0, Geometry.Type.Unknown, wktString, null)); assertTrue(point != null); assertTrue(point.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(point.hasAttribute(VertexDescription.Semantics.M)); @@ -1380,34 +1213,30 @@ public static void testImportExportWktPoint() { assertTrue(z == 5.1); assertTrue(m == 33.1); - wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision15, - point, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportPrecision15, point, null); assertTrue(wktString.equals("POINT ZM (30.1 10.6 5.1 33.1)")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } - wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPoint - | WktExportFlags.wktExportPrecision15, point, null); + wktString = exporterWKT.execute(WktExportFlags.wktExportMultiPoint | WktExportFlags.wktExportPrecision15, point, null); assertTrue(wktString.equals("MULTIPOINT ZM ((30.1 10.6 5.1 33.1))")); wktParser.resetParser(wktString); while (wktParser.nextToken() != WktParser.WktToken.not_available) { } } + @Deprecated @Test - public static void testImportGeoJsonGeometryCollection() - throws JSONException { - OperatorImportFromGeoJson importer = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); + public static void testImportGeoJsonGeometryCollection() throws JSONException { + OperatorImportFromGeoJson importer = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); String geoJsonString; Envelope2D envelope = new Envelope2D(); WktParser wktParser = new WktParser(); geoJsonString = "{\"type\" : \"GeometryCollection\", \"geometries\" : [{\"type\" : \"Point\", \"coordinates\": [0,0]}, {\"type\" : \"GeometryCollection\" , \"geometries\" : [ {\"type\" : \"Point\", \"coordinates\" : [0, 0]} , {\"type\" : \"Point\", \"coordinates\" : [1, 1]} ,{ \"type\" : \"Point\", \"coordinates\" : [2, 2]}, {\"type\" : \"LineString\", \"coordinates\" : []}]} , {\"type\" : \"Point\", \"coordinates\" : [1, 1]}, {\"type\" : \"Point\" , \"coordinates\" : [2, 2]} ] }"; - OGCStructure structure = importer.executeOGC(0, geoJsonString, null).m_ogcStructure.m_structures - .get(0); + OGCStructure structure = importer.executeOGC(0, geoJsonString, null).m_ogcStructure.m_structures.get(0); assertTrue(structure.m_type == 7); assertTrue(structure.m_structures.get(0).m_type == 1); @@ -1423,288 +1252,485 @@ public static void testImportGeoJsonGeometryCollection() } @Test - public static void testImportGeoJsonMultiPolygon() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); + public static void testImportGeoJsonMultiPolygon() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); + OperatorExportToGeoJson exporterGeoJson = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + MapGeometry map_geometry; Polygon polygon; + SpatialReference spatial_reference; String geoJsonString; Envelope2D envelope = new Envelope2D(); // Test Import from MultiPolygon - - geoJsonString = "{\"type\": \"Multipolygon\", \"coordinates\": []}"; - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, - geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\": \"MultiPolygon\", \"coordinates\": []}"; + polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null).getGeometry()); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); assertTrue(!polygon.hasAttribute(VertexDescription.Semantics.M)); - polygon = (Polygon) (GeometryEngine.geometryFromGeoJson(geoJsonString, - 0, Geometry.Type.Unknown).getGeometry()); + geoJsonString = "{\"coordinates\" : [], \"type\": \"MultiPolygon\", \"crs\": {\"type\": \"name\", \"some\": \"stuff\", \"properties\": {\"some\" : \"stuff\", \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); - assertTrue(!polygon.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(spatial_reference.getLatestID() == 4326); + + geoJsonString = "{\"coordinates\" : null, \"crs\": null, \"type\": \"MultiPolygon\"}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + assertTrue(polygon != null); + assertTrue(polygon.isEmpty()); + assertTrue(spatial_reference == null); - geoJsonString = "{\"type\": \"Multipolygon\", \"coordinates\": [[], [[], [[10, 10, 5], [20, 10, 5], [20, 20, 5], [10, 20, 5], [10, 10, 5]], [[12, 12, 3]], [], [[10, 10, 1], [12, 12, 1]]], [], [[[90, 90, 88], [60, 90, 7], [60, 60, 7]], [], [[70, 70, 7], [80, 80, 7], [70, 80, 7], [70, 70, 7]]], []]}"; - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, - geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\": \"MultiPolygon\", \"coordinates\" : [[], [], [[[]]]], \"crsURN\": \"urn:ogc:def:crs:OGC:1.3:CRS27\"}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + assertTrue(polygon != null); + assertTrue(polygon.isEmpty()); + assertTrue(spatial_reference != null); + assertTrue(spatial_reference.getLatestID() == 4267); + + geoJsonString = "{\"coordinates\" : [[], [[], [[10, 10, 5], [20, 10, 5], [20, 20, 5], [10, 20, 5], [10, 10, 5]], [[12, 12, 3]], [], [[10, 10, 1], [12, 12, 1]]], [], [[[90, 90, 88], [60, 90, 7], [60, 60, 7]], [], [[70, 70, 7], [80, 80, 7], [70, 80, 7], [70, 70, 7]]], []], \"crs\": {\"type\": \"link\", \"properties\": {\"href\": \"http://spatialreference.org/ref/sr-org/6928/ogcwkt/\"}}, \"type\": \"MultiPolygon\"}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(polygon != null); polygon.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 90 - && envelope.ymin == 10 && envelope.ymax == 90); + assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && envelope.ymin == 10 && envelope.ymax == 90); assertTrue(polygon.getPointCount() == 14); assertTrue(polygon.getPathCount() == 5); - // assertTrue(polygon.calculate_area_2D() > 0.0); - // assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(spatial_reference.getLatestID() == 3857); - // double z = polygon.getAttributeAsDbl(VertexDescription.Semantics.Z, - // 0, 0); - // assertTrue(z == 5); - - // Test import Polygon - geoJsonString = "{\"type\": \"POLYGON\", \"coordinates\": [[], [], [[10, 10, 5], [10, 20, 5], [20, 20, 5], [20, 10, 5]], [[12, 12, 3]], [], [[10, 10, 1], [12, 12, 1]], [], [[60, 60, 7], [60, 90, 7], [90, 90, 7], [60, 60, 7]], [], [[70, 70, 7], [70, 80, 7], [80, 80, 7]], []] }"; - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, - geoJsonString, null).getGeometry()); + JSONObject jsonObject = new JSONObject(geoJsonString); + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, jsonObject, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(polygon != null); + polygon.queryEnvelope2D(envelope); + assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && envelope.ymin == 10 && envelope.ymax == 90); assertTrue(polygon.getPointCount() == 14); assertTrue(polygon.getPathCount() == 5); - // assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); - - geoJsonString = "{\"type\": \"MULTIPOLYGON\", \"coordinates\": [[[[90, 10, 7], [10, 10, 1], [10, 90, 7], [90, 90, 1], [90, 10, 7]]]] }"; // ring - // is - // oriented - // clockwise - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, - geoJsonString, null).getGeometry()); + assertTrue(spatial_reference.getLatestID() == 3857); + + // Test Export to GeoJSON MultiPolygon + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportSkipCRS, spatial_reference, polygon); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPolygon\",\"coordinates\":[[[[10,10,5],[20,10,5],[20,20,5],[10,20,5],[10,10,5]],[[12,12,3],[12,12,3],[12,12,3]],[[10,10,1],[12,12,1],[10,10,1]]],[[[90,90,88],[60,90,7],[60,60,7],[90,90,88]],[[70,70,7],[70,80,7],[80,80,7],[70,70,7]]]]}")); + + geoJsonString = exporterGeoJson.execute(0, spatial_reference, polygon); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPolygon\",\"coordinates\":[[[[10,10,5],[20,10,5],[20,20,5],[10,20,5],[10,10,5]],[[12,12,3],[12,12,3],[12,12,3]],[[10,10,1],[12,12,1],[10,10,1]]],[[[90,90,88],[60,90,7],[60,60,7],[90,90,88]],[[70,70,7],[70,80,7],[80,80,7],[70,70,7]]]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:3857\"}}}")); + + geoJsonString = "{\"type\": \"MultiPolygon\", \"coordinates\": [[[[90, 10, 7], [10, 10, 1], [10, 90, 7], [90, 90, 1], [90, 10, 7]]]] }"; // ring + // i // clockwise + polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null).getGeometry()); assertTrue(polygon != null); assertTrue(polygon.getPointCount() == 4); assertTrue(polygon.getPathCount() == 1); - // assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(polygon.calculateArea2D() > 0); + + // Test import Polygon + geoJsonString = "{\"type\": \"Polygon\", \"coordinates\": [[], [], [[10, 10, 5], [10, 20, 5], [20, 20, 5], [20, 10, 5]], [[12, 12, 3]], [], [[10, 10, 1], [12, 12, 1]], [], [[60, 60, 7], [60, 90, 7], [90, 90, 7], [60, 60, 7]], [], [[70, 70, 7], [70, 80, 7], [80, 80, 7]], []] }"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + assertTrue(polygon != null); + assertTrue(polygon.getPointCount() == 14); + assertTrue(polygon.getPathCount() == 5); + assertTrue(spatial_reference.getLatestID() == 4326); + + geoJsonString = exporterGeoJson.execute(0, spatial_reference, polygon); + assertTrue(geoJsonString.equals("{\"type\":\"Polygon\",\"coordinates\":[[[10,10,5],[20,10,5],[20,20,5],[10,20,5],[10,10,5]],[[12,12,3],[12,12,3],[12,12,3]],[[10,10,1],[12,12,1],[10,10,1]],[[60,60,7],[60,90,7],[90,90,7],[60,60,7]],[[70,70,7],[70,80,7],[80,80,7],[70,70,7]]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4326\"}}}")); + + Envelope env = new Envelope(); + env.addAttribute(VertexDescription.Semantics.Z); + polygon.queryEnvelope(env); + + geoJsonString = "{\"coordinates\" : [], \"type\": \"MultiPolygon\", \"crs\":{\"esriwkt\":\"PROJCS[\\\"Gnomonic\\\",GEOGCS[\\\"GCS_WGS_1984\\\",DATUM[\\\"D_WGS_1984\\\",SPHEROID[\\\"WGS_1984\\\",6378137.0,298.257223563]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],PROJECTION[\\\"Gnomonic\\\"],PARAMETER[\\\"Longitude_Of_Center\\\",0.0],PARAMETER[\\\"Latitude_Of_Center\\\",-45.0],UNIT[\\\"Meter\\\",1.0]]\"}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + String wkt = spatial_reference.getText(); + assertTrue(wkt.equals( + "PROJCS[\"Gnomonic\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Gnomonic\"],PARAMETER[\"Longitude_Of_Center\",0.0],PARAMETER[\"Latitude_Of_Center\",-45.0],UNIT[\"Meter\",1.0]]")); + + geoJsonString = "{\"coordinates\" : [], \"type\": \"MultiPolygon\", \"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"PROJCS[\\\"Gnomonic\\\",GEOGCS[\\\"GCS_WGS_1984\\\",DATUM[\\\"D_WGS_1984\\\",SPHEROID[\\\"WGS_1984\\\",6378137.0,298.257223563]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],PROJECTION[\\\"Gnomonic\\\"],PARAMETER[\\\"Longitude_Of_Center\\\",0.0],PARAMETER[\\\"Latitude_Of_Center\\\",-45.0],UNIT[\\\"Meter\\\",1.0]]\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + wkt = spatial_reference.getText(); + assertTrue(wkt.equals( + "PROJCS[\"Gnomonic\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Gnomonic\"],PARAMETER[\"Longitude_Of_Center\",0.0],PARAMETER[\"Latitude_Of_Center\",-45.0],UNIT[\"Meter\",1.0]]")); + assertTrue(polygon != null); + assertTrue(polygon.isEmpty()); + + // AGOL exports wkt like this... + geoJsonString = "{\"coordinates\" : [], \"type\": \"MultiPolygon\", \"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"ESRI:PROJCS[\\\"Gnomonic\\\",GEOGCS[\\\"GCS_WGS_1984\\\",DATUM[\\\"D_WGS_1984\\\",SPHEROID[\\\"WGS_1984\\\",6378137.0,298.257223563]],PRIMEM[\\\"Greenwich\\\",0.0],UNIT[\\\"Degree\\\",0.0174532925199433]],PROJECTION[\\\"Gnomonic\\\"],PARAMETER[\\\"Longitude_Of_Center\\\",0.0],PARAMETER[\\\"Latitude_Of_Center\\\",-45.0],UNIT[\\\"Meter\\\",1.0]]\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Polygon, geoJsonString, null); + polygon = (Polygon) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + wkt = spatial_reference.getText(); + assertTrue(wkt.equals( + "PROJCS[\"Gnomonic\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Gnomonic\"],PARAMETER[\"Longitude_Of_Center\",0.0],PARAMETER[\"Latitude_Of_Center\",-45.0],UNIT[\"Meter\",1.0]]")); + assertTrue(polygon != null); + assertTrue(polygon.isEmpty()); + + boolean exceptionThrownNoWKT = false; + + try { + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPreferMultiGeometry, + spatial_reference, polygon); + } catch (Exception e) { + exceptionThrownNoWKT = true; + } + + assertTrue(exceptionThrownNoWKT); } @Test - public static void testImportGeoJsonMultiLineString() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); - + public static void testImportGeoJsonMultiLineString() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); + OperatorExportToGeoJson exporterGeoJson = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + MapGeometry map_geometry; Polyline polyline; + SpatialReference spatial_reference; String geoJsonString; Envelope2D envelope = new Envelope2D(); // Test Import from MultiLineString - - geoJsonString = "{\"type\": \"MultiLineString\", \"coordinates\": []}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\":\"MultiLineString\",\"coordinates\":[], \"crs\" : {\"type\" : \"URL\", \"properties\" : {\"url\" : \"http://www.opengis.net/def/crs/EPSG/0/3857\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + polyline = (Polyline) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(polyline != null); + assertTrue(spatial_reference != null); assertTrue(polyline.isEmpty()); - assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.Z)); - assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(spatial_reference.getLatestID() == 3857); - geoJsonString = "{\"type\": \"MultiLineString\", \"coordinates\": [[], [], [[10, 10, 5], [10, 20, 5], [20, 88, 5], [20, 10, 5]], [[12, 88, 3]], [], [[10, 10, 1], [12, 12, 1]], [], [[88, 60, 7], [60, 90, 7], [90, 90, 7]], [], [[70, 70, 7], [70, 80, 7], [80, 80, 7]], []]}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"crs\" : {\"type\" : \"link\", \"properties\" : {\"href\" : \"www.spatialreference.org/ref/epsg/4309/\"}}, \"type\":\"MultiLineString\",\"coordinates\":[[], [], [[10, 10, 5], [10, 20, 5], [20, 88, 5], [20, 10, 5]], [[12, 88, 3]], [], [[10, 10, 1], [12, 12, 1]], [], [[88, 60, 7], [60, 90, 7], [90, 90, 7]], [], [[70, 70, 7], [70, 80, 7], [80, 80, 7]], []]}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + polyline = (Polyline) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(polyline != null); polyline.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 90 - && envelope.ymin == 10 && envelope.ymax == 90); + assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && envelope.ymin == 10 && envelope.ymax == 90); assertTrue(polyline.getPointCount() == 14); assertTrue(polyline.getPathCount() == 5); - // assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(spatial_reference.getLatestID() == 4309); + + geoJsonString = exporterGeoJson.execute(0, spatial_reference, polyline); + assertTrue(geoJsonString.equals("{\"type\":\"MultiLineString\",\"coordinates\":[[[10,10,5],[10,20,5],[20,88,5],[20,10,5]],[[12,88,3],[12,88,3]],[[10,10,1],[12,12,1]],[[88,60,7],[60,90,7],[90,90,7]],[[70,70,7],[70,80,7],[80,80,7]]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4309\"}}}")); // Test Import LineString - geoJsonString = "{\"type\": \"Linestring\", \"coordinates\": [[10, 10, 5], [10, 20, 5], [20, 20, 5], [20, 10, 5]]}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\": \"LineString\", \"coordinates\": [[10, 10, 5], [10, 20, 5], [20, 20, 5], [20, 10, 5]]}"; + polyline = (Polyline) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polyline.getPointCount() == 4); - // assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); - geoJsonString = "{\"type\": \"Linestring\", \"coordinates\": [[10, 10, 5], [10, 20, 5, 3], [20, 20, 5], [20, 10, 5]]}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\": \"LineString\", \"coordinates\": [[10, 10, 5], [10, 20, 5, 3], [20, 20, 5], [20, 10, 5]]}"; + polyline = (Polyline) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polyline.getPointCount() == 4); - // assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); - // assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(polyline.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(polyline.hasAttribute(VertexDescription.Semantics.M)); + + geoJsonString = "{\"type\":\"LineString\",\"coordinates\": [[10, 10, 5], [10, 20, 5], [20, 20, 5], [], [20, 10, 5]],\"crs\" : {\"type\" : \"link\", \"properties\" : {\"href\" : \"www.opengis.net/def/crs/EPSG/0/3857\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + polyline = (Polyline) (map_geometry.getGeometry()); + spatial_reference = map_geometry.getSpatialReference(); + assertTrue(polyline.getPointCount() == 4); + assertTrue(spatial_reference.getLatestID() == 3857); + geoJsonString = exporterGeoJson.execute(0, spatial_reference, polyline); + assertTrue(geoJsonString.equals("{\"type\":\"LineString\",\"coordinates\":[[10,10,5],[10,20,5],[20,20,5],[20,10,5]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:3857\"}}}")); + + geoJsonString = exporterGeoJson.execute(0, null, polyline); + assertTrue(geoJsonString.equals("{\"type\":\"LineString\",\"coordinates\":[[10,10,5],[10,20,5],[20,20,5],[20,10,5]],\"crs\":null}")); } @Test - public static void testImportGeoJsonMultiPoint() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); - + public static void testImportGeoJsonMultiPoint() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); + OperatorExportToGeoJson exporterGeoJson = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + MapGeometry map_geometry; MultiPoint multipoint; + SpatialReference spatial_reference; String geoJsonString; Envelope2D envelope = new Envelope2D(); // Test Import from Multi_point - geoJsonString = "{\"type\": \"MultiPoint\", \"coordinates\": []}"; - multipoint = (MultiPoint) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\":[]}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + multipoint = (MultiPoint) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(multipoint != null); assertTrue(multipoint.isEmpty()); - assertTrue(!multipoint.hasAttribute(VertexDescription.Semantics.Z)); - assertTrue(!multipoint.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(spatial_reference.getLatestID() == 4326); + + geoJsonString = exporterGeoJson.execute(0, null, multipoint); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[],\"crs\":null}")); multipoint = new MultiPoint(); - multipoint.add(118.15114354234563, 33.82234433423462345); + multipoint.add(118.15, 2); multipoint.add(88, 88); - multipoint = new MultiPoint(); + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPrecision16, SpatialReference.create(4269), multipoint); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[[118.15,2],[88,88]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4269\"}}}")); + + multipoint.setEmpty(); multipoint.add(88, 2); multipoint.add(88, 88); - geoJsonString = "{\"type\": \"Multipoint\", \"coordinates\": [[], [], [10, 88, 88, 33], [10, 20, 5, 33], [20, 20, 5, 33], [20, 10, 5, 33], [12, 12, 3, 33], [], [10, 10, 1, 33], [12, 12, 1, 33], [], [60, 60, 7, 33], [60, 90.1, 7, 33], [90, 90, 7, 33], [], [70, 70, 7, 33], [70, 80, 7, 33], [80, 80, 7, 33], []]}"; - multipoint = (MultiPoint) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = exporterGeoJson.execute(0, SpatialReference.create(102100), multipoint); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[[88,2],[88,88]],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:3857\"}}}")); + + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\":[[], [], [10, 88, 88, 33], [10, 20, 5, 33], [20, 20, 5, 33], [20, 10, 5, 33], [12, 12, 3, 33], [], [10, 10, 1, 33], [12, 12, 1, 33], [], [60, 60, 7, 33], [60, 90.1, 7, 33], [90, 90, 7, 33], [], [70, 70, 7, 33], [70, 80, 7, 33], [80, 80, 7, 33], []],\"crs\":{\"type\":\"OGC\",\"properties\":{\"urn\":\"urn:ogc:def:crs:OGC:1.3:CRS83\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + multipoint = (MultiPoint) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(multipoint != null); - multipoint.queryEnvelope2D(envelope); - // assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && - // envelope.ymin == 10 && Math.abs(envelope.ymax - 90.1) <= 0.001); assertTrue(multipoint.getPointCount() == 13); - // assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); - // assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(spatial_reference.getLatestID() == 4269); - geoJsonString = "{\"type\": \"Multipoint\", \"coordinates\": [[10, 88, 88, 33], [10, 20, 5, 33], [20, 20, 5, 33], [20, 10, 5, 33], [12, 12, 3, 33], [10, 10, 1, 33], [12, 12, 1, 33], [60, 60, 7, 33], [60, 90.1, 7, 33], [90, 90, 7, 33], [70, 70, 7, 33], [70, 80, 7, 33], [80, 80, 7, 33]]}"; - multipoint = (MultiPoint) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\": [[10, 88, 88, 33], [10, 20, 5, 33], [20, 20, 5, 33], [], [20, 10, 5, 33], [12, 12, 3, 33], [], [10, 10, 1, 33], [12, 12, 1, 33], [60, 60, 7, 33], [60, 90.1, 7, 33], [90, 90, 7, 33], [70, 70, 7, 33], [70, 80, 7, 33], [80, 80, 7, 33]]}"; + multipoint = (MultiPoint) importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry(); assertTrue(multipoint != null); - // assertTrue(envelope.xmin == 10 && envelope.xmax == 90 && - // envelope.ymin == 10 && ::fabs(envelope.ymax - 90.1) <= 0.001); assertTrue(multipoint.getPointCount() == 13); - // assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); - // assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(multipoint.hasAttribute(VertexDescription.Semantics.M)); - geoJsonString = "{\"type\": \"Multipoint\", \"coordinates\": [[], [], [10, 10, 5, 33]]}"; - multipoint = (MultiPoint) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPrecision15, null, multipoint); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[[10,88,88,33],[10,20,5,33],[20,20,5,33],[20,10,5,33],[12,12,3,33],[10,10,1,33],[12,12,1,33],[60,60,7,33],[60,90.1,7,33],[90,90,7,33],[70,70,7,33],[70,80,7,33],[80,80,7,33]],\"crs\":null}")); + + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\":[[], [], [10, 10, 5, 33]]}"; + multipoint = (MultiPoint) importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry(); + + geoJsonString = exporterGeoJson.execute(0, null, multipoint); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[[10,10,5,33]],\"crs\":null}")); } @Test - public static void testImportGeoJsonPolygon() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); + public static void testImportGeoJsonPolygon() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); Polygon polygon; String geoJsonString; Envelope2D envelope = new Envelope2D(); // Test Import from Polygon - geoJsonString = "{\"type\": \"Polygon\", \"coordinates\": []}"; - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Unknown, - geoJsonString, null).getGeometry()); + polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polygon != null); assertTrue(polygon.isEmpty()); assertTrue(!polygon.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(!polygon.hasAttribute(VertexDescription.Semantics.M)); geoJsonString = "{\"type\": \"Polygon\", \"coordinates\": [[], [[10, 10, 5], [20, 10, 5], [20, 20, 5], [10, 20, 5], [10, 10, 5]], [[12, 12, 3]], [], [[10, 10, 1], [12, 12, 1]]]}"; - polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Unknown, - geoJsonString, null).getGeometry()); + polygon = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polygon != null); polygon.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 20 - && envelope.ymin == 10 && envelope.ymax == 20); + assertTrue(envelope.xmin == 10 && envelope.xmax == 20 && envelope.ymin == 10 && envelope.ymax == 20); assertTrue(polygon.getPointCount() == 8); assertTrue(polygon.getPathCount() == 3); - // assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(polygon.hasAttribute(VertexDescription.Semantics.Z)); - geoJsonString = "{\"type\": \"polygon\", \"coordinates\": [[[35, 10], [10, 20], [15, 40], [45, 45], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}"; - Polygon polygon2 = (Polygon) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + geoJsonString = "{\"type\": \"Polygon\", \"coordinates\": [[[35, 10], [10, 20], [15, 40], [45, 45], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}"; + Polygon polygon2 = (Polygon) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polygon2 != null); } @Test - public static void testImportGeoJsonLineString() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); + public static void testImportGeoJsonLineString() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); Polyline polyline; String geoJsonString; Envelope2D envelope = new Envelope2D(); // Test Import from LineString - geoJsonString = "{\"type\": \"LineString\", \"coordinates\": []}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + polyline = (Polyline) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polyline != null); assertTrue(polyline.isEmpty()); assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.Z)); assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.M)); geoJsonString = "{\"type\": \"LineString\", \"coordinates\": [[10, 10, 5], [10, 20, 5], [20, 20, 5], [20, 10, 5]]}"; - polyline = (Polyline) (importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString, null).getGeometry()); + polyline = (Polyline) (importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null).getGeometry()); assertTrue(polyline != null); polyline.queryEnvelope2D(envelope); - assertTrue(envelope.xmin == 10 && envelope.xmax == 20 - && envelope.ymin == 10 && envelope.ymax == 20); + assertTrue(envelope.xmin == 10 && envelope.xmax == 20 && envelope.ymin == 10 && envelope.ymax == 20); assertTrue(polyline.getPointCount() == 4); assertTrue(polyline.getPathCount() == 1); - // assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(!polyline.hasAttribute(VertexDescription.Semantics.M)); } @Test - public static void testImportGeoJsonPoint() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); - + public static void testImportGeoJsonPoint() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); + OperatorExportToGeoJson exporterGeoJson = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + MapGeometry map_geometry; + SpatialReference spatial_reference; Point point; String geoJsonString; // Test Import from Point + geoJsonString = "{\"type\":\"Point\",\"coordinates\":[],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:3857\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + point = (Point) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); + assertTrue(spatial_reference.getLatestID() == 3857); - geoJsonString = "{\"type\": \"Point\", \"coordinates\": []}"; - point = (Point) (importerGeoJson.execute(0, Geometry.Type.Unknown, - geoJsonString, null).getGeometry()); assertTrue(point != null); assertTrue(point.isEmpty()); - assertTrue(!point.hasAttribute(VertexDescription.Semantics.Z)); - assertTrue(!point.hasAttribute(VertexDescription.Semantics.M)); - geoJsonString = "{\"type\": \"Point\", \"coordinates\": [30.1, 10.6, 5.1, 33.1]}"; - point = (Point) (importerGeoJson.execute(0, Geometry.Type.Unknown, - geoJsonString, null).getGeometry()); + geoJsonString = exporterGeoJson.execute(0, null, point); + assertTrue(geoJsonString.equals("{\"type\":\"Point\",\"coordinates\":[],\"crs\":null}")); + + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPreferMultiGeometry, null, point); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[],\"crs\":null}")); + + geoJsonString = "{\"type\":\"Point\",\"coordinates\":[30.1,10.6,5.1,33.1],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:ESRI::54051\"}}}"; + map_geometry = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + point = (Point) map_geometry.getGeometry(); + spatial_reference = map_geometry.getSpatialReference(); assertTrue(point != null); - // assertTrue(point.hasAttribute(VertexDescription.Semantics.Z)); - // assertTrue(point.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(point.hasAttribute(VertexDescription.Semantics.Z)); + assertTrue(point.hasAttribute(VertexDescription.Semantics.M)); + assertTrue(spatial_reference.getLatestID() == 54051); double x = point.getX(); double y = point.getY(); - // double z = point.getZ(); - // double m = point.getM(); + double z = point.getZ(); + double m = point.getM(); assertTrue(x == 30.1); assertTrue(y == 10.6); - // assertTrue(z == 5.1); - // assertTrue(m == 33.1); + assertTrue(z == 5.1); + assertTrue(m == 33.1); + + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPrecision15, spatial_reference, point); + assertTrue(geoJsonString.equals("{\"type\":\"Point\",\"coordinates\":[30.1,10.6,5.1,33.1],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"ESRI:54051\"}}}")); + + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPrecision15, SpatialReference.create(4287), point); + assertTrue(geoJsonString.equals("{\"type\":\"Point\",\"coordinates\":[30.1,10.6,5.1,33.1],\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"EPSG:4287\"}}}")); + + geoJsonString = exporterGeoJson.execute(GeoJsonExportFlags.geoJsonExportPreferMultiGeometry | GeoJsonExportFlags.geoJsonExportPrecision15, null, point); + assertTrue(geoJsonString.equals("{\"type\":\"MultiPoint\",\"coordinates\":[[30.1,10.6,5.1,33.1]],\"crs\":null}")); } @Test - public static void testImportGeoJsonSpatialReference() throws JSONException { - OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal - .getInstance().getOperator(Operator.Type.ImportFromGeoJson); + public static void testImportExportGeoJsonMalformed() { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); + OperatorExportToGeoJson exporterGeoJson = (OperatorExportToGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ExportToGeoJson); + + String geoJsonString; + + try { + geoJsonString = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"Polygon\",\"coordinates\":[[2,2,2],[3,3,3],[4,4,4],[2,2,2]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"Polygon\",\"coordinates\":[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]],2,4]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\":[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"LineString\",\"coordinates\":[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPoint\",\"coordinates\":[[2,2,2],[3,3,3],[4,4,4],[2,2,2],[[]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[2,2,2],[3,3,3],[4,4,4],[2,2,2],[[]]]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]],[1,1,1],[2,2,2],[3,3,3],[1,1,1]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"Polygon\",\"coordinates\":[[[2,2,2],[3,3,3],[4,4,4],[2,2,2]],[1,1,1],[2,2,2],[3,3,3],[1,1,1]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[[]]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"MultiPolygon\",\"coordinates\":[[[[{}]]]}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + + try { + geoJsonString = "{\"type\":\"Point\",\"coordinates\":[30.1,10.6,[],33.1],\"crs\":\"EPSG:3857\"}"; + importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString, null); + assertTrue(false); + } catch (Exception e) { + } + } + + @Test + public static void testImportGeoJsonSpatialReference() throws Exception { + OperatorImportFromGeoJson importerGeoJson = (OperatorImportFromGeoJson) OperatorFactoryLocal.getInstance().getOperator(Operator.Type.ImportFromGeoJson); String geoJsonString4326; String geoJsonString3857; // Test Import from Point - geoJsonString4326 = "{\"type\": \"Point\", \"coordinates\": [3.0, 5.0], \"crs\": \"EPSG:4326\"}"; geoJsonString3857 = "{\"type\": \"Point\", \"coordinates\": [3.0, 5.0], \"crs\": \"EPSG:3857\"}"; - MapGeometry mapGeometry4326 = importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString4326, null); - MapGeometry mapGeometry3857 = importerGeoJson.execute(0, - Geometry.Type.Unknown, geoJsonString3857, null); + MapGeometry mapGeometry4326 = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString4326, null); + MapGeometry mapGeometry3857 = importerGeoJson.execute(0, Geometry.Type.Unknown, geoJsonString3857, null); assertTrue(mapGeometry4326.equals(mapGeometry3857) == false); - assertTrue(mapGeometry4326.getGeometry().equals( - mapGeometry3857.getGeometry())); + assertTrue(mapGeometry4326.getGeometry().equals(mapGeometry3857.getGeometry())); } public static Polygon makePolygon() { diff --git a/src/test/java/com/esri/core/geometry/TestIntersection.java b/src/test/java/com/esri/core/geometry/TestIntersection.java index ab7dd9f0..8e3f7fcc 100644 --- a/src/test/java/com/esri/core/geometry/TestIntersection.java +++ b/src/test/java/com/esri/core/geometry/TestIntersection.java @@ -1,8 +1,11 @@ package com.esri.core.geometry; import junit.framework.TestCase; + import org.junit.Test; +import com.esri.core.geometry.PolygonUtils.PiPResult; + //import java.util.Random; public class TestIntersection extends TestCase { @@ -1038,4 +1041,47 @@ public void testIntersectionIssue2() { boolean eq = OperatorEquals.local().execute(g, polyline, sr, null); assertTrue(eq); } + + /* + Point2D uniqueIntersectionPointOfNonDisjointGeometries(Geometry g1, Geometry g2, SpatialReference sr) { + Geometry g1Test = g1; + boolean g1Polygon = g1.getType() == Geometry.Type.Polygon; + boolean g2Polygon = g2.getType() == Geometry.Type.Polygon; + + if (g1Polygon || g2Polygon) + { + if (g1Polygon) { + Point2D p = getFirstPoint(g2); + if (PolygonUtils.isPointInPolygon2D((Polygon)g1, p, 0) != PiPResult.PiPOutside) + return p; + } + if (g2Polygon) { + Point2D p = getFirstPoint(g1); + if (PolygonUtils.isPointInPolygon2D((Polygon)g2, p, 0) != PiPResult.PiPOutside) + return p; + } + } + + if (g1Polygon) + { + Polyline polyline = new Polyline(); + polyline.add((MultiPath)g1, false); + g1Test = polyline; + } + Geometry g2Test = g2; + if (g2Polygon) + { + Polyline polyline = new Polyline(); + polyline.add((MultiPath)g2, false); + g2Test = polyline; + } + + GeometryCursor gc = OperatorIntersection.local().execute(new SimpleGeometryCursor(g1Test), new SimpleGeometryCursor(g2Test), sr, null, 3); + for (Geometry res = gc.next(); res != null; res = gc.next()) { + return getFirstPoint(res); + } + + throw new GeometryException("internal error"); + }*/ + } diff --git a/src/test/java/com/esri/core/geometry/TestOGC.java b/src/test/java/com/esri/core/geometry/TestOGC.java index dcca0c3a..5b2ef6b3 100644 --- a/src/test/java/com/esri/core/geometry/TestOGC.java +++ b/src/test/java/com/esri/core/geometry/TestOGC.java @@ -14,7 +14,6 @@ import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; import org.codehaus.jackson.JsonParseException; -import org.json.JSONException; import org.junit.Test; import java.io.IOException; @@ -40,6 +39,8 @@ public void testPoint() { assertTrue(p.Y() == 2); assertTrue(g.equals(OGCGeometry.fromText("POINT(1 2)"))); assertTrue(!g.equals(OGCGeometry.fromText("POINT(1 3)"))); + assertTrue(g.equals((Object)OGCGeometry.fromText("POINT(1 2)"))); + assertTrue(!g.equals((Object)OGCGeometry.fromText("POINT(1 3)"))); OGCGeometry buf = g.buffer(10); assertTrue(buf.geometryType().equals("Polygon")); OGCPolygon poly = (OGCPolygon) buf.envelope(); @@ -48,7 +49,7 @@ public void testPoint() { } @Test - public void testPolygon() { + public void testPolygon() throws Exception { OGCGeometry g = OGCGeometry .fromText("POLYGON((-10 -10, 10 -10, 10 10, -10 10, -10 -10), (-5 -5, -5 5, 5 5, 5 -5, -5 -5))"); assertTrue(g.geometryType().equals("Polygon")); @@ -64,14 +65,26 @@ public void testPolygon() { b = lsi.equals(OGCGeometry .fromText("LINESTRING(-5 -5, -5 5, 5 5, 5 -5, -5 -5)")); assertTrue(b); + b = lsi.equals((Object)OGCGeometry + .fromText("LINESTRING(-5 -5, -5 5, 5 5, 5 -5, -5 -5)")); assertTrue(!lsi.equals(ls)); OGCMultiCurve boundary = p.boundary(); String s = boundary.asText(); assertTrue(s.equals("MULTILINESTRING ((-10 -10, 10 -10, 10 10, -10 10, -10 -10), (-5 -5, -5 5, 5 5, 5 -5, -5 -5))")); + + { + OGCGeometry g2 = OGCGeometry.fromGeoJson("{\"type\": \"Polygon\", \"coordinates\": [[[1.00000001,1.00000001], [4.00000001,1.00000001], [4.00000001,4.00000001], [1.00000001,4.00000001]]]}"); + OGCGeometry.fromGeoJson("{\"type\": \"LineString\", \"coordinates\": [[1.00000001,1.00000001], [7.00000001,8.00000001]]}").intersects(g2); + OGCGeometry.fromGeoJson("{\"type\": \"LineString\", \"coordinates\": [[2.449,4.865], [7.00000001,8.00000001]]}").intersects(g2); + + OGCGeometry g3 = OGCGeometry.fromGeoJson("{\"type\": \"Polygon\", \"coordinates\": [[[1.00000001,1.00000001], [4.00000001,1.00000001], [4.00000001,4.00000001], [1.00000001,4.00000001]]]}"); + boolean bb = g2.equals((Object)g3); + assertTrue(bb); + } } @Test - public void testGeometryCollection() throws JSONException { + public void testGeometryCollection() throws Exception { OGCGeometry g = OGCGeometry .fromText("GEOMETRYCOLLECTION(POLYGON EMPTY, POINT(1 1), LINESTRING EMPTY, MULTIPOLYGON EMPTY, MULTILINESTRING EMPTY)"); assertTrue(g.geometryType().equals("GeometryCollection")); @@ -139,6 +152,10 @@ public void testGeometryCollection() throws JSONException { wktString = g.asText(); assertTrue(wktString .equals("GEOMETRYCOLLECTION (POLYGON EMPTY, POINT (1 1), GEOMETRYCOLLECTION EMPTY, LINESTRING EMPTY, GEOMETRYCOLLECTION (POLYGON EMPTY, POINT (1 1), LINESTRING EMPTY, MULTIPOLYGON EMPTY, MULTILINESTRING EMPTY, MULTIPOINT EMPTY), MULTIPOLYGON EMPTY, MULTILINESTRING EMPTY)")); + + assertTrue(g.equals((Object)OGCGeometry.fromText(wktString))); + + assertTrue(g.hashCode() == OGCGeometry.fromText(wktString).hashCode()); } @@ -768,7 +785,7 @@ public void testIsectTria1() { } @Test - public void testIsectTriaJson1() throws JsonParseException, IOException { + public void testIsectTriaJson1() throws Exception { String json1 = "{\"rings\":[[[1, 0], [3, 0], [1, 2], [1, 0]]], \"spatialReference\":{\"wkid\":4326}}"; String json2 = "{\"rings\":[[[0, 1], [2, 1], [0, 3], [0, 1]]], \"spatialReference\":{\"wkid\":4326}}"; OGCGeometry g0 = OGCGeometry.fromJson(json1); @@ -868,7 +885,7 @@ public void testMultiPolygonArea() { } @Test - public void testPolylineSimplifyIssueGithub52() throws JsonParseException, IOException { + public void testPolylineSimplifyIssueGithub52() throws Exception { String json = "{\"paths\":[[[2,0],[4,3],[5,1],[3.25,1.875],[1,3]]],\"spatialReference\":{\"wkid\":4326}}"; { OGCGeometry g = OGCGeometry.fromJson(json); diff --git a/src/test/java/com/esri/core/geometry/TestPolygon.java b/src/test/java/com/esri/core/geometry/TestPolygon.java index 8901df4e..e34a14b7 100644 --- a/src/test/java/com/esri/core/geometry/TestPolygon.java +++ b/src/test/java/com/esri/core/geometry/TestPolygon.java @@ -1,7 +1,10 @@ package com.esri.core.geometry; +import java.io.IOException; + import junit.framework.TestCase; +import org.json.JSONException; import org.junit.Test; import com.esri.core.geometry.ogc.OGCGeometry; @@ -1199,5 +1202,104 @@ public void testReplaceNaNs() { assertTrue(b); } - } + } + + @Test + public void testPolygon2PolygonFails() throws IOException, JSONException { + OperatorFactoryLocal factory = OperatorFactoryLocal.getInstance(); + OperatorExportToGeoJson exporter = (OperatorExportToGeoJson) factory + .getOperator(Operator.Type.ExportToGeoJson); + String result = exporter.execute(birmingham()); + + OperatorImportFromGeoJson importer = (OperatorImportFromGeoJson) factory + .getOperator(Operator.Type.ImportFromGeoJson); + MapGeometry mapGeometry = importer.execute( + GeoJsonImportFlags.geoJsonImportDefaults, + Geometry.Type.Polygon, result, null); + Polygon polygon = (Polygon) mapGeometry.getGeometry(); + assertEquals(birmingham(), polygon); + } + + @Test + public void testPolygon2PolygonFails2() throws JSONException { + String birminghamGeojson = GeometryEngine + .geometryToGeoJson(birmingham()); + MapGeometry returnedGeometry = GeometryEngine.geometryFromGeoJson( + birminghamGeojson, GeoJsonImportFlags.geoJsonImportDefaults, + Geometry.Type.Polygon); + Polygon polygon = (Polygon) returnedGeometry.getGeometry(); + assertEquals(polygon, birmingham()); + } + + @Test + public void testPolygon2PolygonWorks() throws JSONException { + String birminghamGeojson = GeometryEngine + .geometryToGeoJson(birmingham()); + MapGeometry returnedGeometry = GeometryEngine.geometryFromGeoJson( + birminghamGeojson, GeoJsonImportFlags.geoJsonImportDefaults, + Geometry.Type.Polygon); + Polygon polygon = (Polygon) returnedGeometry.getGeometry(); + assertEquals(polygon.toString(), birmingham().toString()); + } + + @Test + public void testPolygon2Polygon2Works() throws JSONException, IOException { + String birminghamJson = GeometryEngine.geometryToJson(4326, + birmingham()); + MapGeometry returnedGeometry = GeometryEngine + .jsonToGeometry(birminghamJson); + Polygon polygon = (Polygon) returnedGeometry.getGeometry(); + assertEquals(polygon, birmingham()); + String s = polygon.toString(); + } + + @Test + public void testSegmentIteratorCrash() { + Polygon poly = new Polygon(); + + // clockwise => outer ring + poly.startPath(0, 0); + poly.lineTo(-0.5, 0.5); + poly.lineTo(0.5, 1); + poly.lineTo(1, 0.5); + poly.lineTo(0.5, 0); + + // hole + poly.startPath(0.5, 0.2); + poly.lineTo(0.6, 0.5); + poly.lineTo(0.2, 0.9); + poly.lineTo(-0.2, 0.5); + poly.lineTo(0.1, 0.2); + poly.lineTo(0.2, 0.3); + + // island + poly.startPath(0.1, 0.7); + poly.lineTo(0.3, 0.7); + poly.lineTo(0.3, 0.4); + poly.lineTo(0.1, 0.4); + + assertEquals(poly.getSegmentCount(), 15); + assertEquals(poly.getPathCount(), 3); + SegmentIterator segmentIterator = poly.querySegmentIterator(); + int paths = 0; + int segments = 0; + while (segmentIterator.nextPath()) { + paths++; + Segment segment; + while (segmentIterator.hasNextSegment()) { + segment = segmentIterator.nextSegment(); + segments++; + } + } + assertEquals(paths, 3); + assertEquals(segments, 15); + } + + private static Polygon birmingham() { + Polygon poly = new Polygon(); + poly.addEnvelope(new Envelope(-1.954245, 52.513531, -1.837357, + 52.450123), false); + poly.addEnvelope(new Envelope(0, 0, 1, 1), false); + return poly; + } } diff --git a/src/test/java/com/esri/core/geometry/TestQuadTree.java b/src/test/java/com/esri/core/geometry/TestQuadTree.java index 7ffdc06d..6dde9220 100644 --- a/src/test/java/com/esri/core/geometry/TestQuadTree.java +++ b/src/test/java/com/esri/core/geometry/TestQuadTree.java @@ -1,6 +1,12 @@ package com.esri.core.geometry; import java.util.ArrayList; +import java.util.Random; +import java.util.HashMap; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.Map; import junit.framework.TestCase; @@ -19,11 +25,24 @@ protected void tearDown() throws Exception { @Test public static void test1() { + + { + QuadTree quad_tree = new QuadTree(Envelope2D.construct(-10, -10, 10, 10), 8); + + QuadTree.QuadTreeIterator qt = quad_tree.getIterator(true); + assertTrue(qt.next() == -1); + + qt.resetIterator(Envelope2D.construct(0, 0, 0, 0), 0); + + assertTrue(quad_tree.getIntersectionCount(Envelope2D.construct(0, 0, 0, 0), 0, 10) == 0); + assertTrue(quad_tree.getElementCount() == 0); + } + Polyline polyline; polyline = makePolyline(); MultiPathImpl polylineImpl = (MultiPathImpl) polyline._getImpl(); - QuadTree quadtree = buildQuadTree_(polylineImpl); + QuadTree quadtree = buildQuadTree_(polylineImpl, false); Line queryline = new Line(34, 9, 66, 46); QuadTree.QuadTreeIterator qtIter = quadtree.getIterator(); @@ -37,12 +56,12 @@ public static void test1() { assertTrue(index == 6 || index == 8 || index == 14); element_handle = qtIter.next(); } - + Envelope2D envelope = new Envelope2D(34, 9, 66, 46); Polygon queryPolygon = new Polygon(); queryPolygon.addEnvelope(envelope, true); - qtIter.resetIterator(queryline, 0.0); + qtIter.resetIterator(queryline, 0.0); element_handle = qtIter.next(); while (element_handle > 0) { @@ -52,6 +71,336 @@ public static void test1() { } } + @Test + public static void testQuadTreeWithDuplicates() { + int pass_count = 10; + int figure_size = 400; + int figure_size2 = 100; + Envelope extent1 = new Envelope(); + extent1.setCoords(-100000, -100000, 100000, 100000); + + RandomCoordinateGenerator generator1 = new RandomCoordinateGenerator(Math.max(figure_size, 10000), extent1, 0.001); + Random random = new Random(2013); + int rand_max = 32; + + Polygon poly_red = new Polygon(); + Polygon poly_blue = new Polygon(); + + int r = figure_size; + + for (int c = 0; c < pass_count; c++) { + Point pt; + for (int j = 0; j < r; j++) { + int rand = random.nextInt(rand_max); + boolean b_random_new = r > 10 && ((1.0 * rand) / rand_max > 0.95); + pt = generator1.GetRandomCoord(); + if (j == 0 || b_random_new) + poly_blue.startPath(pt); + else + poly_blue.lineTo(pt); + } + + Envelope2D env = new Envelope2D(); + + QuadTree quad_tree_blue = buildQuadTree_((MultiPathImpl) poly_blue._getImpl(), false); + QuadTree quad_tree_blue_duplicates = buildQuadTree_((MultiPathImpl) poly_blue._getImpl(), true); + + Envelope2D e1 = quad_tree_blue.getDataExtent(); + Envelope2D e2 = quad_tree_blue_duplicates.getDataExtent(); + assertTrue(e1.equals(e2)); + assertTrue(quad_tree_blue.getElementCount() == poly_blue.getSegmentCount()); + + SegmentIterator seg_iter_blue = poly_blue.querySegmentIterator(); + + poly_red.setEmpty(); + + r = figure_size2; + if (r < 3) + continue; + + for (int j = 0; j < r; j++) { + int rand = random.nextInt(rand_max); + boolean b_random_new = r > 10 && ((1.0 * rand) / rand_max > 0.95); + pt = generator1.GetRandomCoord(); + if (j == 0 || b_random_new) + poly_red.startPath(pt); + else + poly_red.lineTo(pt); + } + + QuadTree.QuadTreeIterator iterator = quad_tree_blue.getIterator(); + SegmentIteratorImpl seg_iter_red = ((MultiPathImpl) poly_red._getImpl()).querySegmentIterator(); + + HashMap map1 = new HashMap(0); + + int count = 0; + int intersections_per_query = 0; + while (seg_iter_red.nextPath()) { + while (seg_iter_red.hasNextSegment()) { + Segment segment_red = seg_iter_red.nextSegment(); + segment_red.queryEnvelope2D(env); + + iterator.resetIterator(env, 0.0); + + int count_upper = 0; + int element_handle; + while ((element_handle = iterator.next()) != -1) { + count_upper++; + int index = quad_tree_blue.getElement(element_handle); + Boolean iter = (Boolean) map1.get(new Integer(index)); + if (iter == null) { + count++; + map1.put(new Integer(index), new Boolean(true)); + } + + intersections_per_query++; + } + + int intersection_count = quad_tree_blue.getIntersectionCount(env, 0.0, -1); + assertTrue(intersection_count == count_upper); + } + } + + seg_iter_red.resetToFirstPath(); + + HashMap map2 = new HashMap(0); + QuadTree.QuadTreeIterator iterator_duplicates = quad_tree_blue_duplicates.getIterator(); + + int count_duplicates = 0; + int intersections_per_query_duplicates = 0; + while (seg_iter_red.nextPath()) { + while (seg_iter_red.hasNextSegment()) { + Segment segment_red = seg_iter_red.nextSegment(); + segment_red.queryEnvelope2D(env); + + iterator_duplicates.resetIterator(env, 0.0); + + int count_lower = 0; + HashMap map_per_query = new HashMap(0); + + int count_upper = 0; + int element_handle; + while ((element_handle = iterator_duplicates.next()) != -1) { + count_upper++; + int index = quad_tree_blue_duplicates.getElement(element_handle); + Boolean iter = (Boolean) map2.get(new Integer(index)); + if (iter == null) { + count_duplicates++; + map2.put(new Integer(index), new Boolean(true)); + } + + Boolean iter_lower = (Boolean) map_per_query.get(index); + if (iter_lower == null) { + count_lower++; + intersections_per_query_duplicates++; + map_per_query.put(new Integer(index), new Boolean(true)); + } + + int q = quad_tree_blue_duplicates.getQuad(element_handle); + assertTrue(quad_tree_blue_duplicates.getSubTreeElementCount(q) >= quad_tree_blue_duplicates.getContainedSubTreeElementCount(q)); + } + + int intersection_count = quad_tree_blue_duplicates.getIntersectionCount(env, 0.0, -1); + boolean b_has_data = quad_tree_blue_duplicates.hasData(env, 0.0); + assertTrue(b_has_data || intersection_count == 0); + assertTrue(count_lower <= intersection_count && intersection_count <= count_upper); + assertTrue(count_upper <= 4 * count_lower); + } + } + + assertTrue(count == count_duplicates); + assertTrue(intersections_per_query == intersections_per_query_duplicates); + } + } + + @Test + public static void testSortedIterator() { + int pass_count = 10; + int figure_size = 400; + int figure_size2 = 100; + Envelope extent1 = new Envelope(); + extent1.setCoords(-100000, -100000, 100000, 100000); + + RandomCoordinateGenerator generator1 = new RandomCoordinateGenerator(Math.max(figure_size, 10000), extent1, 0.001); + + Random random = new Random(2013); + int rand_max = 32; + + Polygon poly_red = new Polygon(); + Polygon poly_blue = new Polygon(); + + int r = figure_size; + + for (int c = 0; c < pass_count; c++) { + Point pt; + for (int j = 0; j < r; j++) { + int rand = random.nextInt(rand_max); + boolean b_random_new = r > 10 && ((1.0 * rand) / rand_max > 0.95); + pt = generator1.GetRandomCoord(); + if (j == 0 || b_random_new) + poly_blue.startPath(pt); + else + poly_blue.lineTo(pt); + } + + Envelope2D env = new Envelope2D(); + + QuadTree quad_tree_blue = buildQuadTree_((MultiPathImpl) poly_blue._getImpl(), false); + + Envelope2D e1 = quad_tree_blue.getDataExtent(); + assertTrue(quad_tree_blue.getElementCount() == poly_blue.getSegmentCount()); + + SegmentIterator seg_iter_blue = poly_blue.querySegmentIterator(); + + poly_red.setEmpty(); + + r = figure_size2; + if (r < 3) + continue; + + for (int j = 0; j < r; j++) { + int rand = random.nextInt(rand_max); + boolean b_random_new = r > 10 && ((1.0 * rand) / rand_max > 0.95); + pt = generator1.GetRandomCoord(); + if (j == 0 || b_random_new) + poly_red.startPath(pt); + else + poly_red.lineTo(pt); + } + + QuadTree.QuadTreeIterator iterator = quad_tree_blue.getIterator(); + SegmentIteratorImpl seg_iter_red = ((MultiPathImpl) poly_red._getImpl()).querySegmentIterator(); + + HashMap map1 = new HashMap(0); + + int count = 0; + int intersections_per_query = 0; + while (seg_iter_red.nextPath()) { + while (seg_iter_red.hasNextSegment()) { + Segment segment_red = seg_iter_red.nextSegment(); + segment_red.queryEnvelope2D(env); + + iterator.resetIterator(env, 0.0); + + int count_upper = 0; + int element_handle; + while ((element_handle = iterator.next()) != -1) { + count_upper++; + int index = quad_tree_blue.getElement(element_handle); + Boolean iter = (Boolean) map1.get(index); + if (iter == null) { + count++; + map1.put(new Integer(index), new Boolean(true)); + } + + intersections_per_query++; + } + + int intersection_count = quad_tree_blue.getIntersectionCount(env, 0.0, -1); + assertTrue(intersection_count == count_upper); + } + } + + seg_iter_red.resetToFirstPath(); + + HashMap map2 = new HashMap(0); + QuadTree.QuadTreeIterator sorted_iterator = quad_tree_blue.getIterator(true); + + int count_sorted = 0; + int intersections_per_query_sorted = 0; + while (seg_iter_red.nextPath()) { + while (seg_iter_red.hasNextSegment()) { + Segment segment_red = seg_iter_red.nextSegment(); + segment_red.queryEnvelope2D(env); + + sorted_iterator.resetIterator(env, 0.0); + + int count_upper_sorted = 0; + int element_handle; + int last_index = -1; + while ((element_handle = sorted_iterator.next()) != -1) { + count_upper_sorted++; + int index = quad_tree_blue.getElement(element_handle); + assertTrue(last_index < index); // ensure the element handles are returned in sorted order + last_index = index; + Boolean iter = (Boolean) map2.get(index); + if (iter == null) { + count_sorted++; + map2.put(new Integer(index), new Boolean(true)); + } + + intersections_per_query_sorted++; + } + + int intersection_count = quad_tree_blue.getIntersectionCount(env, 0.0, -1); + assertTrue(intersection_count == count_upper_sorted); + } + } + + assertTrue(count == count_sorted); + assertTrue(intersections_per_query == intersections_per_query_sorted); + } + } + + @Test + public static void test_perf_quad_tree() { + Envelope extent1 = new Envelope(); + extent1.setCoords(-1000, -1000, 1000, 1000); + + RandomCoordinateGenerator generator1 = new RandomCoordinateGenerator(1000, extent1, 0.001); + //HiResTimer timer; + for (int N = 16; N <= 1024/**1024*/; N *= 2) { + //timer.StartMeasurement(); + + Envelope2D extent = new Envelope2D(); + extent.setCoords(-1000, -1000, 1000, 1000); + HashMap data = new HashMap(0); + QuadTree qt = new QuadTree(extent, 10); + for (int i = 0; i < N; i++) { + Envelope2D env = new Envelope2D(); + Point2D center = generator1.GetRandomCoord().getXY(); + double w = 10; + env.setCoords(center, w, w); + env.intersect(extent); + if (env.isEmpty()) + continue; + + int h = qt.insert(i, env); + data.put(new Integer(h), env); + } + + int ecount = 0; + AttributeStreamOfInt32 handles = new AttributeStreamOfInt32(0); + QuadTree.QuadTreeIterator iter = qt.getIterator(); + + Iterator> pairs = data.entrySet().iterator(); + while (pairs.hasNext()) { + Map.Entry entry = pairs.next(); + iter.resetIterator((Envelope2D) entry.getValue(), 0.001); + boolean remove_self = false; + for (int h = iter.next(); h != -1; h = iter.next()) { + if (h != entry.getKey().intValue()) + handles.add(h); + else { + remove_self = true; + } + + ecount++; + } + + for (int i = 0; i < handles.size(); i++) { + qt.removeElement(handles.get(i));//remove elements that were selected. + } + + if (remove_self) + qt.removeElement(entry.getKey().intValue()); + handles.resize(0); + } + + //printf("%d %0.3f (%I64d, %f, mem %I64d)\n", N, timer.GetMilliseconds(), ecount, ecount / double(N * N), memsize); + } + } + @Test public static void test2() { MultiPoint multipoint = new MultiPoint(); @@ -81,7 +430,7 @@ public static void test2() { assertTrue(count == 10000); } - + public static Polyline makePolyline() { Polyline poly = new Polyline(); @@ -120,10 +469,10 @@ public static Polyline makePolyline() { return poly; } - static QuadTree buildQuadTree_(MultiPathImpl multipathImpl) { + static QuadTree buildQuadTree_(MultiPathImpl multipathImpl, boolean bStoreDuplicates) { Envelope2D extent = new Envelope2D(); multipathImpl.queryEnvelope2D(extent); - QuadTree quadTree = new QuadTree(extent, 8); + QuadTree quadTree = new QuadTree(extent, 8, bStoreDuplicates); int hint_index = -1; Envelope2D boundingbox = new Envelope2D(); SegmentIteratorImpl seg_iter = multipathImpl.querySegmentIterator(); diff --git a/src/test/java/com/esri/core/geometry/TestRelation.java b/src/test/java/com/esri/core/geometry/TestRelation.java index ec21049d..5c00d8ad 100644 --- a/src/test/java/com/esri/core/geometry/TestRelation.java +++ b/src/test/java/com/esri/core/geometry/TestRelation.java @@ -1,7 +1,10 @@ package com.esri.core.geometry; +import java.io.IOException; + import junit.framework.TestCase; +import org.codehaus.jackson.JsonParseException; import org.junit.Test; import com.esri.core.geometry.Geometry.GeometryAccelerationDegree; @@ -5481,7 +5484,7 @@ public void testCrosses_github_issue_40() { null); assertTrue(answer2); } - + @Test public void testDisjointCrash() { Polygon g1 = new Polygon(); @@ -5492,5 +5495,14 @@ public void testDisjointCrash() { OperatorDisjoint.local().accelerateGeometry(g1, SpatialReference.create(4267), GeometryAccelerationDegree.enumHot); boolean res = OperatorDisjoint.local().execute(g1, g2, SpatialReference.create(4267), null); assertTrue(!res); - } + } + + @Test + public void testDisjointFail() throws JsonParseException, IOException { + MapGeometry geometry1 = OperatorImportFromJson.local().execute(Geometry.Type.Unknown, "{\"paths\":[[[3,3],[3,3]]],\"spatialReference\":{\"wkid\":4326}}"); + MapGeometry geometry2 = OperatorImportFromJson.local().execute(Geometry.Type.Unknown, "{\"rings\":[[[2,2],[2,4],[4,4],[4,2],[2,2]]],\"spatialReference\":{\"wkid\":4326}}"); + OperatorDisjoint.local().accelerateGeometry(geometry1.getGeometry(), geometry1.getSpatialReference(), GeometryAccelerationDegree.enumMedium); + boolean res = OperatorDisjoint.local().execute(geometry1.getGeometry(), geometry2.getGeometry(), geometry1.getSpatialReference(), null); + assertTrue(!res); + } } diff --git a/src/test/java/com/esri/core/geometry/TestSerialization.java b/src/test/java/com/esri/core/geometry/TestSerialization.java index 289546da..4d736a8c 100644 --- a/src/test/java/com/esri/core/geometry/TestSerialization.java +++ b/src/test/java/com/esri/core/geometry/TestSerialization.java @@ -37,18 +37,17 @@ public void testSerializePoint() { } - // try - // { - // FileOutputStream streamOut = new FileOutputStream(m_thisDirectory + - // "savedPoint.txt"); - // ObjectOutputStream oo = new ObjectOutputStream(streamOut); - // Point pt = new Point(10, 40); - // oo.writeObject(pt); - // } - // catch(Exception ex) - // { - // fail("Point serialization failure"); - // } + //try + //{ + //FileOutputStream streamOut = new FileOutputStream("c:/temp/savedPoint1.txt"); + //ObjectOutputStream oo = new ObjectOutputStream(streamOut); + //Point pt = new Point(10, 40, 2); + //oo.writeObject(pt); + //} + //catch(Exception ex) + //{ + //fail("Point serialization failure"); + //} try { InputStream s = TestSerialization.class @@ -59,6 +58,17 @@ public void testSerializePoint() { } catch (Exception ex) { fail("Point serialization failure"); } + + try { + InputStream s = TestSerialization.class + .getResourceAsStream("savedPoint1.txt"); + ObjectInputStream ii = new ObjectInputStream(s); + Point ptRes = (Point) ii.readObject(); + assertTrue(ptRes.getX() == 10 && ptRes.getY() == 40 && ptRes.getZ() == 2); + } catch (Exception ex) { + fail("Point serialization failure"); + } + } @Test @@ -98,22 +108,21 @@ public void testSerializePolygon() { fail("Polygon serialization failure"); } - // try - // { - // FileOutputStream streamOut = new FileOutputStream(m_thisDirectory + - // "savedPolygon.txt"); - // ObjectOutputStream oo = new ObjectOutputStream(streamOut); - // Polygon pt = new Polygon(); - // pt.startPath(10, 10); - // pt.lineTo(100, 100); - // pt.lineTo(200, 100); - // pt = (Polygon)GeometryEngine.simplify(pt, null); - // oo.writeObject(pt); - // } - // catch(Exception ex) - // { - // fail("Polygon serialization failure"); - // } + //try + //{ + //FileOutputStream streamOut = new FileOutputStream("c:/temp/savedPolygon1.txt"); + //ObjectOutputStream oo = new ObjectOutputStream(streamOut); + //Polygon pt = new Polygon(); + //pt.startPath(10, 10); + //pt.lineTo(100, 100); + //pt.lineTo(200, 100); + //pt = (Polygon)GeometryEngine.simplify(pt, null); + //oo.writeObject(pt); + //} + //catch(Exception ex) + //{ + //fail("Polygon serialization failure"); + //} try { InputStream s = TestSerialization.class @@ -124,6 +133,15 @@ public void testSerializePolygon() { } catch (Exception ex) { fail("Polygon serialization failure"); } + try { + InputStream s = TestSerialization.class + .getResourceAsStream("savedPolygon1.txt"); + ObjectInputStream ii = new ObjectInputStream(s); + Polygon ptRes = (Polygon) ii.readObject(); + assertTrue(ptRes != null); + } catch (Exception ex) { + fail("Polygon serialization failure"); + } } @Test @@ -145,21 +163,20 @@ public void testSerializePolyline() { fail("Polyline serialization failure"); } - // try - // { - // FileOutputStream streamOut = new FileOutputStream(m_thisDirectory + - // "savedPolyline.txt"); - // ObjectOutputStream oo = new ObjectOutputStream(streamOut); - // Polyline pt = new Polyline(); - // pt.startPath(10, 10); - // pt.lineTo(100, 100); - // pt.lineTo(200, 100); - // oo.writeObject(pt); - // } - // catch(Exception ex) - // { - // fail("Polyline serialization failure"); - // } + //try + //{ + //FileOutputStream streamOut = new FileOutputStream("c:/temp/savedPolyline1.txt"); + //ObjectOutputStream oo = new ObjectOutputStream(streamOut); + //Polyline pt = new Polyline(); + //pt.startPath(10, 10); + //pt.lineTo(100, 100); + //pt.lineTo(200, 100); + //oo.writeObject(pt); + //} + //catch(Exception ex) + //{ + //fail("Polyline serialization failure"); + //} try { InputStream s = TestSerialization.class @@ -170,6 +187,15 @@ public void testSerializePolyline() { } catch (Exception ex) { fail("Polyline serialization failure"); } + try { + InputStream s = TestSerialization.class + .getResourceAsStream("savedPolyline1.txt"); + ObjectInputStream ii = new ObjectInputStream(s); + Polyline ptRes = (Polyline) ii.readObject(); + assertTrue(ptRes != null); + } catch (Exception ex) { + fail("Polyline serialization failure"); + } } @Test @@ -188,18 +214,17 @@ public void testSerializeEnvelope() { fail("Envelope serialization failure"); } - // try - // { - // FileOutputStream streamOut = new FileOutputStream(m_thisDirectory + - // "savedEnvelope.txt"); - // ObjectOutputStream oo = new ObjectOutputStream(streamOut); - // Envelope pt = new Envelope(10, 10, 400, 300); - // oo.writeObject(pt); - // } - // catch(Exception ex) - // { - // fail("Envelope serialization failure"); - // } + //try + //{ + //FileOutputStream streamOut = new FileOutputStream("c:/temp/savedEnvelope1.txt"); + //ObjectOutputStream oo = new ObjectOutputStream(streamOut); + //Envelope pt = new Envelope(10, 10, 400, 300); + //oo.writeObject(pt); + //} + //catch(Exception ex) + //{ + //fail("Envelope serialization failure"); + //} try { InputStream s = TestSerialization.class @@ -210,6 +235,15 @@ public void testSerializeEnvelope() { } catch (Exception ex) { fail("Envelope serialization failure"); } + try { + InputStream s = TestSerialization.class + .getResourceAsStream("savedEnvelope1.txt"); + ObjectInputStream ii = new ObjectInputStream(s); + Envelope ptRes = (Envelope) ii.readObject(); + assertTrue(ptRes.getXMax() == 400); + } catch (Exception ex) { + fail("Envelope serialization failure"); + } } @Test @@ -230,20 +264,19 @@ public void testSerializeMultiPoint() { fail("MultiPoint serialization failure"); } - // try - // { - // FileOutputStream streamOut = new FileOutputStream(m_thisDirectory + - // "savedMultiPoint.txt"); - // ObjectOutputStream oo = new ObjectOutputStream(streamOut); - // MultiPoint pt = new MultiPoint(); - // pt.add(10, 30); - // pt.add(120, 40); - // oo.writeObject(pt); - // } - // catch(Exception ex) - // { - // fail("MultiPoint serialization failure"); - // } + //try + //{ + //FileOutputStream streamOut = new FileOutputStream("c:/temp/savedMultiPoint1.txt"); + //ObjectOutputStream oo = new ObjectOutputStream(streamOut); + //MultiPoint pt = new MultiPoint(); + //pt.add(10, 30); + //pt.add(120, 40); + //oo.writeObject(pt); + //} + //catch(Exception ex) + //{ + //fail("MultiPoint serialization failure"); + //} try { InputStream s = TestSerialization.class @@ -254,6 +287,15 @@ public void testSerializeMultiPoint() { } catch (Exception ex) { fail("MultiPoint serialization failure"); } + try { + InputStream s = TestSerialization.class + .getResourceAsStream("savedMultiPoint1.txt"); + ObjectInputStream ii = new ObjectInputStream(s); + MultiPoint ptRes = (MultiPoint) ii.readObject(); + assertTrue(ptRes.getPoint(1).getY() == 40); + } catch (Exception ex) { + fail("MultiPoint serialization failure"); + } } @Test diff --git a/src/test/java/com/esri/core/geometry/TestSpatialReference.java b/src/test/java/com/esri/core/geometry/TestSpatialReference.java index f0f6aacd..8d21712f 100644 --- a/src/test/java/com/esri/core/geometry/TestSpatialReference.java +++ b/src/test/java/com/esri/core/geometry/TestSpatialReference.java @@ -1,17 +1,17 @@ package com.esri.core.geometry; -import org.junit.Assert; +import junit.framework.TestCase; import org.junit.Test; -public class TestSpatialReference extends Assert { +public class TestSpatialReference extends TestCase { @Test - public void equals() { - final String wktext1 = "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]"; - final String wktext2 = "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]"; + public void testEquals() { + String wktext1 = "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]"; + String wktext2 = "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_Auxiliary_Sphere\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"Standard_Parallel_1\",0.0],PARAMETER[\"Auxiliary_Sphere_Type\",0.0],UNIT[\"Meter\",1.0]]"; - final SpatialReference a1 = SpatialReference.create(wktext1); - final SpatialReference b = SpatialReference.create(wktext2); - final SpatialReference a2 = SpatialReference.create(wktext1); + SpatialReference a1 = SpatialReference.create(wktext1); + SpatialReference b = SpatialReference.create(wktext2); + SpatialReference a2 = SpatialReference.create(wktext1); assertTrue(a1.equals(a1)); assertTrue(b.equals(b)); @@ -22,3 +22,4 @@ public void equals() { assertFalse(b.equals(a1)); } } + diff --git a/src/test/java/com/esri/core/geometry/TestWkid.java b/src/test/java/com/esri/core/geometry/TestWkid.java index d953ae95..cd249233 100644 --- a/src/test/java/com/esri/core/geometry/TestWkid.java +++ b/src/test/java/com/esri/core/geometry/TestWkid.java @@ -19,6 +19,7 @@ public void test() { assertTrue(Math.abs(tol84 - 1e-8) < 1e-8 * 1e-8); } + @Test public void test_80() { SpatialReference sr = SpatialReference.create(3857); diff --git a/src/test/resources/com/esri/core/geometry/savedEnvelope1.txt b/src/test/resources/com/esri/core/geometry/savedEnvelope1.txt new file mode 100644 index 00000000..5f6ee18b Binary files /dev/null and b/src/test/resources/com/esri/core/geometry/savedEnvelope1.txt differ diff --git a/src/test/resources/com/esri/core/geometry/savedMultiPoint1.txt b/src/test/resources/com/esri/core/geometry/savedMultiPoint1.txt new file mode 100644 index 00000000..e38260f2 Binary files /dev/null and b/src/test/resources/com/esri/core/geometry/savedMultiPoint1.txt differ diff --git a/src/test/resources/com/esri/core/geometry/savedPoint1.txt b/src/test/resources/com/esri/core/geometry/savedPoint1.txt new file mode 100644 index 00000000..f9efdbe5 Binary files /dev/null and b/src/test/resources/com/esri/core/geometry/savedPoint1.txt differ diff --git a/src/test/resources/com/esri/core/geometry/savedPolygon1.txt b/src/test/resources/com/esri/core/geometry/savedPolygon1.txt new file mode 100644 index 00000000..1fd9c105 Binary files /dev/null and b/src/test/resources/com/esri/core/geometry/savedPolygon1.txt differ diff --git a/src/test/resources/com/esri/core/geometry/savedPolyline1.txt b/src/test/resources/com/esri/core/geometry/savedPolyline1.txt new file mode 100644 index 00000000..a5624471 Binary files /dev/null and b/src/test/resources/com/esri/core/geometry/savedPolyline1.txt differ