/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010, 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.data.shapefile.shp;
import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LinearRing;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.util.List;
import org.geotoolkit.geometry.jts.JTS;
import org.apache.sis.storage.DataStoreException;
/**
* Decimation while reading
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class DecimatePolygonHandler extends PolygonHandler {
private final double resX;
private final double resY;
public DecimatePolygonHandler(final boolean read3D, final double[] res){
super(read3D);
this.resX = res[0];
this.resY = res[1];
}
public DecimatePolygonHandler(final ShapeType type, final boolean read3D, final double[] res) throws DataStoreException{
super(type,read3D);
this.resX = res[0];
this.resY = res[1];
}
@Override
public Object read(final ByteBuffer buffer, final ShapeType type) {
if (type == ShapeType.NULL) {
return createNull();
}
//clear from previous read
shells.clear();
holes.clear();
// skip the bounds
buffer.position(buffer.position() + 32);
final int numParts = buffer.getInt();
final int numPoints = buffer.getInt();
final int[] partOffsets = new int[numParts];
for (int i = 0; i < numParts; i++) {
partOffsets[i] = buffer.getInt();
}
final DoubleBuffer dbuffer = buffer.asDoubleBuffer();
final int dimensions = (read3D && shapeType == ShapeType.POLYGONZ)? 3:2;
//read everything in one round : +2 for minZ/maxZ
final double[] coords = new double[numPoints*dimensions + ((dimensions==2)?0:2)];
final int xySize = numPoints*2;
dbuffer.get(coords);
int coordIndex = 0;
for (int part = 0; part < numParts; part++) {
final int finish;
if (part == (numParts - 1)) {
finish = numPoints;
} else {
finish = partOffsets[part + 1];
}
final int length = finish - partOffsets[part];
// REVISIT: polyons with only 1 to 3 points are not polygons -
// geometryFactory will bomb so we skip if we find one.
if(length > 0 && length < 4){
coordIndex += length;
continue;
}
final Coordinate[] points = new Coordinate[length];
for (int i = 0; i < length; i++) {
if(dimensions==2){
points[i] = new Coordinate(coords[coordIndex*2],coords[coordIndex*2+1]);
}else{
points[i] = new Coordinate(coords[coordIndex*2],coords[coordIndex*2+1],coords[xySize+coordIndex+2]);
}
coordIndex++;
}
JTS.ensureClosed(points);
final LinearRing ring = GEOMETRY_FACTORY.createLinearRing(decimateRing(points));
if (CGAlgorithms.isCCW(points)) {
// counter-clockwise
holes.add(ring);
} else {
// clockwise
shells.add(ring);
}
}
// quick optimization: if there's only one shell no need to check
// for holes inclusion
if (shells.size() == 1) {
return createMulti(shells.get(0), holes);
}
// if for some reason, there is only one hole, we just reverse it and
// carry on.
else if (holes.size() == 1 && shells.isEmpty()) {
//LOGGER.warning("only one hole in this polygon record");
return createMulti(JTS.reverseRing(holes.get(0)));
} else {
// build an association between shells and holes
final List<List<LinearRing>> holesForShells = assignHolesToShells(shells, holes);
return buildGeometries(shells, holes, holesForShells);
}
}
private Coordinate[] decimateRing(final Coordinate[] coords) {
int lenght = 1;
int i=1,j=0;
for(; i<coords.length-1; i++){
final double distX = Math.abs(coords[j].x - coords[i].x);
if(distX > resX){
lenght++;
j++;
coords[j] = coords[i];
continue;
}
final double distY = Math.abs(coords[j].y - coords[i].y);
if(distY > resY){
lenght++;
j++;
coords[j] = coords[i];
continue;
}
}
//always include the last point, to preserve the ring
lenght++; j++;
coords[j] = coords[i];
if(lenght == coords.length){
//nothing to decimate
return coords;
}else{
//ensure we have the minimum number of points
if(lenght < 4){
final Coordinate lastCoord = coords[coords.length-1];
for(i=lenght-1;i<4;i++){
coords[i] = lastCoord;
}
lenght = 4;
}
//ensure it forms a closed line string if asked for
if(!coords[0].equals2D(coords[lenght-1])){
coords[lenght-1] = new Coordinate(coords[0]);
}
final Coordinate[] cs = new Coordinate[lenght];
System.arraycopy(coords, 0, cs, 0, lenght);
return cs;
}
}
}