/*
* 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 java.nio.ByteBuffer;
import com.vividsolutions.jts.geom.LineString;
import java.nio.DoubleBuffer;
import org.apache.sis.storage.DataStoreException;
/**
* Decimation while reading
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class DecimateMultiLineHandler extends MultiLineHandler {
private final double resX;
private final double resY;
public DecimateMultiLineHandler(final boolean read3D, final double[] res){
super(read3D);
this.resX = res[0];
this.resY = res[1];
}
public DecimateMultiLineHandler(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();
}
final int dimensions = (read3D && shapeType == ShapeType.ARCZ) ? 3 : 2;
// skip bounding box (not needed)
buffer.position(buffer.position() + 4 * 8);
final int numParts = buffer.getInt();
final int numPoints = buffer.getInt(); // total number of points
//store each line string buffer start position
final int[] partOffsets = new int[numParts];
for (int i = 0; i < numParts; i++) {
partOffsets[i] = buffer.getInt();
}
// read the first two coordinates and start building the coordinate sequences
final double[][] lines = new double[numParts][0];
//use a double buffer to increase bulk reading
final DoubleBuffer dbuffer = buffer.asDoubleBuffer();
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];
final double[] coords;
if (length == 1) {
//only one point for a line, JTS do not like that, so we make two points at same place.
coords = new double[2*dimensions];
dbuffer.get(coords, 0, 2);
coords[2] = coords[0];
coords[3] = coords[1];
} else {
coords = new double[length*dimensions];
dbuffer.get(coords, 0, length*2);
}
lines[part] = coords;
}
// if we have another coordinate, read and add to the coordinate
// sequences
if (dimensions == 3) {
// z min, max
dbuffer.position(dbuffer.position() + 2);
for (int part = 0; part < numParts; part++) {
final int finish;
if (part == (numParts - 1)) {
finish = numPoints;
} else {
finish = partOffsets[part + 1];
}
final double[] coords = lines[part];
final int length = finish - partOffsets[part];
if (length == 1) {
//only one point for a line, JTS do not like that, so we make two points at same place.
coords[4] = coords[5] = dbuffer.get();
} else {
dbuffer.get(coords, length*2, length);
}
}
}
// Prepare line strings and return the multilinestring
final LineString[] lineCharSequences = new LineString[numParts];
for (int part = 0; part < numParts; part++) {
if(dimensions == 2){
lineCharSequences[part] = GEOMETRY_FACTORY.createLineString(
new ShapeCoordinateSequence2D(lines[part],decimateLine2D(lines[part])));
}else{
lineCharSequences[part] = GEOMETRY_FACTORY.createLineString(
new ShapeCoordinateSequence3D(lines[part]));
}
}
return GEOMETRY_FACTORY.createMultiLineString(lineCharSequences);
}
// private int decimateLine2D(double[] array){
// final int size = array.length/2;
// if(size < 3){
// return size;
// }
//
// //maximium distance from the line
// final double resxSq = resX * resX;
// final double resySq = resY * resY;
// double toleranceSq = Math.min(resxSq, resySq);
//
// int length = 2;
// int afterP1 = 2;
// double x1 = array[0];
// double y1 = array[1];
//
//search: for (int j=2; j<array.length; j+=2) {
// final double x2 = array[j ] - x1;
// final double y2 = array[j+1] - y1;
// final double x2Sq = x2 * x2;
// final double y2Sq = y2 * y2;
// if (x2 > resxSq || y2 > resySq) {
// final double lineLengthSq = x2Sq + y2Sq;
// afterP1 = j;
// while ((j += 2) < array.length) {
// int k = afterP1;
// do {
// final double px = array[k++] - x1;
// final double py = array[k++] - y1;
// /*
// * Definition of term:
// * - "the line" is the line from (x1,y1) to (x2,y2).
// *
// * Compute the squared distance from (x1,y1) to the projection of (xp,yp) on
// * the line. We do that by computing the dot product of the (x1,y1)-(xp,yp)
// * vector by the unitary vector colinear with the line.
// */
// double distanceSq = px * x2 + py * y2;
// distanceSq *= distanceSq / lineLengthSq;
// /*
// * Squared distance to line is the length of the (x1,y1)-(xp,yp)
// * vector minor the length we computed above (Phytagore relation).
// */
// distanceSq = px * px + py * py - distanceSq;
// if (distanceSq > toleranceSq) {
// array[length++] = x1 = array[j - 2];
// array[length++] = y1 = array[j - 1];
// continue search;
// }
// } while (k != j);
// }
// }
// }
//
// if(afterP1 != array.length){
// array[length++] = array[array.length - 2];
// array[length++] = array[array.length - 1];
// }
//
// return length/2;
//
// //we have at least two points when we are here
//// if(array.length == length){
//// //no points where removed
//// return array;
//// }else{
//// final double[] decimated = new double[length];
//// System.arraycopy(array, 0, decimated, 0, length);
//// return decimated;
//// }
// }
private int decimateLine2D(final double[] coords){
int lenght = 1;
for(int i=2,j=0; i<coords.length; i+=2){
final double distX = Math.abs(coords[j] - coords[i]);
if(distX > resX){
lenght++;
j+=2;
coords[j] = coords[i];
coords[j+1] = coords[i+1];
continue;
}
final double distY = Math.abs(coords[j+1] - coords[i+1]);
if(distY > resY){
lenght++;
j+=2;
coords[j] = coords[i];
coords[j+1] = coords[i+1];
continue;
}
}
if(lenght == 1){
//copy last point in second position
lenght++;
coords[2] = coords[coords.length-2];
coords[3] = coords[coords.length-1];
}
return lenght;
}
}