/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.processing.coverage.coveragetovector; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Polygon; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.apache.sis.measure.NumberRange; /** * Define an "in construction" geometries. * This type of objects contain a list of floeatings coordinates sequences * that are progressivly merged together to obtain a polygon which might contain holes. * * @author Johann Sorel (Geomatys) */ public class Boundary { private static final GeometryFactory GF = new GeometryFactory(); private static final LinearRing[] EMPTY_RING_ARRAY = new LinearRing[0]; //finished geometries private final List<LinearRing> holes = new ArrayList<LinearRing>(); //in construction geometries private final LinkedList<LinkedList<Coordinate>> floatings = new LinkedList<LinkedList<Coordinate>>(); public final NumberRange range; public Boundary(final NumberRange range){ this.range = range; } public void start(final int firstX, final int secondX, final int y){ if(firstX == secondX) throw new IllegalArgumentException("bugging algorithm"); final LinkedList<Coordinate> exterior = new LinkedList<Coordinate>(); exterior.addFirst(new Coordinate(firstX, y)); exterior.addFirst(new Coordinate(firstX, y+1)); exterior.addLast(new Coordinate(secondX, y)); exterior.addLast(new Coordinate(secondX, y+1)); floatings.add(exterior); //checkValidity(); } public void addFloating(final Coordinate from, final Coordinate to){ if(from.equals2D(to)){ throw new IllegalArgumentException("bugging algorithm"); } //System.err.println("Add Floating From: " + from + " New : " + to ); final LinkedList<Coordinate> ll = new LinkedList<Coordinate>(); ll.addFirst(to); ll.addLast(from); floatings.add(ll); //checkValidity(); } public void add(final Coordinate from, final Coordinate to){ if(from.equals2D(to)){ throw new IllegalArgumentException("bugging algorithm"); } //System.err.println("Add From: " + from + " New : " + to ); for(final LinkedList<Coordinate> ll : floatings){ final Coordinate first = ll.getFirst(); if(first.equals2D(from)){ if(from.x == to.x && ll.size() > 1){ final Coordinate second = ll.get(1); if(second.x == from.x){ //points are aligned, just move the first point first.y = to.y; }else{ //points are not aligned, must create a new point ll.addFirst(to); } }else if(from.y == to.y && ll.size() > 1){ final Coordinate second = ll.get(1); if(second.y == from.y){ //points are aligned, just move the first point first.x = to.x; }else{ //points are not aligned, must create a new point ll.addFirst(to); } }else{ ll.addFirst(to); } //checkValidity(); return; } final Coordinate last = ll.getLast(); if(last.equals2D(from)){ if(from.x == to.x && ll.size() > 1){ final Coordinate second = ll.get(ll.size()-2); if(second.x == from.x){ //points are aligned, just move the first point last.y = to.y; }else{ //points are not aligned, must create a new point ll.addLast(to); } }else if(from.y == to.y && ll.size() > 1){ final Coordinate second = ll.get(ll.size()-2); if(second.y == from.y){ //points are aligned, just move the first point last.x = to.x; }else{ //points are not aligned, must create a new point ll.addLast(to); } }else{ ll.addLast(to); } //checkValidity(); return; } } //checkValidity(); throw new IllegalArgumentException("bugging algorithm"); } public Polygon link(final Coordinate from, final Coordinate to){ if(from.equals2D(to)){ throw new IllegalArgumentException("bugging algorithm : " + to); } //System.err.println("Link From: " + from + " to : " + to ); LinkedList<Coordinate> fromList = null; boolean fromStart = false; LinkedList<Coordinate> toList = null; boolean toStart = false; for(final LinkedList<Coordinate> ll : floatings){ if(fromList == null){ final Coordinate first = ll.getFirst(); final Coordinate last = ll.getLast(); if(first.equals2D(from)){ fromStart = true; fromList = ll; }else if(last.equals2D(from)){ fromStart = false; fromList = ll; } } if(toList == null){ final Coordinate first = ll.getFirst(); final Coordinate last = ll.getLast(); if(first.equals2D(to)){ toStart = true; toList = ll; }else if(last.equals2D(to)){ toStart = false; toList = ll; } } if(fromList != null && toList != null) break; } if(fromList != null && toList != null){ if(fromList == toList){ //same list finish it //checkValidity(); return finish(fromList); }else{ combine(fromList, fromStart, toList, toStart); //checkValidity(); return null; } }else if(fromList != null ){ add(from, to); //checkValidity(); return null; }else if(toList != null){ add(to, from); //checkValidity(); return null; } //checkValidity(); throw new IllegalArgumentException("bugging algorithm"); } private void combine(final LinkedList<Coordinate> fromList, final boolean fromStart, final LinkedList<Coordinate> toList, final boolean toStart){ if(fromStart){ if(toStart){ while(!toList.isEmpty()){ fromList.addFirst(toList.pollFirst()); } }else{ while(!toList.isEmpty()){ fromList.addFirst(toList.pollLast()); } } }else{ if(toStart){ while(!toList.isEmpty()){ fromList.addLast(toList.pollFirst()); } }else{ while(!toList.isEmpty()){ fromList.addLast(toList.pollLast()); } } } floatings.remove(toList); //checkValidity(); } public void checkValidity(){ //check for list with less than 2 elements for(LinkedList<Coordinate> ll : floatings){ if(ll.size() < 2){ //System.err.println(">>>> ERROR : " + this.toString()); throw new IllegalArgumentException("What ? a list with less than 2 elements, not valid !"); } } //check for diagonal cases for(LinkedList<Coordinate> ll : floatings){ Coordinate last = ll.get(0); for(int i=1;i<ll.size();i++){ Coordinate current = ll.get(i); if(last.x != current.x && last.y != current.y){ //System.err.println(">>>> ERROR : " + this.toString()); throw new IllegalArgumentException("What ? a diagonal, not valid !"); } last = current; } } } private Polygon finish(final LinkedList<Coordinate> coords){ if(floatings.size() == 1){ //closing the polygon enveloppe //copy first point at the end coords.add(new Coordinate(coords.get(0))); final Coordinate[] ring = coords.toArray(new Coordinate[coords.size()]); final LinearRing exterior = GF.createLinearRing(ring); final Polygon polygon = GF.createPolygon(exterior, holes.toArray(EMPTY_RING_ARRAY)); polygon.setUserData(range); floatings.remove(coords); //checkValidity(); return polygon; }else{ //closing a hole in the geometry //copy first point at the end coords.add(new Coordinate(coords.get(0))); final Coordinate[] ring = coords.toArray(new Coordinate[coords.size()]); holes.add(GF.createLinearRing(ring)); floatings.remove(coords); //checkValidity(); return null; } } private static void reverse(final Coordinate[] array){ for(int l=0, r=array.length-1; l<r; l++, r--) { Coordinate temp = array[l]; array[l] = array[r]; array[r] = temp; } } public Polygon merge(final Boundary candidate){ //System.err.println("M > before : " + toString()); //System.err.println("M > with : " + candidate.toString()); //merge the floating sequences this.floatings.addAll(candidate.floatings); //merge the holes this.holes.addAll(candidate.holes); candidate.floatings.clear(); candidate.holes.clear(); //System.err.println("M > after : " + toString()); //checkValidity(); return null; } @Override public String toString() { final StringBuilder sb = new StringBuilder("Boundary : "); sb.append(range.toString()); for(LinkedList<Coordinate> coords : floatings){ sb.append(" \t{"); for(Coordinate c : coords){ sb.append('[').append((int)c.x).append(';').append((int)c.y).append(']'); } sb.append('}'); } return sb.toString(); } public boolean isEmpty(){ return floatings.isEmpty(); } }