/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.qp.storeadapter.indexrow;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.row.WriteIndexRow;
import com.foundationdb.server.error.InvalidSpatialObjectException;
import com.foundationdb.server.service.blob.BlobRef;
import com.foundationdb.server.types.TClass;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.common.BigDecimalWrapper;
import com.foundationdb.server.types.common.types.TBigDecimal;
import com.foundationdb.server.types.mcompat.mtypes.MNumeric;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.server.spatial.Spatial;
import com.geophile.z.Space;
import com.geophile.z.SpatialObject;
import com.geophile.z.spatialobject.d2.Point;
import com.vividsolutions.jts.io.ParseException;
public class SpatialColumnHandler
{
public SpatialColumnHandler(Index index)
{
space = index.space();
dimensions = space.dimensions();
assert dimensions == 2;
assert dimensions == index.dimensions();
firstSpatialField = index.firstSpatialArgument();
lastSpatialField = index.lastSpatialArgument();
int spatialColumns = lastSpatialField - firstSpatialField + 1;
tinstances = new TInstance[spatialColumns];
positions = new int[spatialColumns];
for (int c = 0; c < spatialColumns; c++) {
IndexColumn indexColumn = index.getKeyColumns().get(firstSpatialField + c);
Column column = indexColumn.getColumn();
tinstances[c] = column.getType();
positions[c] = column.getPosition();
}
coords = new double[dimensions];
indexMethod = index.getIndexMethod();
}
public boolean handleSpatialColumn(WriteIndexRow writeIndexRow, int indexField, long zValue)
{
assert zValue >= 0;
if (indexField == firstSpatialField) {
writeIndexRow.pKey().append(zValue);
}
return indexField >= firstSpatialField && indexField <= lastSpatialField;
}
public void processSpatialObject(Row rowData, Operation operation)
{
bind(rowData);
long[] zs = zArray();
Spatial.shuffle(space, spatialObject, zs);
for (int i = 0; i < zs.length && zs[i] != Space.Z_NULL; i++) {
operation.handleZValue(zs[i]);
}
}
private void bind (Row row) {
if (lastSpatialField > firstSpatialField) {
assert indexMethod == Index.IndexMethod.GEO_LAT_LON : indexMethod;
// Point coordinates stored in two columns
assert dimensions == 2 : dimensions;
double coord = Double.NaN;
double x = Double.NaN;
double y = Double.NaN;
for (int d = 0; d < dimensions; d++) {
ValueSource source = row.value(positions[d]);
TClass tclass = source.getType().typeClass();
if (tclass == MNumeric.DECIMAL) {
BigDecimalWrapper wrapper = TBigDecimal.getWrapper(source, tinstances[d]);
coord = wrapper.asBigDecimal().doubleValue();
}
else if (tclass == MNumeric.BIGINT) {
coord = source.getInt64();
}
else if (tclass == MNumeric.INT) {
coord = source.getInt32();
}
else {
assert false : row.rowType().table().getColumn(positions[d]);
}
if (d == 0) {
x = coord;
} else {
y = coord;
}
coords[d] = coord;
}
spatialObject = new Point(x, y);
} else {
ValueSource source = row.value(positions[0]);
try {
switch (indexMethod) {
case GEO_WKB:
byte[] spatialObjectBytes = ((BlobRef)source.getObject()).getBytes();
spatialObject = Spatial.deserializeWKB(space, spatialObjectBytes);
break;
case GEO_WKT:
String spatialObjectText = source.getString();
spatialObject = Spatial.deserializeWKT(space, spatialObjectText);
break;
default:
assert false : indexMethod;
}
} catch (ParseException e) {
throw new InvalidSpatialObjectException();
}
}
}
private long[] zArray()
{
assert spatialObject != null;
int maxZ = spatialObject.maxZ();
if (zs == null || maxZ > zs.length) {
zs = new long[maxZ];
}
return zs;
}
private final Space space;
private final int dimensions;
private final int[] positions;
private final TInstance[] tinstances;
private final int firstSpatialField;
private final int lastSpatialField;
private SpatialObject spatialObject;
private long[] zs;
private final double[] coords;
private final Index.IndexMethod indexMethod;
// Inner classes
public static abstract class Operation
{
public abstract void handleZValue(long z);
}
}