All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.locationtech.spatial4j.context.SpatialContext Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2015 Voyager Search and MITRE
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, Version 2.0 which
 * accompanies this distribution and is available at
 *    http://www.apache.org/licenses/LICENSE-2.0.txt
 ******************************************************************************/

package org.locationtech.spatial4j.context;

import org.locationtech.spatial4j.distance.CartesianDistCalc;
import org.locationtech.spatial4j.distance.DistanceCalculator;
import org.locationtech.spatial4j.distance.GeodesicSphereDistCalc;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.io.BinaryCodec;
import org.locationtech.spatial4j.io.LegacyShapeWriter;
import org.locationtech.spatial4j.io.SupportedFormats;
import org.locationtech.spatial4j.io.WKTReader;
import org.locationtech.spatial4j.shape.*;
import org.locationtech.spatial4j.shape.impl.RectangleImpl;

import java.text.ParseException;
import java.util.List;

/**
 * This is a facade to most of Spatial4j, holding things like {@link DistanceCalculator},
 * {@link ShapeFactory},
 * {@link org.locationtech.spatial4j.io.ShapeIO}.
 * 

* If you want a typical geodetic context, just reference {@link #GEO}. Otherwise, * You should either create and configure a {@link SpatialContextFactory} and then call * {@link SpatialContextFactory#newSpatialContext()}, OR, call * {@link org.locationtech.spatial4j.context.SpatialContextFactory#makeSpatialContext(java.util.Map, ClassLoader)} * to do this via configuration data. *

* Thread-safe & immutable. */ public class SpatialContext { /** A popular default SpatialContext implementation for geospatial. */ public static final SpatialContext GEO = new SpatialContext(new SpatialContextFactory()); //These are non-null private final boolean geo; private final ShapeFactory shapeFactory; private final DistanceCalculator calculator; private final Rectangle worldBounds; private final BinaryCodec binaryCodec; private final SupportedFormats formats; /** * Consider using {@link org.locationtech.spatial4j.context.SpatialContextFactory} instead. * * @param geo Establishes geo vs cartesian / Euclidean. * @param calculator Optional; defaults to haversine or cartesian depending on {@code geo}. * @param worldBounds Optional; defaults to GEO_WORLDBOUNDS or MAX_WORLDBOUNDS depending on units. */ @Deprecated public SpatialContext(boolean geo, DistanceCalculator calculator, Rectangle worldBounds) { this(initFromLegacyConstructor(geo, calculator, worldBounds)); } private static SpatialContextFactory initFromLegacyConstructor(boolean geo, DistanceCalculator calculator, Rectangle worldBounds) { SpatialContextFactory factory = new SpatialContextFactory(); factory.geo = geo; factory.distCalc = calculator; factory.worldBounds = worldBounds; return factory; } @Deprecated public SpatialContext(boolean geo) { this(initFromLegacyConstructor(geo, null, null)); } /** * Called by {@link org.locationtech.spatial4j.context.SpatialContextFactory#newSpatialContext()}. */ public SpatialContext(SpatialContextFactory factory) { this.geo = factory.geo; this.shapeFactory = factory.makeShapeFactory(this); if (factory.distCalc == null) { this.calculator = isGeo() ? new GeodesicSphereDistCalc.Haversine() : new CartesianDistCalc(); } else { this.calculator = factory.distCalc; } //TODO remove worldBounds from Spatial4j: see Issue #55 Rectangle bounds = factory.worldBounds; if (bounds == null) { this.worldBounds = isGeo() ? new RectangleImpl(-180, 180, -90, 90, this) : new RectangleImpl(-Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE, Double.MAX_VALUE, this); } else { if (isGeo() && !bounds.equals(new RectangleImpl(-180, 180, -90, 90, this))) throw new IllegalArgumentException("for geo (lat/lon), bounds must be " + GEO.getWorldBounds()); if (bounds.getMinX() > bounds.getMaxX()) throw new IllegalArgumentException("worldBounds minX should be <= maxX: "+ bounds); if (bounds.getMinY() > bounds.getMaxY()) throw new IllegalArgumentException("worldBounds minY should be <= maxY: "+ bounds); //hopefully worldBounds' rect implementation is compatible this.worldBounds = new RectangleImpl(bounds, this); } this.binaryCodec = factory.makeBinaryCodec(this); factory.checkDefaultFormats(); this.formats = factory.makeFormats(this); } /** A factory for {@link Shape}s. */ public ShapeFactory getShapeFactory() { return shapeFactory; } public SupportedFormats getFormats() { return formats; } public DistanceCalculator getDistCalc() { return calculator; } /** Convenience that uses {@link #getDistCalc()} */ public double calcDistance(Point p, double x2, double y2) { return getDistCalc().distance(p, x2, y2); } /** Convenience that uses {@link #getDistCalc()} */ public double calcDistance(Point p, Point p2) { return getDistCalc().distance(p, p2); } /** * The extent of x & y coordinates should fit within the return'ed rectangle. * Do *NOT* invoke reset() on this return type. */ public Rectangle getWorldBounds() { return worldBounds; } /** If true then {@link #normX(double)} will wrap longitudes outside of the standard * geodetic boundary into it. Example: 181 will become -179. */ @Deprecated public boolean isNormWrapLongitude() { return shapeFactory.isNormWrapLongitude(); } /** Is the mathematical world model based on a sphere, or is it a flat plane? The word * "geodetic" or "geodesic" is sometimes used to refer to the former, and the latter is sometimes * referred to as "Euclidean" or "cartesian". */ public boolean isGeo() { return geo; } /** Normalize the 'x' dimension. Might reduce precision or wrap it to be within the bounds. This * is called by {@link org.locationtech.spatial4j.io.WKTReader} before creating a shape. */ @Deprecated public double normX(double x) { return shapeFactory.normX(x); } /** Normalize the 'y' dimension. Might reduce precision or wrap it to be within the bounds. This * is called by {@link org.locationtech.spatial4j.io.WKTReader} before creating a shape. */ @Deprecated public double normY(double y) { return shapeFactory.normY(y); } /** Ensure fits in {@link #getWorldBounds()}. It's called by any shape factory method that * gets an 'x' dimension. */ @Deprecated public void verifyX(double x) { shapeFactory.verifyX(x); } /** Ensure fits in {@link #getWorldBounds()}. It's called by any shape factory method that * gets a 'y' dimension. */ @Deprecated public void verifyY(double y) { shapeFactory.verifyY(y); } /** Construct a point. */ @Deprecated public Point makePoint(double x, double y) { return shapeFactory.pointXY(x, y); } /** Construct a rectangle. */ @Deprecated public Rectangle makeRectangle(Point lowerLeft, Point upperRight) { return shapeFactory.rect(lowerLeft, upperRight); } /** * Construct a rectangle. If just one longitude is on the dateline (+/- 180) (aka anti-meridian) * then potentially adjust its sign to ensure the rectangle does not cross the * dateline. */ @Deprecated public Rectangle makeRectangle(double minX, double maxX, double minY, double maxY) { return shapeFactory.rect(minX, maxX, minY, maxY); } /** Construct a circle. The units of "distance" should be the same as x & y. */ @Deprecated public Circle makeCircle(double x, double y, double distance) { return shapeFactory.circle(x, y, distance); } /** Construct a circle. The units of "distance" should be the same as x & y. */ @Deprecated public Circle makeCircle(Point point, double distance) { return shapeFactory.circle(point, distance); } /** Constructs a line string. It's an ordered sequence of connected vertexes. There * is no official shape/interface for it yet so we just return Shape. */ @Deprecated public Shape makeLineString(List points) { return shapeFactory.lineString(points, 0); } /** Constructs a buffered line string. It's an ordered sequence of connected vertexes, * with a buffer distance along the line in all directions. There * is no official shape/interface for it so we just return Shape. */ @Deprecated public Shape makeBufferedLineString(List points, double buf) { return shapeFactory.lineString(points, buf); } /** Construct a ShapeCollection, analogous to an OGC GeometryCollection. */ @Deprecated public ShapeCollection makeCollection(List coll) { return shapeFactory.multiShape(coll); } /** The {@link org.locationtech.spatial4j.io.WKTReader} used by {@link #readShapeFromWkt(String)}. */ @Deprecated public WKTReader getWktShapeParser() { return (WKTReader)formats.getWktReader(); } /** Reads a shape from the string formatted in WKT. * @see org.locationtech.spatial4j.io.WKTReader * @param wkt non-null WKT. * @return non-null * @throws ParseException if it failed to parse. */ @Deprecated public Shape readShapeFromWkt(String wkt) throws ParseException, InvalidShapeException { return getWktShapeParser().parse(wkt); } public BinaryCodec getBinaryCodec() { return binaryCodec; } /** * Try to read a shape from any supported formats * * @param value * @return shape or null if unable to parse any shape * @throws InvalidShapeException */ @Deprecated public Shape readShape(String value) throws InvalidShapeException { return formats.read(value); } /** Writes the shape to a String using the old/deprecated * {@link org.locationtech.spatial4j.io.LegacyShapeWriter}. The JTS based subclass will write it * to WKT if the legacy format doesn't support that shape. * Spatial4j in the near future won't support writing shapes to strings. * @param shape non-null * @return non-null */ @Deprecated public String toString(Shape shape) { return LegacyShapeWriter.writeShape(shape); } @Override public String toString() { if (this.equals(GEO)) { return GEO.getClass().getSimpleName()+".GEO"; } else { return getClass().getSimpleName()+"{" + "geo=" + geo + ", calculator=" + calculator + ", worldBounds=" + worldBounds + '}'; } } }