Skip to content

Commit

Permalink
Iox2wkb/RingCollector: fix issues with duplicate coordinates (claeis/…
Browse files Browse the repository at this point in the history
  • Loading branch information
claeis committed Apr 13, 2023
1 parent fa4dfac commit 7f87e74
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 73 deletions.
1 change: 1 addition & 0 deletions doc/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ideas/open issues

iox-ili 1.21.18 (SNAPSHOT)
-----------------------------
- Iox2wkb/RingCollector: fix issues with duplicate coordinates (ili2db#510)

iox-ili 1.21.17 (2023-02-22)
-----------------------------
Expand Down
124 changes: 72 additions & 52 deletions src/main/java/ch/interlis/iox_j/wkb/Iox2wkb.java
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ public byte[] polyline2wkb(IomObject polylineObjs[],boolean isSurfaceOrArea,bool
throws Iox2wkbException
{

List<List<LineSegment>> lines = collectSegments(polylineObjs, asCompoundCurve, p, false);
List<List<LineSegment>> lines = new ArrayList<List<LineSegment>>();
for(int polylinei=0;polylinei<polylineObjs.length;polylinei++){
IomObject polyline = polylineObjs[polylinei];
lines.addAll(collectSegments(new IomObject[] {polyline}, asCompoundCurve, p, false));
}

try {
os.reset();
for (List<LineSegment> line : lines){
Expand All @@ -290,53 +295,68 @@ public byte[] polyline2wkb(IomObject polylineObjs[],boolean isSurfaceOrArea,bool
return os.toByteArray();
}

private List<List<LineSegment>> collectSegments(IomObject[] polylineObjs, boolean asCompoundCurve, double p, boolean repairTouchingLine)
throws Iox2wkbException {
RingCollector ringCollector = new RingCollector(repairTouchingLine);
for (IomObject polylineObj : polylineObjs) {
if (polylineObj == null) {
continue;
}

ringCollector.startNewRing();
for(int sequencei=0; sequencei<polylineObj.getattrvaluecount(Iom_jObject.POLYLINE_SEQUENCE); sequencei++) {
IomObject sequence=polylineObj.getattrobj(Iom_jObject.POLYLINE_SEQUENCE,sequencei);
int segmentc=sequence.getattrvaluecount(Iom_jObject.SEGMENTS_SEGMENT);

for(int segmenti=0; segmenti<segmentc; segmenti++){
IomObject segment = sequence.getattrobj(Iom_jObject.SEGMENTS_SEGMENT, segmenti);
Coordinate segmentCoordinate = coord2JTS(segment);

if (segment.getobjecttag().equals(Iom_jObject.COORD)){
ringCollector.add(segmentCoordinate, WKBConstants.wkbLineString);
}
else if (segment.getobjecttag().equals(Iom_jObject.ARC))
{
if (asCompoundCurve){
ringCollector.add(arcSupportingCoord2JTS(segment),WKBConstants.wkbCircularString);
ringCollector.add(segmentCoordinate,WKBConstants.wkbCircularString);
}else if(p==0.0){
ringCollector.add(arcSupportingCoord2JTS(segment),WKBConstants.wkbLineString);
ringCollector.add(segmentCoordinate,WKBConstants.wkbLineString);
} else {
Coordinate lastCoordinate = ringCollector.getLastCoordinate();
ArcSegment arc = new ArcSegment(lastCoordinate, arcSupportingCoord2JTS(segment), segmentCoordinate, p);
for (Coordinate c: arc.getCoordinates()) {
if (c.equals(lastCoordinate)) continue;
ringCollector.add(c,WKBConstants.wkbLineString);
}
}
}
else {
throw new Iox2wkbException("custom line form not supported");
}
}
}
}

return ringCollector.getRings();
}
private List<List<LineSegment>> collectSegments(IomObject[] polylineObjs, boolean asCompoundCurve, double p, boolean repairTouchingLine)
throws Iox2wkbException {
RingCollector ringCollector = new RingCollector(repairTouchingLine);
ringCollector.startNewRing();
Coordinate lastCoordinate = null;
for (IomObject polylineObj : polylineObjs) {
if (polylineObj == null) {
continue;
}

for(int sequencei=0; sequencei<polylineObj.getattrvaluecount(Iom_jObject.POLYLINE_SEQUENCE); sequencei++) {
IomObject sequence=polylineObj.getattrobj(Iom_jObject.POLYLINE_SEQUENCE,sequencei);
int segmentc=sequence.getattrvaluecount(Iom_jObject.SEGMENTS_SEGMENT);

for(int segmenti=0; segmenti<segmentc; segmenti++){
IomObject segment = sequence.getattrobj(Iom_jObject.SEGMENTS_SEGMENT, segmenti);
Coordinate segmentCoordinate = coord2JTS(segment);
lastCoordinate = ringCollector.getLastCoordinate();

if (segment.getobjecttag().equals(Iom_jObject.COORD)){
if(lastCoordinate==null || !lastCoordinate.equals(segmentCoordinate)) {
ringCollector.addStraight(segmentCoordinate);
}
}
else if (segment.getobjecttag().equals(Iom_jObject.ARC))
{
Coordinate midPt = arcSupportingCoord2JTS(segment);
if(lastCoordinate!=null && lastCoordinate.equals(midPt)) {
if(!lastCoordinate.equals(segmentCoordinate)) {
ringCollector.addStraight(segmentCoordinate);
}
}else if(midPt.equals(segmentCoordinate)){
if(lastCoordinate==null || !lastCoordinate.equals(segmentCoordinate)) {
ringCollector.addStraight(segmentCoordinate);
}
}else {
if (asCompoundCurve){
ringCollector.addArc(midPt,segmentCoordinate);
}else if(p==0.0){
ringCollector.addStraight(midPt);
ringCollector.addStraight(segmentCoordinate);
} else {
ArcSegment arc = new ArcSegment(lastCoordinate,midPt, segmentCoordinate, p);
for (Coordinate c: arc.getCoordinates()) {
if(lastCoordinate!=null && !lastCoordinate.equals(c)) {
ringCollector.addStraight(c);
lastCoordinate=c;
}
}
}
}
}
else {
throw new Iox2wkbException("custom line form not supported");
}
}
}
}

return ringCollector.getRings();
}

/** Converts a SURFACE to a JTS Polygon.
* @param obj INTERLIS SURFACE structure
* @param strokeP maximum stroke to use when removing ARCs
Expand All @@ -361,7 +381,7 @@ public byte[] surface2wkb(IomObject obj,boolean asCurvePolygon,double strokeP, b
throw new Iox2wkbException("clipped surface not supported");
}

List<IomObject> polylines = new ArrayList<IomObject>();
List<List<LineSegment>> rings = new ArrayList<List<LineSegment>>();
for (int surfacei = 0; surfacei < obj.getattrvaluecount(Iom_jObject.MULTISURFACE_SURFACE); surfacei++) {
if (surfacei > 0) {
throw new Iox2wkbException("unclipped surface with multi 'surface' elements");
Expand All @@ -372,6 +392,7 @@ public byte[] surface2wkb(IomObject obj,boolean asCurvePolygon,double strokeP, b
for (int boundaryi = 0; boundaryi < boundaryc; boundaryi++) {
IomObject boundary = surface.getattrobj(Iom_jObject.SURFACE_BOUNDARY, boundaryi);
int polylinec = boundary.getattrvaluecount(Iom_jObject.BOUNDARY_POLYLINE);
List<IomObject> polylines = new ArrayList<IomObject>();
for (int polylinei = 0; polylinei < polylinec; polylinei++){
IomObject polyline = boundary.getattrobj(Iom_jObject.BOUNDARY_POLYLINE, polylinei);
if (polyline.getattrobj(Iom_jObject.POLYLINE_LINEATTR, 0) != null) {
Expand All @@ -382,10 +403,10 @@ public byte[] surface2wkb(IomObject obj,boolean asCurvePolygon,double strokeP, b
}
polylines.add(polyline);
}
rings.addAll(collectSegments(polylines.toArray(new IomObject[polylines.size()]), asCurvePolygon, strokeP, repairTouchingLine));
}
}

List<List<LineSegment>> rings = collectSegments(polylines.toArray(new IomObject[0]), asCurvePolygon, strokeP, repairTouchingLine);
try {
os.reset();
writeByteOrder();
Expand Down Expand Up @@ -447,18 +468,17 @@ public byte[] multiline2wkb(IomObject obj,boolean asCurve,double strokeP)
return null;
}

List<IomObject> polylines = new ArrayList<IomObject>();
int polylinec=obj.getattrvaluecount(Iom_jObject.MULTIPOLYLINE_POLYLINE);

List<List<LineSegment>> lines = new ArrayList<List<LineSegment>>();
for(int polylinei=0;polylinei<polylinec;polylinei++){
IomObject polyline = obj.getattrobj(Iom_jObject.MULTIPOLYLINE_POLYLINE,polylinei);
if (polyline.getobjectconsistency() == IomConstants.IOM_INCOMPLETE) {
throw new Iox2wkbException("clipped polyline not supported");
}
polylines.add(polyline);
lines.addAll(collectSegments(new IomObject[] {polyline}, asCurve, strokeP, false));
}

List<List<LineSegment>> lines = collectSegments(polylines.toArray(new IomObject[0]), asCurve, strokeP, false);

try {
os.reset();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/ch/interlis/iox_j/wkb/LineSegment.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,8 @@ public LineSegment splitTailAt(Coordinate coordinate){
public Iterator<Coordinate> iterator() {
return coordinates.iterator();
}

public boolean contains(Coordinate coordinate) {
return coordinatesMap.containsKey(coordinate);
}
}
56 changes: 36 additions & 20 deletions src/main/java/ch/interlis/iox_j/wkb/RingCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ public class RingCollector {

private final boolean repairSelfTouchingRing;
private final LinkedList<List<LineSegment>> rings;
private final Map<Coordinate, Integer> coordinates2Segment;
private int currentRingIndex = -1;
private Coordinate start;
private Coordinate startOfCurrentRing;
private Coordinate carryOverCoordinate;

public RingCollector (boolean repairSelfTouchingRing){
this.repairSelfTouchingRing = repairSelfTouchingRing;
this.rings = new LinkedList();
this.coordinates2Segment = new HashMap<Coordinate, Integer>();
this.rings = new LinkedList<List<LineSegment>>();
}

private List<LineSegment> getCurrentRing(){
Expand All @@ -45,8 +43,7 @@ public void startNewRing() {
rings.addLast(new ArrayList<LineSegment>());
rings.getLast().add(new LineSegment());
currentRingIndex = rings.size() - 1;
coordinates2Segment.clear();
start = null;
startOfCurrentRing = null;
carryOverCoordinate = null;
}

Expand All @@ -55,43 +52,46 @@ public void startNewRing() {
* @param coordinate Coordinate to add to current ring.
* @param WkbType Required type for the LineSegment the coordinate is added to.
*/
public void add(Coordinate coordinate, int WkbType){
private void add(Coordinate coordinate, int WkbType){
if (carryOverCoordinate != null && repairSelfTouchingRing){
Coordinate carry = carryOverCoordinate;
startNewRing();
getCurrentSegment().add(carry);
start = carry;
coordinates2Segment.put(carry, 0);
startOfCurrentRing = carry;
}

List<LineSegment> ring = getCurrentRing();
LineSegment segment = getCurrentSegment();

// different/new segment required?
if (!segment.trySetWkbType(WkbType)) {
Coordinate lastCoord = segment.getLast();
segment = new LineSegment(WkbType);
segment.add(lastCoord);
ring.add(segment);
}

if (start == null) {
start = coordinate;
if (startOfCurrentRing == null) {
// new ring
startOfCurrentRing = coordinate;
}
else if (start.equals(coordinate)){
else if (startOfCurrentRing.equals(coordinate)){
// not a new ring, but same coord as start of current ring
// finish current ring, and keep same coord as start of a new ring
carryOverCoordinate = coordinate;
}
else if (repairSelfTouchingRing && coordinates2Segment.containsKey(coordinate) && !start.equals(coordinate)){
else if (repairSelfTouchingRing && getSegmentIdx(getCurrentRing(),coordinate)!=null && !startOfCurrentRing.equals(coordinate)){
// not a new ring, and coord on current ring but not the start of current ring
extractInnerRing(coordinate);
}

segment = getCurrentSegment();
segment.add(coordinate);
coordinates2Segment.put(coordinate, getCurrentRing().size() - 1);
}

private void extractInnerRing(Coordinate coordinate) {
List<LineSegment> ring = getCurrentRing();
int segmentIdx = coordinates2Segment.get(coordinate);
int segmentIdx = getSegmentIdx(ring,coordinate);
LineSegment containingSegment = ring.get(segmentIdx);
ArrayList<LineSegment> extractedRing = new ArrayList<LineSegment>();

Expand All @@ -108,15 +108,16 @@ private void extractInnerRing(Coordinate coordinate) {
extractedRing.get(extractedRing.size() - 1).add(coordinate);

rings.add(extractedRing);
removeRingFromMap(extractedRing);
}

private void removeRingFromMap(List<LineSegment> ring){
for (LineSegment segment : ring){
for (Coordinate c : segment){
coordinates2Segment.remove(c);
private Integer getSegmentIdx(List<LineSegment> ring, Coordinate coordinate) {
for (int idx=0;idx<ring.size();idx++){
LineSegment segment=ring.get(idx);
if(segment.contains(coordinate)) {
return idx;
}
}
return null;
}

public List<List<LineSegment>> getRings() {
Expand All @@ -125,7 +126,22 @@ public List<List<LineSegment>> getRings() {

public Coordinate getLastCoordinate() {
List<LineSegment> ring = getCurrentRing();
if(ring==null) {
return null;
}
LineSegment segment = ring.get(ring.size() - 1);
if(segment.size()==0) {
return null;
}
return segment.getLast();
}

public void addStraight(Coordinate coord) {
add(coord, WKBConstants.wkbLineString);
}

public void addArc(Coordinate midPt, Coordinate endPt) {
add(midPt, WKBConstants.wkbCircularString);
add(endPt, WKBConstants.wkbCircularString);
}
}
Loading

0 comments on commit 7f87e74

Please sign in to comment.