├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGES.md ├── CONTRIBUTING.md ├── FORMATS.md ├── README.md ├── about.md ├── asl-v20.txt ├── devnotes.md ├── eclipse ├── notice.md ├── pom.xml └── src ├── main └── java │ ├── org │ └── locationtech │ │ └── spatial4j │ │ ├── SpatialPredicate.java │ │ ├── context │ │ ├── SpatialContext.java │ │ ├── SpatialContextFactory.java │ │ ├── jts │ │ │ ├── DatelineRule.java │ │ │ ├── JtsSpatialContext.java │ │ │ ├── JtsSpatialContextFactory.java │ │ │ └── ValidationRule.java │ │ └── package-info.java │ │ ├── distance │ │ ├── AbstractDistanceCalculator.java │ │ ├── CartesianDistCalc.java │ │ ├── DistanceCalculator.java │ │ ├── DistanceUtils.java │ │ ├── GeodesicSphereDistCalc.java │ │ └── package-info.java │ │ ├── exception │ │ ├── InvalidShapeException.java │ │ └── UnsupportedSpatialPredicate.java │ │ ├── io │ │ ├── BinaryCodec.java │ │ ├── GeoJSONReader.java │ │ ├── GeoJSONWriter.java │ │ ├── GeohashUtils.java │ │ ├── LegacyShapeReader.java │ │ ├── LegacyShapeWriter.java │ │ ├── OnePointsBuilder.java │ │ ├── ParseUtils.java │ │ ├── PolyshapeReader.java │ │ ├── PolyshapeWriter.java │ │ ├── ShapeIO.java │ │ ├── ShapeReader.java │ │ ├── ShapeWriter.java │ │ ├── SupportedFormats.java │ │ ├── WKTReader.java │ │ ├── WKTWriter.java │ │ ├── WktShapeParser.java │ │ ├── jackson │ │ │ ├── GeometryAsGeoJSONSerializer.java │ │ │ ├── GeometryAsWKTSerializer.java │ │ │ ├── GeometryDeserializer.java │ │ │ ├── PackageVersion.java │ │ │ ├── PackageVersion.java.in │ │ │ ├── ShapeAsGeoJSONSerializer.java │ │ │ ├── ShapeAsWKTSerializer.java │ │ │ ├── ShapeDeserializer.java │ │ │ ├── ShapesAsGeoJSONModule.java │ │ │ ├── ShapesAsWKTModule.java │ │ │ └── package-info.java │ │ ├── jts │ │ │ ├── JtsBinaryCodec.java │ │ │ ├── JtsGeoJSONWriter.java │ │ │ ├── JtsPolyshapeWriter.java │ │ │ ├── JtsWKTReaderShapeParser.java │ │ │ └── JtsWKTWriter.java │ │ └── package-info.java │ │ ├── package-info.java │ │ └── shape │ │ ├── BaseShape.java │ │ ├── Circle.java │ │ ├── Point.java │ │ ├── Rectangle.java │ │ ├── Shape.java │ │ ├── ShapeCollection.java │ │ ├── ShapeFactory.java │ │ ├── SpatialRelation.java │ │ ├── impl │ │ ├── BBoxCalculator.java │ │ ├── BufferedLine.java │ │ ├── BufferedLineString.java │ │ ├── CircleImpl.java │ │ ├── GeoCircle.java │ │ ├── InfBufLine.java │ │ ├── PointImpl.java │ │ ├── Range.java │ │ ├── RectangleImpl.java │ │ └── ShapeFactoryImpl.java │ │ ├── jts │ │ ├── JtsGeometry.java │ │ ├── JtsPoint.java │ │ └── JtsShapeFactory.java │ │ └── package-info.java │ └── overview.html └── test ├── java └── org │ └── locationtech │ └── spatial4j │ ├── TestLog.java │ ├── context │ ├── SpatialContextFactoryTest.java │ └── jts │ │ └── JtsSpatialContextTest.java │ ├── distance │ ├── CompareRadiansSnippet.java │ └── TestDistances.java │ ├── io │ ├── BaseRoundTripTest.java │ ├── BinaryCodecTest.java │ ├── GeneralGeoJSONTest.java │ ├── GeneralPolyshapeTest.java │ ├── GeneralReadWriteShapeTest.java │ ├── GeneralWktTest.java │ ├── JtsBinaryCodecTest.java │ ├── JtsPolyshapeParserTest.java │ ├── JtsWKTReaderShapeParserTest.java │ ├── JtsWktShapeParserTest.java │ ├── LegacyShapeReadWriterTest.java │ ├── ShapeFormatTest.java │ ├── TestGeohashUtils.java │ ├── WKTWriterTest.java │ ├── WktCustomShapeParserTest.java │ ├── WktShapeParserTest.java │ ├── benchmark │ │ └── ShapeBenchmarks.java │ └── jackson │ │ ├── JacksonGeoJSONReaderTest.java │ │ ├── JacksonGeoJSONWriterTest.java │ │ ├── JacksonShapeReader.java │ │ ├── JacksonShapeWriter.java │ │ ├── ObjectWithGeometry.java │ │ └── SimpleJacksonTest.java │ ├── shape │ ├── AbstractTestShapes.java │ ├── BufferedLineStringTest.java │ ├── BufferedLineTest.java │ ├── JtsGeometryTest.java │ ├── RandomizedShapeTest.java │ ├── RectIntersectionTestHelper.java │ ├── RoundingDistCalc.java │ ├── ShapeCollectionTest.java │ ├── TestShapes2D.java │ ├── TestShapesGeo.java │ ├── impl │ │ └── BBoxCalculatorTest.java │ └── jts │ │ └── JtsShapeFactoryTest.java │ └── util │ ├── Geom.java │ └── GeomBuilder.java └── resources ├── fiji.wkt.txt ├── russia.wkt.txt └── samples.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ main, master ] 9 | pull_request: 10 | branches: [ main, master ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | java: [ '8', '11' , '17', '21' ] 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Set up JDK ${{ matrix.java }} 23 | uses: actions/setup-java@v4 24 | with: 25 | java-version: ${{ matrix.java }} 26 | distribution: 'temurin' 27 | cache: maven 28 | - name: Build with Maven 29 | run: mvn --batch-mode --update-snapshots verify 30 | 31 | - name: Publish Test Results 32 | if: always() 33 | uses: EnricoMi/publish-unit-test-result-action@v2 34 | with: 35 | files: target/surefire-reports/*.xml 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.ipr 2 | /.idea/ 3 | *.iml 4 | target/ 5 | 6 | .classpath 7 | .project 8 | .settings/ 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Spatial4j 2 | 3 | The Spatial4j project is always excited to accept contributions from the community. This document 4 | contains some guidelines to help users and developers contribute to the project. 5 | 6 | - [Code Style](#code) 7 | - [Issues and Bugs](#bugs) 8 | - [Discussion Forum](#discuss) 9 | - [Submitting Patches](#patches) 10 | 11 | ## Code Style 12 | 13 | Spatial4j adheres to (as much as possible) the 14 | [Google Java Style](https://google.github.io/styleguide/javaguide.html) conventions. If a patch 15 | or commit deviates from these guidelines a reviewer will likely ask for it to be reformatted. 16 | 17 | ## Issues, Bugs, and Feature Requests 18 | 19 | Spatial4j utilizes Github for issue tracking. Bugs, issues, and feature requests should be 20 | filed [here](https://github.com/locationtech/spatial4j/issues). 21 | 22 | ## Discussion Forum 23 | 24 | Often communication can be carried out through comments on an issue or pull request directly but 25 | for larger discussions that are more general in nature it is recommended that the project 26 | [mailing list](https://locationtech.org/mailman/listinfo/spatial4j-dev) be used. 27 | 28 | ## Submitting Patches 29 | 30 | The best way to submit a patch or add a new feature to the code is to submit a [ 31 | pull request](https://help.github.com/articles/using-pull-requests/). Below are some guidelines to 32 | follow when developing code intended to be submitted via pull request. 33 | 34 | This [guide](http://people.redhat.com/rjones/how-to-supply-code-to-open-source-projects/) contains 35 | some useful guidelines for contributing to open source projects in general. Below are some additionally 36 | stressed points. 37 | 38 | ### Send email first 39 | 40 | It is never a bad idea to email the mailing list with thoughts about a change you intend to make 41 | before you make it. This allows the committers to weigh in with thoughts and suggestions that will 42 | help you make the change and ultimately ensure your successful contribution to the project. 43 | 44 | ### Filing an ECA 45 | 46 | Contributors must electronically sign the Eclipse Contributor Agreement (ECA). 47 | This is a one-time event, and is quick & easy. 48 | 49 | * http://www.eclipse.org/legal/ECA.php 50 | 51 | For more information, please see the Eclipse Committer Handbook: 52 | https://www.eclipse.org/projects/handbook/#resources-commit 53 | 54 | ### One patch per one bug/feature 55 | 56 | Avoid submitting patches that mix together multiple features and/or bug fixes into a single changeset. 57 | It is much easier to review and understand a patch that is dedicated to a single purpose. 58 | 59 | ### No cruft 60 | 61 | While working on a patch often developers can't resist the urge to reformat code that is unrelated 62 | to the patch. This adds unnecessary "noise" that makes the job of the reviewer more difficult. It 63 | also makes the history of a change harder to analyze after the fact. If a patch contains unnecessary 64 | whitespace or other formatting changes a reviewer will ask for them to be removed. 65 | 66 | ### Viewing the entire project history 67 | 68 | (Optional) We recommended that developers do this step to be able to view the 69 | pre-LocationTech history of the project. This can be achieved with the following 70 | command after the repository has been cloned: 71 | 72 | git fetch origin refs/replace/*:refs/replace/* 73 | 74 | -------------------------------------------------------------------------------- /about.md: -------------------------------------------------------------------------------- 1 | ## About This Content 2 | 3 | May 22, 2015 4 | 5 | ### License 6 | 7 | The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the 8 | Content is provided to you under the terms and conditions of the Apache License, Version 2.0. A copy of the Apache 9 | License, Version 2.0 is available at 10 | [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) 11 | 12 | If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another 13 | party ("Redistributor") and different terms and conditions may apply to your use of any object code in the Content. 14 | Check the Redistributor’s license that was provided with the Content. If no such license exists, contact the 15 | Redistributor. Unless otherwise indicated below, the terms and conditions of the Apache License, Version 2.0 still apply 16 | to any source code in the Content and such source code may be obtained at 17 | [http://www.eclipse.org](http://www.eclipse.org). 18 | -------------------------------------------------------------------------------- /devnotes.md: -------------------------------------------------------------------------------- 1 | This file has notes for committers. 2 | 3 | # Making a snapshot release 4 | 5 | Note: depends on having access to the Sonatype repo described further below 6 | 7 | mvn deploy -Prelease 8 | 9 | # Making a new release 10 | 11 | First, understand that LocationTech projects undergo releases using an official process described here: 12 | https://www.eclipse.org/projects/handbook/#release with complete and thorough details here: 13 | https://wiki.eclipse.org/Development_Resources/HOWTO/Release_Reviews 14 | 15 | Note: 16 | * See https://projects.eclipse.org/projects/locationtech.spatial4j and "Committer Tools" panel at right, including 17 | * "Create a new release" 18 | * "Generate IP Log" 19 | * References to the "PMC" (Project Management Committee) in Spatial4j's case is the 20 | LocationTech Technology PMC. There aren't 21 | project-specific PMCs. 22 | 23 | *TODO distill the process here.* 24 | 25 | Those steps can be concurrent with following some of the earlier technical steps below. Deploying/releasing any 26 | jars must wait until the release date assuming the release review is successful. 27 | 28 | ## Review files... 29 | 30 | * Review CHANGES.md — up to date? 31 | 32 | * Review README.md — up to date? 33 | 34 | * Review pom — up to date? Run display-plugin-updates & display-dependency-updates. Do *not* remove the SNAPSHOT; 35 | that'll be handled later. 36 | 37 | 38 | ## Build, Tag, and Deploy to Sonatype 39 | 40 | Optional: create a release branch if there will be release-specific changes. Probably not. 41 | 42 | **Prepare** the release: https://maven.apache.org/maven-release/maven-release-plugin/examples/prepare-release.html 43 | 44 | ``` 45 | mvn release:prepare 46 | ``` 47 | 48 | This will create a tag in git named spatial4j-0.6 (or whatever the version is) with the pom updated. 49 | If something goes wrong, you'll have to do `release:rollback` and then possibly remove the tag (pushing to GitHub) 50 | so that next time it can succeed. 51 | 52 | Some "release.properties" file and "pom.xml.releaseBackup" files will be produced. They will be removed later at the `clean` phase 53 | 54 | Note that org.locationtech.spatial4j.io.jackson.PackageVersion includes a hard-coded version. 55 | It's overwritten in the build process. You should manually update it to the next snapshot release and commit. 56 | It's okay that the Maven release plugin, when setting the final version, did so only in the POM but not this 57 | souce code file because the jars that get created include the modified file (both compiled and source). 58 | 59 | **Perform** the release: https://maven.apache.org/maven-release/maven-release-plugin/examples/perform-release.html 60 | 61 | mvn release:perform 62 | 63 | This should build, GPG sign, and deploy artifacts to Sonatype. 64 | When I last edited these instructions, there was previously a different process. So... something will probably go wrong. 65 | It is intentional that there remains a manual step further below at Sonatype to send the binaries to Maven Central. 66 | Further info: https://central.sonatype.org/pages/apache-maven.html#performing-a-release-deployment-with-the-maven-release-plugin 67 | 68 | **Clean** 69 | 70 | mvn release:clean 71 | 72 | 73 | ## Release deployed artifacts to Maven Central 74 | 75 | http://central.sonatype.org/pages/releasing-the-deployment.html 76 | 77 | ## Publish the Maven site (includes Javadoc) 78 | 79 | We publish the Maven "site" HTML on GitHub, and we link to it from the readme and others might too. The site 80 | includes the javadoc API. 81 | 82 | Instructions: 83 | http://blog.progs.be/517/publishing-javadoc-to-github-using-maven 84 | 85 | Summary: 86 | 87 | First checkout the release tag (e.g. spatial4j-0.5) or modify pom.xml temporarily to have this version. The site 88 | reports reference the version, so this is why. 89 | 90 | mvn clean site 91 | mvn scm-publish:publish-scm 92 | 93 | When site completes, open the target/site/index.html to view it to see if it's reasonable. Then continue to the publish 94 | step. The publish step will require your username & password for GitHub. Observe the final published content online: 95 | 96 | https://locationtech.github.io/spatial4j/ 97 | 98 | ## GitHub Release 99 | 100 | On the project's Git based homepage, navigate to the "Tags": https://github.com/locationtech/spatial4j/tags 101 | 102 | Find the tag for 0.8 (or whatever the version is), click the "..." menu next to it, and choose "Create a Release". 103 | 104 | Put a brief characterization of the release at the top, and then paste in the content from CHANGES.md. Add the release binaries as well (all JAR files). 105 | 106 | ## Update the Eclipse CMS 107 | 108 | At the Eclipse project site for Spatial4j, hit the Edit button: 109 | https://projects.eclipse.org/projects/locationtech.spatial4j 110 | Then on the "Download" section, update the version. 111 | 112 | ## Send announcement 113 | -------------------------------------------------------------------------------- /eclipse: -------------------------------------------------------------------------------- 1 | rm */.classpath 2 | rm */.project 3 | rm */*/.classpath 4 | rm */*/.project 5 | rm */*/*/.classpath 6 | rm */*/*/.project 7 | rm */*/*/*/.classpath 8 | rm */*/*/*/.project 9 | mvn eclipse:eclipse -P updateLucene 10 | 11 | -------------------------------------------------------------------------------- /notice.md: -------------------------------------------------------------------------------- 1 | # Notices for Spatial4j 2 | 3 | This content is produced and maintained by the Eclipse Spatial4j project. 4 | 5 | * Project home: https://projects.eclipse.org/projects/locationtech.spatial4j 6 | 7 | ## Trademarks 8 | 9 | LocationTech Spatial4j, and Spatial4j are trademarks of the Eclipse Foundation. 10 | 11 | ## Declared Project Licenses 12 | 13 | This program and the accompanying materials are made available under the terms 14 | of the Apache License, Version 2.0 which is available at 15 | https://www.apache.org/licenses/LICENSE-2.0. 16 | 17 | SPDX-License-Identifier: Apache-2.0 18 | 19 | ## Source Code 20 | 21 | The project maintains the following source code repositories: 22 | 23 | * https://github.com/locationtech/spatial4j 24 | 25 | ## Third-party Content 26 | 27 | (none) 28 | 29 | ## Cryptography 30 | 31 | (none) -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/context/jts/DatelineRule.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.context.jts; 10 | 11 | /** 12 | * Indicates the algorithm used to process JTS Polygons and JTS LineStrings for detecting dateline 13 | * (aka anti-meridian) crossings. It only applies when geo=true. 14 | */ 15 | public enum DatelineRule { 16 | /** No polygon will cross the dateline. */ 17 | none, 18 | 19 | /** 20 | * Adjacent points with an x (longitude) difference that spans more than half way around the 21 | * globe will be interpreted as going the other (shorter) way, and thus cross the dateline. 22 | */ 23 | width180, // TODO is there a better name that doesn't have '180' in it? 24 | 25 | /** 26 | * For rectangular polygons, the point order is interpreted as being counter-clockwise (CCW). 27 | * However, non-rectangular polygons or other shapes aren't processed this way; they use the 28 | * {@link #width180} rule instead. The CCW rule is specified by OGC Simple Features 29 | * Specification v. 1.2.0 section 6.1.11.1. 30 | */ 31 | ccwRect 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/context/jts/JtsSpatialContextFactory.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.context.jts; 10 | 11 | import org.locationtech.spatial4j.context.SpatialContextFactory; 12 | import org.locationtech.spatial4j.io.GeoJSONReader; 13 | import org.locationtech.spatial4j.io.LegacyShapeReader; 14 | import org.locationtech.spatial4j.io.LegacyShapeWriter; 15 | import org.locationtech.spatial4j.io.PolyshapeReader; 16 | import org.locationtech.spatial4j.io.WKTReader; 17 | import org.locationtech.spatial4j.io.jts.*; 18 | import org.locationtech.spatial4j.shape.jts.JtsShapeFactory; 19 | import org.locationtech.jts.geom.CoordinateSequenceFactory; 20 | import org.locationtech.jts.geom.GeometryFactory; 21 | import org.locationtech.jts.geom.PrecisionModel; 22 | import org.locationtech.jts.geom.impl.CoordinateArraySequenceFactory; 23 | 24 | import java.util.Map; 25 | 26 | /** 27 | * See {@link SpatialContextFactory#makeSpatialContext(java.util.Map, ClassLoader)}. 28 | *
29 | * The following keys are looked up in the args map, in addition to those in the 30 | * superclass: 31 | *
45 | * The buffer operation is fairly insensitive to topological invalidity, and the act of 46 | * computing the buffer can often resolve minor issues such as self-intersecting rings. However, 47 | * in some situations the computed result may not be what is desired (i.e. the buffer operation 48 | * may be "confused" by certain topologies, and fail to produce a result which is close to the 49 | * original. An example where this can happen is a "bow-tie: or "figure-8" polygon, with one 50 | * very small lobe and one large one. Depending on the orientations of the lobes, the buffer(0) 51 | * operation may keep the small lobe and discard the "valid" large lobe). 52 | *
53 | */ 54 | repairBuffer0 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/context/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | /** SpatialContext implementations are the facade to the Spatial4j API. */ 10 | package org.locationtech.spatial4j.context; -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/distance/AbstractDistanceCalculator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 MITRE and VoyagerSearch 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.distance; 10 | 11 | import org.locationtech.spatial4j.shape.Point; 12 | 13 | /** 14 | */ 15 | public abstract class AbstractDistanceCalculator implements DistanceCalculator { 16 | 17 | @Override 18 | public double distance(Point from, Point to) { 19 | return distance(from, to.getX(), to.getY()); 20 | } 21 | 22 | @Override 23 | public boolean within(Point from, double toX, double toY, double distance) { 24 | return distance(from, toX, toY) <= distance; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return getClass().getSimpleName(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/distance/CartesianDistCalc.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.distance; 10 | 11 | import org.locationtech.spatial4j.context.SpatialContext; 12 | import org.locationtech.spatial4j.shape.Circle; 13 | import org.locationtech.spatial4j.shape.Point; 14 | import org.locationtech.spatial4j.shape.Rectangle; 15 | 16 | /** 17 | * Calculates based on Euclidean / Cartesian 2d plane. 18 | */ 19 | public class CartesianDistCalc extends AbstractDistanceCalculator { 20 | 21 | public static final CartesianDistCalc INSTANCE = new CartesianDistCalc(); 22 | public static final CartesianDistCalc INSTANCE_SQUARED = new CartesianDistCalc(true); 23 | 24 | private final boolean squared; 25 | 26 | public CartesianDistCalc() { 27 | this.squared = false; 28 | } 29 | 30 | /** 31 | * @param squared Set to true to have {@link #distance(org.locationtech.spatial4j.shape.Point, org.locationtech.spatial4j.shape.Point)} 32 | * return the square of the correct answer. This is a 33 | * performance optimization used when sorting in which the 34 | * actual distance doesn't matter so long as the sort order is 35 | * consistent. 36 | */ 37 | public CartesianDistCalc(boolean squared) { 38 | this.squared = squared; 39 | } 40 | 41 | @Override 42 | public double distance(Point from, double toX, double toY) { 43 | double xSquaredPlusYSquared = distanceSquared(from.getX(), from.getY(), toX, toY); 44 | if (squared) 45 | return xSquaredPlusYSquared; 46 | 47 | return Math.sqrt(xSquaredPlusYSquared); 48 | } 49 | 50 | private static double distanceSquared(double fromX, double fromY, double toX, double toY) { 51 | double deltaX = fromX - toX; 52 | double deltaY = fromY - toY; 53 | return deltaX*deltaX + deltaY*deltaY; 54 | } 55 | 56 | /** 57 | * Distance from point to a line segment formed between points 'v' and 'w'. 58 | * It respects the "squared" option. 59 | */ 60 | // TODO add to generic DistanceCalculator and develop geo versions. 61 | public double distanceToLineSegment(Point point, double vX, double vY, double wX, double wY) { 62 | // Translated from: http://bl.ocks.org/mbostock/4218871 63 | double d = distanceSquared(vX, vY, wX, wY); 64 | double toX; 65 | double toY; 66 | if (d <= 0) { 67 | toX = vX; 68 | toY = vY; 69 | } else { 70 | // t = ((point[0] - v[0]) * (w[0] - v[0]) + (point[1] - v[1]) * (w[1] - v[1])) / d 71 | double t = ((point.getX() - vX) * (wX - vX) + (point.getY() - vY) * (wY - vY)) / d; 72 | if (t < 0) { 73 | toX = vX; 74 | toY = vY; 75 | } else if (t > 1) { 76 | toX = wX; 77 | toY = wY; 78 | } else { 79 | toX = vX + t * (wX - vX); 80 | toY = vY + t * (wY - vY); 81 | } 82 | } 83 | return distance(point, toX, toY); 84 | } 85 | 86 | @Override 87 | public boolean within(Point from, double toX, double toY, double distance) { 88 | double deltaX = from.getX() - toX; 89 | double deltaY = from.getY() - toY; 90 | return deltaX*deltaX + deltaY*deltaY <= distance*distance; 91 | } 92 | 93 | @Override 94 | public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) { 95 | if (distDEG == 0) { 96 | if (reuse == null) 97 | return from; 98 | reuse.reset(from.getX(), from.getY()); 99 | return reuse; 100 | } 101 | double bearingRAD = DistanceUtils.toRadians(bearingDEG); 102 | double x = from.getX() + Math.sin(bearingRAD) * distDEG; 103 | double y = from.getY() + Math.cos(bearingRAD) * distDEG; 104 | if (reuse == null) { 105 | return ctx.makePoint(x, y); 106 | } else { 107 | reuse.reset(x, y); 108 | return reuse; 109 | } 110 | } 111 | 112 | @Override 113 | public Rectangle calcBoxByDistFromPt(Point from, double distDEG, SpatialContext ctx, Rectangle reuse) { 114 | double minX = from.getX() - distDEG; 115 | double maxX = from.getX() + distDEG; 116 | double minY = from.getY() - distDEG; 117 | double maxY = from.getY() + distDEG; 118 | if (reuse == null) { 119 | return ctx.makeRectangle(minX, maxX, minY, maxY); 120 | } else { 121 | reuse.reset(minX, maxX, minY, maxY); 122 | return reuse; 123 | } 124 | } 125 | 126 | @Override 127 | public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx) { 128 | return from.getY(); 129 | } 130 | 131 | @Override 132 | public double area(Rectangle rect) { 133 | return rect.getArea(null); 134 | } 135 | 136 | @Override 137 | public double area(Circle circle) { 138 | return circle.getArea(null); 139 | } 140 | 141 | @Override 142 | public boolean equals(Object o) { 143 | if (this == o) return true; 144 | if (o == null || getClass() != o.getClass()) return false; 145 | 146 | CartesianDistCalc that = (CartesianDistCalc) o; 147 | 148 | if (squared != that.squared) return false; 149 | 150 | return true; 151 | } 152 | 153 | @Override 154 | public int hashCode() { 155 | return (squared ? 1 : 0); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/distance/DistanceCalculator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.distance; 10 | 11 | import org.locationtech.spatial4j.context.SpatialContext; 12 | import org.locationtech.spatial4j.shape.Circle; 13 | import org.locationtech.spatial4j.shape.Point; 14 | import org.locationtech.spatial4j.shape.Rectangle; 15 | 16 | /** 17 | * Performs calculations relating to distance, such as the distance between a pair of points. A 18 | * calculator might be based on Euclidean space, or a spherical model, or theoretically something 19 | * else like an ellipsoid. 20 | */ 21 | public interface DistanceCalculator { 22 | 23 | /** The distance betweenfrom
and to
. */
24 | public double distance(Point from, Point to);
25 |
26 | /** The distance between from
and Point(toX,toY)
. */
27 | public double distance(Point from, double toX, double toY);
28 |
29 | /** Returns true if the distance between from and to is <= distance. */
30 | public boolean within(Point from, double toX, double toY, double distance);
31 |
32 | /**
33 | * Calculates where a destination point is given an origin (from
)
34 | * distance, and bearing (given in degrees -- 0-360). If reuse is given, then
35 | * this method may reset() it and return it.
36 | */
37 | public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse);
38 |
39 | /**
40 | * Calculates the bounding box of a circle, as specified by its center point
41 | * and distance.
42 | */
43 | public Rectangle calcBoxByDistFromPt(Point from, double distDEG, SpatialContext ctx, Rectangle reuse);
44 |
45 | /**
46 | * The Y
coordinate of the horizontal axis of a circle that has maximum width. On a
47 | * 2D plane, this result is always from.getY()
but, perhaps surprisingly, on a sphere
48 | * it is going to be slightly different.
49 | */
50 | public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx);
51 |
52 | public double area(Rectangle rect);
53 |
54 | public double area(Circle circle);
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/distance/GeodesicSphereDistCalc.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.distance;
10 |
11 | import org.locationtech.spatial4j.context.SpatialContext;
12 | import org.locationtech.spatial4j.shape.Circle;
13 | import org.locationtech.spatial4j.shape.Point;
14 | import org.locationtech.spatial4j.shape.Rectangle;
15 |
16 | import static org.locationtech.spatial4j.distance.DistanceUtils.toDegrees;
17 | import static org.locationtech.spatial4j.distance.DistanceUtils.toRadians;
18 |
19 | /**
20 | * A base class for a Distance Calculator that assumes a spherical earth model.
21 | */
22 | public abstract class GeodesicSphereDistCalc extends AbstractDistanceCalculator {
23 |
24 | private static final double radiusDEG = DistanceUtils.toDegrees(1);//in degrees
25 |
26 | @Override
27 | public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) {
28 | if (distDEG == 0) {
29 | if (reuse == null)
30 | return from;
31 | reuse.reset(from.getX(), from.getY());
32 | return reuse;
33 | }
34 | Point result = DistanceUtils.pointOnBearingRAD(
35 | toRadians(from.getY()), toRadians(from.getX()),
36 | toRadians(distDEG),
37 | toRadians(bearingDEG), ctx, reuse);//output result is in radians
38 | result.reset(toDegrees(result.getX()), toDegrees(result.getY()));
39 | return result;
40 | }
41 |
42 | @Override
43 | public Rectangle calcBoxByDistFromPt(Point from, double distDEG, SpatialContext ctx, Rectangle reuse) {
44 | return DistanceUtils.calcBoxByDistFromPtDEG(from.getY(), from.getX(), distDEG, ctx, reuse);
45 | }
46 |
47 | @Override
48 | public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx) {
49 | return DistanceUtils.calcBoxByDistFromPt_latHorizAxisDEG(from.getY(), from.getX(), distDEG);
50 | }
51 |
52 | @Override
53 | public double area(Rectangle rect) {
54 | //From http://mathforum.org/library/drmath/view/63767.html
55 | double lat1 = toRadians(rect.getMinY());
56 | double lat2 = toRadians(rect.getMaxY());
57 | return Math.PI / 180 * radiusDEG * radiusDEG *
58 | Math.abs(Math.sin(lat1) - Math.sin(lat2)) *
59 | rect.getWidth();
60 | }
61 |
62 | @Override
63 | public double area(Circle circle) {
64 | //formula is a simplified case of area(rect).
65 | double lat = toRadians(90 - circle.getRadius());
66 | return 2 * Math.PI * radiusDEG * radiusDEG * (1 - Math.sin(lat));
67 | }
68 |
69 | @Override
70 | public boolean equals(Object obj) {
71 | if (obj == null)
72 | return false;
73 | return getClass().equals(obj.getClass());
74 | }
75 |
76 | @Override
77 | public int hashCode() {
78 | return getClass().hashCode();
79 | }
80 |
81 | @Override
82 | public final double distance(Point from, double toX, double toY) {
83 | return toDegrees(distanceLatLonRAD(toRadians(from.getY()), toRadians(from.getX()), toRadians(toY), toRadians(toX)));
84 | }
85 |
86 | protected abstract double distanceLatLonRAD(double lat1, double lon1, double lat2, double lon2);
87 |
88 | public static class Haversine extends GeodesicSphereDistCalc {
89 |
90 | @Override
91 | protected double distanceLatLonRAD(double lat1, double lon1, double lat2, double lon2) {
92 | return DistanceUtils.distHaversineRAD(lat1,lon1,lat2,lon2);
93 | }
94 |
95 | }
96 |
97 | public static class LawOfCosines extends GeodesicSphereDistCalc {
98 |
99 | @Override
100 | protected double distanceLatLonRAD(double lat1, double lon1, double lat2, double lon2) {
101 | return DistanceUtils.distLawOfCosinesRAD(lat1, lon1, lat2, lon2);
102 | }
103 |
104 | }
105 |
106 | public static class Vincenty extends GeodesicSphereDistCalc {
107 |
108 | @Override
109 | protected double distanceLatLonRAD(double lat1, double lon1, double lat2, double lon2) {
110 | return DistanceUtils.distVincentyRAD(lat1, lon1, lat2, lon2);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/distance/package-info.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | /**
10 | * Ways to calculate distance.
11 | */
12 | package org.locationtech.spatial4j.distance;
13 |
14 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/exception/InvalidShapeException.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.exception;
10 |
11 | /**
12 | * A shape was constructed but failed because, based on the given parts, it's invalid. For example
13 | * a rectangle's minimum Y was specified as greater than the maximum Y. This class is not used for
14 | * parsing exceptions; that's usually {@link java.text.ParseException}.
15 | */
16 | public class InvalidShapeException extends RuntimeException {
17 |
18 | public InvalidShapeException(String reason, Throwable cause) {
19 | super(reason, cause);
20 | }
21 |
22 | public InvalidShapeException(String reason) {
23 | super(reason);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/exception/UnsupportedSpatialPredicate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | // NOTE: we keep the header as it came from ASF; it did not originate in Spatial4j
19 |
20 | package org.locationtech.spatial4j.exception;
21 |
22 | import org.locationtech.spatial4j.SpatialPredicate;
23 |
24 | /**
25 | * Exception thrown when something cannot implement the {@link SpatialPredicate}.
26 | */
27 | public class UnsupportedSpatialPredicate extends UnsupportedOperationException {
28 |
29 | public UnsupportedSpatialPredicate(SpatialPredicate op) {
30 | super(op.getName());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/io/LegacyShapeReader.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.io;
10 |
11 | import java.io.IOException;
12 | import java.io.Reader;
13 | import java.text.ParseException;
14 | import java.util.StringTokenizer;
15 |
16 | import org.locationtech.spatial4j.context.SpatialContext;
17 | import org.locationtech.spatial4j.context.SpatialContextFactory;
18 | import org.locationtech.spatial4j.exception.InvalidShapeException;
19 | import org.locationtech.spatial4j.shape.Point;
20 | import org.locationtech.spatial4j.shape.Shape;
21 |
22 | /**
23 | * Reads a shape from the old format.
24 | * new WKTReader(ctx.getGeometryFactory()))58 | * @return Non-Null 59 | */ 60 | protected Shape parseIfSupported(String str, WKTReader reader) throws ParseException { 61 | try { 62 | Geometry geom = reader.read(str); 63 | 64 | //Normalizes & verifies coordinates 65 | checkCoordinates(geom); 66 | 67 | if (geom instanceof org.locationtech.jts.geom.Point) { 68 | org.locationtech.jts.geom.Point ptGeom = (org.locationtech.jts.geom.Point) geom; 69 | if (getShapeFactory().useJtsPoint()) 70 | return new JtsPoint(ptGeom, (JtsSpatialContext) ctx); 71 | else 72 | return getShapeFactory().pointXY(ptGeom.getX(), ptGeom.getY()); 73 | } else if (geom.isRectangle()) { 74 | return getShapeFactory().makeRectFromRectangularPoly(geom); 75 | } else { 76 | return getShapeFactory().makeShapeFromGeometry(geom); 77 | } 78 | } catch (InvalidShapeException e) { 79 | throw e; 80 | } catch (Exception e) { 81 | throw new InvalidShapeException("error reading WKT: "+e.toString(), e); 82 | } 83 | } 84 | 85 | 86 | protected void checkCoordinates(Geometry geom) { 87 | // note: JTS WKTReader has already normalized coords with the JTS PrecisionModel. 88 | geom.apply(new CoordinateSequenceFilter() { 89 | boolean changed = false; 90 | @Override 91 | public void filter(CoordinateSequence seq, int i) { 92 | double x = seq.getX(i); 93 | double y = seq.getY(i); 94 | 95 | //Note: we don't simply call ctx.normX & normY because 96 | // those methods use the precisionModel, but WKTReader already 97 | // used the precisionModel. It's be nice to turn that off somehow but alas. 98 | if (ctx.isGeo() && ctx.isNormWrapLongitude()) { 99 | double xNorm = DistanceUtils.normLonDEG(x); 100 | if (Double.compare(x, xNorm) != 0) {//handles NaN 101 | changed = true; 102 | seq.setOrdinate(i, CoordinateSequence.X, xNorm); 103 | } 104 | // double yNorm = DistanceUtils.normLatDEG(y); 105 | // if (y != yNorm) { 106 | // changed = true; 107 | // seq.setOrdinate(i,CoordinateSequence.Y,yNorm); 108 | // } 109 | } 110 | ctx.verifyX(x); 111 | ctx.verifyY(y); 112 | } 113 | 114 | @Override 115 | public boolean isDone() { return false; } 116 | 117 | @Override 118 | public boolean isGeometryChanged() { return changed; } 119 | }); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/io/jts/JtsWKTWriter.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 VoyagerSearch 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.io.jts; 10 | 11 | import org.locationtech.spatial4j.context.jts.JtsSpatialContext; 12 | import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory; 13 | import org.locationtech.spatial4j.io.WKTWriter; 14 | import org.locationtech.spatial4j.shape.Shape; 15 | import org.locationtech.spatial4j.shape.jts.JtsGeometry; 16 | 17 | /** 18 | * Writes the WKT using JTS directly 19 | */ 20 | public class JtsWKTWriter extends WKTWriter { 21 | 22 | public JtsWKTWriter(JtsSpatialContext ctx, JtsSpatialContextFactory factory) { 23 | 24 | } 25 | 26 | @Override 27 | public String toString(Shape shape) { 28 | if (shape instanceof JtsGeometry) { 29 | return ((JtsGeometry) shape).getGeom().toText(); 30 | } 31 | return super.toString(shape); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/io/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | /** Reading & writing shapes in various forms. */ 10 | package org.locationtech.spatial4j.io; -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/package-info.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | /** 10 | * This is the base package for Spatial4j from which the rest of it is organized. 11 | */ 12 | package org.locationtech.spatial4j; 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/shape/BaseShape.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 VoyagerSearch 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.shape; 10 | 11 | import org.locationtech.spatial4j.context.SpatialContext; 12 | 13 | public abstract class BaseShape
14 | * In geospatial contexts, it may cross the international date line (-180 15 | * longitude) if {@link #getCrossesDateLine()} however it cannot pass the poles 16 | * although it may span the globe. It spans the globe if the X coordinate 17 | * (Longitude) goes from -180 to 180 as seen from {@link #getMinX()} and {@link 18 | * #getMaxX()}. 19 | */ 20 | public interface Rectangle extends Shape { 21 | 22 | /** 23 | * Expert: Resets the state of this shape given the arguments. This is a 24 | * performance feature to avoid excessive Shape object allocation as well as 25 | * some argument error checking. Mutable shapes is error-prone so use with 26 | * care. 27 | */ 28 | public void reset(double minX, double maxX, double minY, double maxY); 29 | 30 | /** 31 | * The width. In geospatial contexts, this is generally in degrees longitude 32 | * and is aware of the dateline (aka anti-meridian). It will always be >= 0. 33 | */ 34 | public double getWidth(); 35 | 36 | /** 37 | * The height. In geospatial contexts, this is in degrees latitude. It will 38 | * always be >= 0. 39 | */ 40 | public double getHeight(); 41 | 42 | /** The left edge of the X coordinate. */ 43 | public double getMinX(); 44 | 45 | /** The bottom edge of the Y coordinate. */ 46 | public double getMinY(); 47 | 48 | /** The right edge of the X coordinate. */ 49 | public double getMaxX(); 50 | 51 | /** The top edge of the Y coordinate. */ 52 | public double getMaxY(); 53 | 54 | /** Only meaningful for geospatial contexts. */ 55 | public boolean getCrossesDateLine(); 56 | 57 | /** 58 | * A specialization of {@link Shape#relate(Shape)} 59 | * for a vertical line. 60 | */ 61 | public SpatialRelation relateYRange(double minY, double maxY); 62 | 63 | /** 64 | * A specialization of {@link Shape#relate(Shape)} 65 | * for a horizontal line. 66 | */ 67 | public SpatialRelation relateXRange(double minX, double maxX); 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/locationtech/spatial4j/shape/Shape.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015 Voyager Search and MITRE 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License, Version 2.0 which 5 | * accompanies this distribution and is available at 6 | * http://www.apache.org/licenses/LICENSE-2.0.txt 7 | ******************************************************************************/ 8 | 9 | package org.locationtech.spatial4j.shape; 10 | 11 | import org.locationtech.spatial4j.context.SpatialContext; 12 | 13 | /** 14 | * The base interface defining a geometric shape. Shape instances should be 15 | * instantiated via one of the create* methods on a {@link SpatialContext} or 16 | * by reading WKT which calls those methods; they should not be 17 | * created directly. 18 | *
19 | * Shapes are generally immutable and thread-safe. If a particular shape has a
20 | * reset(...)
method then its use means the shape is actually
21 | * mutable. Mutating shape state is considered expert and should be done with care.
22 | */
23 | public interface Shape {
24 |
25 | /**
26 | * Describe the relationship between the two objects. For example
27 | *
38 | * If the shapes are equal then the result is CONTAINS (preferred) or WITHIN. 39 | */ 40 | SpatialRelation relate(Shape other); 41 | 42 | /** 43 | * Get the bounding box for this Shape. This means the shape is within the 44 | * bounding box and that it touches each side of the rectangle. 45 | *
46 | * Postcondition: this.getBoundingBox().relate(this) == CONTAINS
47 | */
48 | Rectangle getBoundingBox();
49 |
50 | /**
51 | * Does the shape have area? This will be false for points and lines. It will
52 | * also be false for shapes that normally have area but are constructed in a
53 | * degenerate case as to not have area (e.g. a circle with 0 radius or
54 | * rectangle with no height or no width).
55 | */
56 | boolean hasArea();
57 |
58 | /**
59 | * Calculates the area of the shape, in square-degrees. If ctx is null then
60 | * simple Euclidean calculations will be used. This figure can be an
61 | * estimate.
62 | */
63 | double getArea(SpatialContext ctx);
64 |
65 | /**
66 | * Returns the center point of this shape. This is usually the same as
67 | * getBoundingBox().getCenter()
but it doesn't have to be.
68 | *
69 | * Postcondition: this.relate(this.getCenter()) == CONTAINS
70 | */
71 | Point getCenter();
72 |
73 | /**
74 | * Returns a buffered version of this shape. The buffer is usually a
75 | * rounded-corner buffer, although some shapes might buffer differently. This
76 | * is an optional operation.
77 | *
78 | * @return Not null, and the returned shape should contain the current shape.
79 | */
80 | Shape getBuffered(double distance, SpatialContext ctx);
81 |
82 | /**
83 | * Shapes can be "empty", which is to say it exists nowhere. The underlying coordinates are
84 | * typically NaN.
85 | */
86 | boolean isEmpty();
87 |
88 | /** The sub-classes of Shape generally implement the
89 | * same contract for {@link Object#equals(Object)} and {@link Object#hashCode()}
90 | * amongst the same sub-interface type. This means, for example, that multiple
91 | * Point implementations of different classes are equal if they share the same x
92 | * & y. */
93 | @Override
94 | public boolean equals(Object other);
95 |
96 |
97 | /**
98 | * Get the SpatialContext that created the Shape
99 | */
100 | public SpatialContext getContext();
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/shape/SpatialRelation.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.shape;
10 |
11 | /**
12 | * The set of spatial relationships. Naming is somewhat consistent with OGC spec
13 | * conventions as seen in SQL/MM and others.
14 | *
15 | * There is no equality case. If two Shape instances are equal then the result 16 | * might be CONTAINS (preferred) or WITHIN. Client logic may have to be aware 17 | * of this edge condition; Spatial4j testing certainly does. 18 | *
19 | * The "CONTAINS" and "WITHIN" wording here is inconsistent with OGC; these here map to OGC
20 | * "COVERS" and "COVERED BY", respectively. The distinction is in the boundaries; in Spatial4j
21 | * there is no boundary distinction -- boundaries are part of the shape as if it was an "interior",
22 | * with respect to OGC's terminology.
23 | */
24 | public enum SpatialRelation {
25 | //see http://docs.geotools.org/latest/userguide/library/jts/dim9.html#preparedgeometry
26 |
27 | /**
28 | * The shape is within the target geometry. It's the converse of {@link #CONTAINS}.
29 | * Boundaries of shapes count too. OGC specs refer to this relation as "COVERED BY";
30 | * WITHIN is differentiated there by not including boundaries.
31 | */
32 | WITHIN,
33 |
34 | /**
35 | * The shape contains the target geometry. It's the converse of {@link #WITHIN}.
36 | * Boundaries of shapes count too. OGC specs refer to this relation as "COVERS";
37 | * CONTAINS is differentiated there by not including boundaries.
38 | */
39 | CONTAINS,
40 |
41 | /**
42 | * The shape shares no point in common with the target shape.
43 | */
44 | DISJOINT,
45 |
46 | /**
47 | * The shape shares some points/overlap with the target shape, and the relation is
48 | * not more specifically {@link #WITHIN} or {@link #CONTAINS}.
49 | */
50 | INTERSECTS;
51 | //Don't have these: TOUCHES, CROSSES, OVERLAPS, nor distinction between CONTAINS/COVERS
52 |
53 | /**
54 | * Given the result of shapeA.relate(shapeB)
, transposing that
55 | * result should yield the result of shapeB.relate(shapeA)
. There
56 | * is a corner case is when the shapes are equal, in which case actually
57 | * flipping the relate() call will result in the same value -- either CONTAINS
58 | * or WITHIN; this method can't possible check for that so the caller might
59 | * have to.
60 | */
61 | public SpatialRelation transpose() {
62 | switch(this) {
63 | case CONTAINS: return SpatialRelation.WITHIN;
64 | case WITHIN: return SpatialRelation.CONTAINS;
65 | default: return this;
66 | }
67 | }
68 |
69 | /**
70 | * If you were to call aShape.relate(bShape) and aShape.relate(cShape), you
71 | * could call this to merge the intersect results as if bShape & cShape were
72 | * combined into {@link ShapeCollection}. If {@code other} is null then the
73 | * result is "this".
74 | */
75 | public SpatialRelation combine(SpatialRelation other) {
76 | // You can think of this algorithm as a state transition / automata.
77 | // 1. The answer must be the same no matter what the order is.
78 | // 2. If any INTERSECTS, then the result is INTERSECTS (done).
79 | // 3. A DISJOINT + WITHIN == INTERSECTS (done).
80 | // 4. A DISJOINT + CONTAINS == CONTAINS.
81 | // 5. A CONTAINS + WITHIN == INTERSECTS (done). (weird scenario)
82 | // 6. X + X == X.)
83 | // 7. X + null == X;
84 | if (other == this || other == null)
85 | return this;
86 | if (this == DISJOINT && other == CONTAINS
87 | || this == CONTAINS && other == DISJOINT)
88 | return CONTAINS;
89 | return INTERSECTS;
90 | }
91 |
92 | /** Not DISJOINT, i.e. there is some sort of intersection. */
93 | public boolean intersects() {
94 | return this != DISJOINT;
95 | }
96 |
97 | /**
98 | * If aShape.relate(bShape)
is r, then r.inverse()
99 | * is inverse(aShape).relate(bShape)
whereas
100 | * inverse(shape)
is theoretically the opposite area covered by a
101 | * shape, i.e. everywhere but where the shape is.
102 | *
103 | * Note that it's not commutative! WITHIN.inverse().inverse() !=
104 | * WITHIN
.
105 | */
106 | public SpatialRelation inverse() {
107 | switch(this) {
108 | case DISJOINT: return CONTAINS;
109 | case CONTAINS: return DISJOINT;
110 | case WITHIN: return INTERSECTS;//not commutative!
111 | }
112 | return INTERSECTS;
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/shape/impl/InfBufLine.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.shape.impl;
10 |
11 | import org.locationtech.spatial4j.shape.Point;
12 | import org.locationtech.spatial4j.shape.Rectangle;
13 | import org.locationtech.spatial4j.shape.SpatialRelation;
14 |
15 | import static org.locationtech.spatial4j.shape.SpatialRelation.*;
16 |
17 | /**
18 | * INERNAL: A buffered line of infinite length.
19 | * Public for test access.
20 | */
21 | public class InfBufLine {
22 |
23 | /** Error epsilon. */
24 | private static final double EPS = 10e-14;
25 |
26 | //TODO consider removing support for vertical line -- let caller
27 | // do something else. BufferedLine could have a factory method
28 | // that returns a rectangle, for example.
29 |
30 | // line: y = slope * x + intercept
31 |
32 | private final double slope;//can be infinite for vertical line
33 | //if slope is infinite, this is x intercept, otherwise y intercept
34 | private final double intercept;
35 |
36 | private final double buf;
37 |
38 | private final double distDenomInv;//cached: 1 / Math.sqrt(slope * slope + 1)
39 |
40 | InfBufLine(double slope, Point point, double buf) {
41 | assert !Double.isNaN(slope);
42 | this.slope = slope;
43 | if (Double.isInfinite(slope)) {
44 | intercept = point.getX();
45 | distDenomInv = Double.NaN;
46 | } else {
47 | intercept = point.getY() - slope * point.getX();
48 | distDenomInv = 1 / Math.sqrt(slope * slope + 1);
49 | }
50 | this.buf = buf;
51 | }
52 |
53 | SpatialRelation relate(Rectangle r, Point prC, Point scratch) {
54 | assert r.getCenter().equals(prC);
55 |
56 | int cQuad = quadrant(prC);
57 |
58 | Point nearestP = scratch;
59 | cornerByQuadrant(r, oppositeQuad[cQuad], nearestP);
60 | boolean nearestContains = contains(nearestP);
61 |
62 | if (nearestContains) {
63 | Point farthestP = scratch;
64 | nearestP = null;//just to be safe (same scratch object)
65 | cornerByQuadrant(r, cQuad, farthestP);
66 | boolean farthestContains = contains(farthestP);
67 | if (farthestContains)
68 | return CONTAINS;
69 | return INTERSECTS;
70 | } else {// not nearestContains
71 | if (quadrant(nearestP) == cQuad)
72 | return DISJOINT;//out of buffer on same side as center
73 | return INTERSECTS;//nearest & farthest points straddle the line
74 | }
75 | }
76 |
77 | boolean contains(Point p) {
78 | return (distanceUnbuffered(p) <= buf + EPS);
79 | }
80 |
81 | /** INTERNAL AKA lineToPointDistance */
82 | public double distanceUnbuffered(Point c) {
83 | if (Double.isInfinite(slope))
84 | return Math.abs(c.getX() - intercept);
85 | // http://math.ucsd.edu/~wgarner/math4c/derivations/distance/distptline.htm
86 | double num = Math.abs(c.getY() - slope * c.getX() - intercept);
87 | return num * distDenomInv;
88 | }
89 |
90 | // /** Amount to add or subtract to intercept to indicate where the
91 | // * buffered line edges cross the y axis.
92 | // * @return
93 | // */
94 | // double interceptBuffOffset() {
95 | // if (Double.isInfinite(slope))
96 | // return slope;
97 | // if (buf == 0)
98 | // return 0;
99 | // double slopeDivBuf = slope / buf;
100 | // return Math.sqrt(buf*buf + slopeDivBuf*slopeDivBuf);
101 | // }
102 |
103 | /** INTERNAL: AKA lineToPointQuadrant */
104 | public int quadrant(Point c) {
105 | //check vertical line case 1st
106 | if (Double.isInfinite(slope)) {
107 | //when slope is infinite, intercept is x intercept instead of y
108 | return c.getX() > intercept ? 1 : 2; //4 : 3 would work too
109 | }
110 | //(below will work for slope==0 horizontal line too)
111 | //is c above or below the line
112 | double yAtCinLine = slope * c.getX() + intercept;
113 | boolean above = c.getY() >= yAtCinLine;
114 | if (slope > 0) {
115 | //if slope is a forward slash, then result is 2 | 4
116 | return above ? 2 : 4;
117 | } else {
118 | //if slope is a backward slash, then result is 1 | 3
119 | return above ? 1 : 3;
120 | }
121 | }
122 |
123 | //TODO ? Use an Enum for quadrant?
124 |
125 | /* quadrants 1-4: NE, NW, SW, SE. */
126 | private static final int[] oppositeQuad= {-1,3,4,1,2};
127 |
128 | public static void cornerByQuadrant(Rectangle r, int cornerQuad, Point out) {
129 | double x = (cornerQuad == 1 || cornerQuad == 4) ? r.getMaxX() : r.getMinX();
130 | double y = (cornerQuad == 1 || cornerQuad == 2) ? r.getMaxY() : r.getMinY();
131 | out.reset(x, y);
132 | }
133 |
134 | public double getSlope() {
135 | return slope;
136 | }
137 |
138 | public double getIntercept() {
139 | return intercept;
140 | }
141 |
142 | public double getBuf() {
143 | return buf;
144 | }
145 |
146 | /** 1 / Math.sqrt(slope * slope + 1) */
147 | public double getDistDenomInv() {
148 | return distDenomInv;
149 | }
150 |
151 | @Override
152 | public String toString() {
153 | return "InfBufLine{" +
154 | "buf=" + buf +
155 | ", intercept=" + intercept +
156 | ", slope=" + slope +
157 | '}';
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/main/java/org/locationtech/spatial4j/shape/impl/PointImpl.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2015 Voyager Search and MITRE
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License, Version 2.0 which
5 | * accompanies this distribution and is available at
6 | * http://www.apache.org/licenses/LICENSE-2.0.txt
7 | ******************************************************************************/
8 |
9 | package org.locationtech.spatial4j.shape.impl;
10 |
11 | import org.locationtech.spatial4j.context.SpatialContext;
12 | import org.locationtech.spatial4j.shape.BaseShape;
13 | import org.locationtech.spatial4j.shape.Circle;
14 | import org.locationtech.spatial4j.shape.Point;
15 | import org.locationtech.spatial4j.shape.Rectangle;
16 | import org.locationtech.spatial4j.shape.Shape;
17 | import org.locationtech.spatial4j.shape.SpatialRelation;
18 |
19 | /** A basic 2D implementation of a Point. */
20 | public class PointImpl extends BaseShape