blob: 327ac8f581f581ff18f8c443ad6d9f5982ca4c02 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial.spatial4j;
import org.apache.lucene.spatial3d.geom.GeoArea;
import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
import org.apache.lucene.spatial3d.geom.GeoAreaShape;
import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.LatLonBounds;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.SpatialRelation;
/**
* A Spatial4j Shape wrapping a {@link GeoAreaShape} ("Geo3D") -- a 3D planar geometry
* based Spatial4j Shape implementation.
* Geo3D implements shapes on the surface of a sphere or ellipsoid.
*
* @param <T> is the type of {@link GeoAreaShape}
* @lucene.experimental
*/
public class Geo3dShape<T extends GeoAreaShape> implements Shape {
protected final SpatialContext spatialcontext;
protected T shape;
protected volatile Rectangle boundingBox = null; // lazy initialized
protected volatile Point center = null; // lazy initialized
public Geo3dShape(final T shape, final SpatialContext spatialcontext) {
this.spatialcontext = spatialcontext;
this.shape = shape;
}
@Override
public SpatialRelation relate(Shape other) {
int relationship;
if (other instanceof Geo3dShape<?>) {
relationship = relate((Geo3dShape<?>) other);
} else if (other instanceof Rectangle) {
relationship = relate((Rectangle) other);
} else if (other instanceof Point) {
relationship = relate((Point) other);
} else {
throw new RuntimeException("Unimplemented shape relationship determination: " + other.getClass());
}
switch (relationship) {
case GeoArea.DISJOINT:
return SpatialRelation.DISJOINT;
case GeoArea.OVERLAPS:
return (other instanceof Point ? SpatialRelation.CONTAINS : SpatialRelation.INTERSECTS);
case GeoArea.CONTAINS:
return (other instanceof Point ? SpatialRelation.CONTAINS : SpatialRelation.WITHIN);
case GeoArea.WITHIN:
return SpatialRelation.CONTAINS;
}
throw new RuntimeException("Undetermined shape relationship: " + relationship);
}
private int relate(Geo3dShape<?> s) {
return shape.getRelationship(s.shape);
}
private int relate(Rectangle r) {
// Construct the right kind of GeoArea first
GeoArea geoArea = GeoAreaFactory.makeGeoArea(shape.getPlanetModel(),
r.getMaxY() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMinY() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMinX() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMaxX() * DistanceUtils.DEGREES_TO_RADIANS);
return geoArea.getRelationship(shape);
}
private int relate(Point p) {
GeoPoint point = new GeoPoint(shape.getPlanetModel(),
p.getY() * DistanceUtils.DEGREES_TO_RADIANS,
p.getX() * DistanceUtils.DEGREES_TO_RADIANS);
if (shape.isWithin(point)) {
return GeoArea.WITHIN;
}
return GeoArea.DISJOINT;
}
@Override
public Rectangle getBoundingBox() {
Rectangle bbox = this.boundingBox;//volatile read once
if (bbox == null) {
LatLonBounds bounds = new LatLonBounds();
shape.getBounds(bounds);
GeoBBox geoBBox = GeoBBoxFactory.makeGeoBBox(shape.getPlanetModel(), bounds);
bbox = new Geo3dRectangleShape(geoBBox, spatialcontext);
this.boundingBox = bbox;
}
return bbox;
}
@Override
public boolean hasArea() {
return true;
}
@Override
public double getArea(SpatialContext spatialContext) {
throw new UnsupportedOperationException();
}
@Override
public Point getCenter() {
Point center = this.center;//volatile read once
if (center == null) {
center = getBoundingBox().getCenter();
this.center = center;
}
return center;
}
@Override
public Shape getBuffered(double distance, SpatialContext spatialContext) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public SpatialContext getContext() {
return spatialcontext;
}
@Override
public String toString() {
return "Geo3D:" + shape.toString();
} // note: the shape usually prints its planet model
@Override
public boolean equals(Object o) {
if (!(o instanceof Geo3dShape<?>))
return false;
final Geo3dShape<?> other = (Geo3dShape<?>) o;
return (other.spatialcontext.equals(spatialcontext) && other.shape.equals(shape));
}
@Override
public int hashCode() {
return spatialcontext.hashCode() + shape.hashCode();
}
}