/*
* 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.lucene;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.measure.UnitConverter;
import javax.measure.Unit;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.MMapDirectory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.geotoolkit.filter.SpatialFilterType;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.geometry.GeneralEnvelope;
import org.geotoolkit.geometry.jts.SRIDGenerator;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.CRS;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.geometry.jts.JTS;
import org.geotoolkit.index.tree.manager.NamedEnvelope;
import org.opengis.filter.Filter;
import org.opengis.filter.spatial.*;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.apache.sis.referencing.CommonCRS;
/**
*
* @author Guilhem Legal
*/
public class LuceneUtils {
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.lucene");
public static final GeometryFactory GF = new GeometryFactory();
public static Directory getAppropriateDirectory(final Path indexDirectory) throws IOException {
// for windows
if (System.getProperty("os.name", "").startsWith("Windows")) {
return new SimpleFSDirectory(indexDirectory);
// for unix
} else {
final String archModel = System.getProperty("sun.arch.data.model");
LOGGER.log(Level.FINER, "archmodel:{0}", archModel);
if ("64".equals(archModel)) {
return new MMapDirectory(indexDirectory);
} else {
return new NIOFSDirectory(indexDirectory);
}
}
}
public static GeneralEnvelope getExtendedReprojectedEnvelope(final Object geom, final CoordinateReferenceSystem treeCrs, final String strUnit, final double distance) throws FactoryException {
GeneralEnvelope bound = getReprojectedEnvelope(geom, treeCrs);
// add the reprojected distance
if (bound != null) {
final Unit unit = Units.valueOf(strUnit);
final Unit crsUnit = treeCrs.getCoordinateSystem().getAxis(0).getUnit();
final CoordinateReferenceSystem crs;
final boolean reproj;
final GeneralEnvelope e;
if (unit.isCompatible(crsUnit)) {
crs = treeCrs;
e = bound;
reproj = false;
} else {
if (Units.isLinear(unit)) {
crs = CRS.forCode("EPSG:3857");
} else {
crs = CommonCRS.defaultGeographic();
}
e = getReprojectedEnvelope(bound, crs);
reproj = true;
}
final UnitConverter converter = unit.getConverterTo(crs.getCoordinateSystem().getAxis(0).getUnit());
final double rdistance = converter.convert(distance);
final double minx = e.getLower(0) - rdistance;
final double miny = e.getLower(1) - rdistance;
final double maxx = e.getUpper(0) + rdistance;
final double maxy = e.getUpper(1) + rdistance;
e.setRange(0, minx, maxx);
e.setRange(1, miny, maxy);
if (reproj) {
bound = getReprojectedEnvelope(e, treeCrs);
}
}
LOGGER.log(Level.FINER, "OBTAINED REPROJECTED ENV:{0}", bound);
return bound;
}
public static GeneralEnvelope getReprojectedEnvelope(final Object geom, final CoordinateReferenceSystem treeCrs) {
if (geom instanceof Geometry) {
return getReprojectedEnvelope((Geometry) geom, treeCrs);
} else if (geom instanceof org.opengis.geometry.Envelope) {
return getReprojectedEnvelope((org.opengis.geometry.Envelope) geom, treeCrs);
}
LOGGER.log(Level.WARNING, "Not a geometry for literal:{0} (class: {1})", new Object[]{geom, geom.getClass().getName()});
return null;
}
/**
* Extract the internal envelope from the geometry and reprojected it to the treeCRS.
*
* @param geom
* @param treeCrs
* @return
*/
private static GeneralEnvelope getReprojectedEnvelope(final Geometry geom, final CoordinateReferenceSystem treeCrs) {
final Envelope jtsBound = geom.getEnvelopeInternal();
final String epsgCode = SRIDGenerator.toSRS(geom.getSRID(), SRIDGenerator.Version.V1);
try {
final CoordinateReferenceSystem geomCRS = CRS.forCode(epsgCode);
final GeneralEnvelope bound = new GeneralEnvelope(geomCRS);
bound.setRange(0, jtsBound.getMinX(), jtsBound.getMaxX());
bound.setRange(1, jtsBound.getMinY(), jtsBound.getMaxY());
// reproject to cartesian CRS
return (GeneralEnvelope) Envelopes.transform(bound, treeCrs);
} catch (FactoryException ex) {
LOGGER.log(Level.WARNING, "Factory exception while getting filter geometry crs", ex);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, "Transform exception while reprojecting filter geometry", ex);
}
return null;
}
/**
* Reproject the envelope in the tree CRS.
* @param env
* @param treeCrs
* @return
*/
private static GeneralEnvelope getReprojectedEnvelope(final org.opengis.geometry.Envelope env, final CoordinateReferenceSystem treeCrs) {
try {
return (GeneralEnvelope) Envelopes.transform(env, treeCrs);
} catch (TransformException ex) {
LOGGER.log(Level.WARNING, "Transform exception while reprojecting filter geometry", ex);
}
return null;
}
public static SpatialFilterType getSpatialFilterType(final Filter filter) {
if (filter instanceof BBOX) {
return SpatialFilterType.BBOX;
} else if (filter instanceof Beyond){
return SpatialFilterType.BEYOND;
} else if (filter instanceof Contains){
return SpatialFilterType.CONTAINS;
} else if (filter instanceof Crosses){
return SpatialFilterType.CROSSES;
} else if (filter instanceof Disjoint){
return SpatialFilterType.DISJOINT;
} else if (filter instanceof DWithin){
return SpatialFilterType.DWITHIN;
} else if (filter instanceof Equals){
return SpatialFilterType.EQUALS;
} else if (filter instanceof Intersects){
return SpatialFilterType.INTERSECTS;
} else if (filter instanceof Overlaps){
return SpatialFilterType.OVERLAPS;
} else if (filter instanceof Touches){
return SpatialFilterType.TOUCHES;
} else if (filter instanceof Within){
return SpatialFilterType.WITHIN;
}
throw new IllegalArgumentException("unexpected filter type:" + filter);
}
public static NamedEnvelope getNamedEnvelope(final String id, final Geometry geom, final CoordinateReferenceSystem crs) throws FactoryException, TransformException {
final com.vividsolutions.jts.geom.Envelope jtsBound = geom.getEnvelopeInternal();
final String epsgCode = SRIDGenerator.toSRS(geom.getSRID(), SRIDGenerator.Version.V1);
final CoordinateReferenceSystem geomCRS = CRS.forCode(epsgCode);
final GeneralEnvelope bound = new GeneralEnvelope(geomCRS);
bound.setRange(0, jtsBound.getMinX(), jtsBound.getMaxX());
bound.setRange(1, jtsBound.getMinY(), jtsBound.getMaxY());
// reproject to specified CRS
return new NamedEnvelope(Envelopes.transform(bound, crs), id);
}
public static Polygon getPolygon(final org.opengis.geometry.Envelope env){
return getPolygon(env.getMinimum(0), env.getMaximum(0), env.getMinimum(1), env.getMaximum(1), env.getCoordinateReferenceSystem());
}
/**
* Return a JTS polygon from bounding box coordinate.
*
* @param minx minimal X coordinate.
* @param maxx maximal X coordinate.
* @param miny minimal Y coordinate.
* @param maxy maximal Y coordinate.
* @param crs coordinate spatial reference.
*/
public static Polygon getPolygon(final double minx, final double maxx, final double miny, final double maxy, final CoordinateReferenceSystem crs){
final Coordinate[] crds = new Coordinate[]{
new Coordinate(0, 0),
new Coordinate(0, 0),
new Coordinate(0, 0),
new Coordinate(0, 0),
new Coordinate(0, 0)};
final CoordinateSequence pts = new CoordinateArraySequence(crds);
final LinearRing rg = new LinearRing(pts, GF);
final Polygon poly = new Polygon(rg, new LinearRing[0],GF);
crds[0].x = minx;
crds[0].y = miny;
crds[1].x = minx;
crds[1].y = maxy;
crds[2].x = maxx;
crds[2].y = maxy;
crds[3].x = maxx;
crds[3].y = miny;
crds[4].x = minx;
crds[4].y = miny;
JTS.setCRS(poly, crs);
return poly;
}
public static Polygon[] getPolygons(final List<Double> minx, final List<Double> maxx, final List<Double> miny, final List<Double> maxy, final CoordinateReferenceSystem crs) {
final List<Polygon> polygonList = new ArrayList<>();
for (int i = 0; i < minx.size(); i++) {
if (Double.isNaN(minx.get(i)) || Double.isNaN(maxx.get(i)) || Double.isNaN(miny.get(i)) || Double.isNaN(maxy.get(i))) {
LOGGER.info("skip NaN envelope");
} else {
polygonList.add(LuceneUtils.getPolygon(minx.get(i), maxx.get(i), miny.get(i), maxy.get(i), crs));
}
}
return polygonList.toArray(new Polygon[polygonList.size()]);
}
}