Move legacy geo_shape implementation to its own module (#77856) (#78735)

This commit moves the legacy geo_shape implementation to its own module and removes
the dependency on Spatial4J / JTS from server.
This commit is contained in:
Ignacio Vera 2021-10-11 08:23:22 +02:00 committed by GitHub
parent ca5480b9a8
commit 82dc997010
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 5634 additions and 3780 deletions

View file

@ -11,9 +11,9 @@ package org.elasticsearch.client.documentation;
import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.query.GeoShapeQueryBuilder; import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder;
@ -178,16 +178,16 @@ public class QueryDSLDocumentationTests extends ESTestCase {
public void testGeoShape() throws IOException { public void testGeoShape() throws IOException {
{ {
// tag::geo_shape // tag::geo_shape
List<Point> points = new ArrayList<>();
points.add(new Point(0, 0));
points.add(new Point(0, 10));
points.add(new Point(10, 10));
points.add(new Point(10, 0));
points.add(new Point(0, 0));
GeoShapeQueryBuilder qb = geoShapeQuery( GeoShapeQueryBuilder qb = geoShapeQuery(
"pin.location", // <1> "pin.location", // <1>
new MultiPointBuilder( // <2> new MultiPoint(points) // <2>
new CoordinatesBuilder() );
.coordinate(0, 0)
.coordinate(0, 10)
.coordinate(10, 10)
.coordinate(10, 0)
.coordinate(0, 0)
.build()));
qb.relation(ShapeRelation.WITHIN); // <3> qb.relation(ShapeRelation.WITHIN); // <3>
// end::geo_shape // end::geo_shape
} }

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
apply plugin: 'elasticsearch.internal-cluster-test'
apply plugin: 'nebula.optional-base'
esplugin {
description 'Placeholder plugin for geospatial features in ES'
classname 'org.elasticsearch.legacygeo.LegacyGeoPlugin'
}
dependencies {
api "org.apache.lucene:lucene-spatial-extras:${versions.lucene}"
api "org.locationtech.spatial4j:spatial4j:${versions.spatial4j}", optional
api "org.locationtech.jts:jts-core:${versions.jts}", optional
testImplementation project(":test:framework")
}
tasks.named("thirdPartyAudit").configure {
ignoreMissingClasses(
// from org.locationtech.spatial4j.io.GeoJSONReader (spatial4j)
'org.noggit.JSONParser',
// from org.locationtech.spatial4j.io.jackson.ShapeAsGeoJSONSerialize
'com.fasterxml.jackson.databind.JsonSerializer',
'com.fasterxml.jackson.databind.JsonDeserializer',
'com.fasterxml.jackson.databind.node.ArrayNode',
'com.fasterxml.jackson.databind.DeserializationContext',
'com.fasterxml.jackson.databind.JsonNode',
'com.fasterxml.jackson.databind.SerializerProvider',
'com.fasterxml.jackson.databind.module.SimpleModule',
'com.fasterxml.jackson.databind.node.ObjectNode',
// from lucene-spatial
'com.google.common.geometry.S2Cell',
'com.google.common.geometry.S2CellId',
'com.google.common.geometry.S2Projections',
'com.google.common.geometry.S2Point',
'com.google.common.geometry.S2$Metric',
'com.google.common.geometry.S2LatLng'
)
}
tasks.named("dependencyLicenses").configure {
mapping from: /lucene-.*/, to: 'lucene'
}

View file

@ -0,0 +1,475 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
derived from unicode conversion examples available at
http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright
from those sources:
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
Some code in core/src/java/org/apache/lucene/util/ArrayUtil.java was
derived from Python 2.4.2 sources available at
http://www.python.org. Full license is here:
http://www.python.org/download/releases/2.4.2/license/
Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
derived from Python 3.1.2 sources available at
http://www.python.org. Full license is here:
http://www.python.org/download/releases/3.1.2/license/
Some code in core/src/java/org/apache/lucene/util/automaton was
derived from Brics automaton sources available at
www.brics.dk/automaton/. Here is the copyright from those sources:
/*
* Copyright (c) 2001-2009 Anders Moeller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
The levenshtein automata tables in core/src/java/org/apache/lucene/util/automaton
were automatically generated with the moman/finenight FSA package.
Here is the copyright for those sources:
# Copyright (c) 2010, Jean-Philippe Barrette-LaPierre, <jpb@rrette.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
derived from ICU (http://www.icu-project.org)
The full license is available here:
http://source.icu-project.org/repos/icu/icu/trunk/license.html
/*
* Copyright (C) 1999-2010, International Business Machines
* Corporation and others. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* provided that the above copyright notice(s) and this permission notice appear
* in all copies of the Software and that both the above copyright notice(s) and
* this permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
* LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Except as contained in this notice, the name of a copyright holder shall not
* be used in advertising or otherwise to promote the sale, use or other
* dealings in this Software without prior written authorization of the
* copyright holder.
*/
The following license applies to the Snowball stemmers:
Copyright (c) 2001, Dr Martin Porter
Copyright (c) 2002, Richard Boulton
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The following license applies to the KStemmer:
Copyright © 2003,
Center for Intelligent Information Retrieval,
University of Massachusetts, Amherst.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The names "Center for Intelligent Information Retrieval" and
"University of Massachusetts" must not be used to endorse or promote products
derived from this software without prior written permission. To obtain
permission, contact info@ciir.cs.umass.edu.
THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF MASSACHUSETTS AND OTHER CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
The following license applies to the Morfologik project:
Copyright (c) 2006 Dawid Weiss
Copyright (c) 2007-2011 Dawid Weiss, Marcin Miłkowski
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Morfologik nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
The dictionary comes from Morfologik project. Morfologik uses data from
Polish ispell/myspell dictionary hosted at http://www.sjp.pl/slownik/en/ and
is licenced on the terms of (inter alia) LGPL and Creative Commons
ShareAlike. The part-of-speech tags were added in Morfologik project and
are not found in the data from sjp.pl. The tagset is similar to IPI PAN
tagset.
---
The following license applies to the Morfeusz project,
used by org.apache.lucene.analysis.morfologik.
BSD-licensed dictionary of Polish (SGJP)
http://sgjp.pl/morfeusz/
Copyright © 2011 Zygmunt Saloni, Włodzimierz Gruszczyński,
Marcin Woliński, Robert Wołosz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,192 @@
Apache Lucene
Copyright 2014 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Includes software from other Apache Software Foundation projects,
including, but not limited to:
- Apache Ant
- Apache Jakarta Regexp
- Apache Commons
- Apache Xerces
ICU4J, (under analysis/icu) is licensed under an MIT styles license
and Copyright (c) 1995-2008 International Business Machines Corporation and others
Some data files (under analysis/icu/src/data) are derived from Unicode data such
as the Unicode Character Database. See http://unicode.org/copyright.html for more
details.
Brics Automaton (under core/src/java/org/apache/lucene/util/automaton) is
BSD-licensed, created by Anders Møller. See http://www.brics.dk/automaton/
The levenshtein automata tables (under core/src/java/org/apache/lucene/util/automaton) were
automatically generated with the moman/finenight FSA library, created by
Jean-Philippe Barrette-LaPierre. This library is available under an MIT license,
see http://sites.google.com/site/rrettesite/moman and
http://bitbucket.org/jpbarrette/moman/overview/
The class org.apache.lucene.util.WeakIdentityMap was derived from
the Apache CXF project and is Apache License 2.0.
The Google Code Prettify is Apache License 2.0.
See http://code.google.com/p/google-code-prettify/
JUnit (junit-4.10) is licensed under the Common Public License v. 1.0
See http://junit.sourceforge.net/cpl-v10.html
This product includes code (JaspellTernarySearchTrie) from Java Spelling Checkin
g Package (jaspell): http://jaspell.sourceforge.net/
License: The BSD License (http://www.opensource.org/licenses/bsd-license.php)
The snowball stemmers in
analysis/common/src/java/net/sf/snowball
were developed by Martin Porter and Richard Boulton.
The snowball stopword lists in
analysis/common/src/resources/org/apache/lucene/analysis/snowball
were developed by Martin Porter and Richard Boulton.
The full snowball package is available from
http://snowball.tartarus.org/
The KStem stemmer in
analysis/common/src/org/apache/lucene/analysis/en
was developed by Bob Krovetz and Sergio Guzman-Lara (CIIR-UMass Amherst)
under the BSD-license.
The Arabic,Persian,Romanian,Bulgarian, Hindi and Bengali analyzers (common) come with a default
stopword list that is BSD-licensed created by Jacques Savoy. These files reside in:
analysis/common/src/resources/org/apache/lucene/analysis/ar/stopwords.txt,
analysis/common/src/resources/org/apache/lucene/analysis/fa/stopwords.txt,
analysis/common/src/resources/org/apache/lucene/analysis/ro/stopwords.txt,
analysis/common/src/resources/org/apache/lucene/analysis/bg/stopwords.txt,
analysis/common/src/resources/org/apache/lucene/analysis/hi/stopwords.txt,
analysis/common/src/resources/org/apache/lucene/analysis/bn/stopwords.txt
See http://members.unine.ch/jacques.savoy/clef/index.html.
The German,Spanish,Finnish,French,Hungarian,Italian,Portuguese,Russian and Swedish light stemmers
(common) are based on BSD-licensed reference implementations created by Jacques Savoy and
Ljiljana Dolamic. These files reside in:
analysis/common/src/java/org/apache/lucene/analysis/de/GermanLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/de/GermanMinimalStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/es/SpanishLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/fi/FinnishLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchMinimalStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/hu/HungarianLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/it/ItalianLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/pt/PortugueseLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/ru/RussianLightStemmer.java
analysis/common/src/java/org/apache/lucene/analysis/sv/SwedishLightStemmer.java
The Stempel analyzer (stempel) includes BSD-licensed software developed
by the Egothor project http://egothor.sf.net/, created by Leo Galambos, Martin Kvapil,
and Edmond Nolan.
The Polish analyzer (stempel) comes with a default
stopword list that is BSD-licensed created by the Carrot2 project. The file resides
in stempel/src/resources/org/apache/lucene/analysis/pl/stopwords.txt.
See http://project.carrot2.org/license.html.
The SmartChineseAnalyzer source code (smartcn) was
provided by Xiaoping Gao and copyright 2009 by www.imdict.net.
WordBreakTestUnicode_*.java (under modules/analysis/common/src/test/)
is derived from Unicode data such as the Unicode Character Database.
See http://unicode.org/copyright.html for more details.
The Morfologik analyzer (morfologik) includes BSD-licensed software
developed by Dawid Weiss and Marcin Miłkowski (http://morfologik.blogspot.com/).
Morfologik uses data from Polish ispell/myspell dictionary
(http://www.sjp.pl/slownik/en/) licenced on the terms of (inter alia)
LGPL and Creative Commons ShareAlike.
Morfologic includes data from BSD-licensed dictionary of Polish (SGJP)
(http://sgjp.pl/morfeusz/)
Servlet-api.jar and javax.servlet-*.jar are under the CDDL license, the original
source code for this can be found at http://www.eclipse.org/jetty/downloads.php
===========================================================================
Kuromoji Japanese Morphological Analyzer - Apache Lucene Integration
===========================================================================
This software includes a binary and/or source version of data from
mecab-ipadic-2.7.0-20070801
which can be obtained from
http://atilika.com/releases/mecab-ipadic/mecab-ipadic-2.7.0-20070801.tar.gz
or
http://jaist.dl.sourceforge.net/project/mecab/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz
===========================================================================
mecab-ipadic-2.7.0-20070801 Notice
===========================================================================
Nara Institute of Science and Technology (NAIST),
the copyright holders, disclaims all warranties with regard to this
software, including all implied warranties of merchantability and
fitness, in no event shall NAIST be liable for
any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether in an
action of contract, negligence or other tortuous action, arising out
of or in connection with the use or performance of this software.
A large portion of the dictionary entries
originate from ICOT Free Software. The following conditions for ICOT
Free Software applies to the current dictionary as well.
Each User may also freely distribute the Program, whether in its
original form or modified, to any third party or parties, PROVIDED
that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
on, or be attached to, the Program, which is distributed substantially
in the same form as set out herein and that such intended
distribution, if actually made, will neither violate or otherwise
contravene any of the laws and regulations of the countries having
jurisdiction over the User or the intended distribution itself.
NO WARRANTY
The program was produced on an experimental basis in the course of the
research and development conducted during the project and is provided
to users as so produced on an experimental basis. Accordingly, the
program is provided without any warranty whatsoever, whether express,
implied, statutory or otherwise. The term "warranty" used herein
includes, but is not limited to, any warranty of the quality,
performance, merchantability and fitness for a particular purpose of
the program and the nonexistence of any infringement or violation of
any right of any third party.
Each user of the program will agree and understand, and be deemed to
have agreed and understood, that there is no warranty whatsoever for
the program and, accordingly, the entire risk arising from or
otherwise connected with the program is assumed by the user.
Therefore, neither ICOT, the copyright holder, or any other
organization that participated in or was otherwise related to the
development of the program and their respective officials, directors,
officers and other employees shall be held liable for any and all
damages, including, without limitation, general, special, incidental
and consequential damages, arising out of or otherwise in connection
with the use or inability to use the program or any product, material
or result produced or otherwise obtained by using the program,
regardless of whether they have been advised of, or otherwise had
knowledge of, the possibility of such damages at any time during the
project or thereafter. Each user will be deemed to have agreed to the
foregoing by his or her commencement of use of the program. The term
"use" as used herein includes, but is not limited to, the use,
modification, copying and distribution of the program and the
production of secondary products from the program.
In the case where the program, whether in its original form or
modified, was distributed or delivered to or received by a user from
any person, organization or entity other than ICOT, unless it makes or
grants independently of ICOT any specific warranty to the user in
writing, such person, organization or entity, will also be exempted
from and not be held liable to the user for any such damages as noted
above as far as the program is concerned.

View file

@ -6,12 +6,13 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.search.geo; package org.elasticsearch.legacygeo.search;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestLegacyGeoShapeFieldMapperPlugin; import org.elasticsearch.search.geo.GeoBoundingBoxQueryIntegTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@ -31,9 +32,16 @@ public class GeoBoundingBoxQueryLegacyGeoShapeIT extends GeoBoundingBoxQueryInte
@Override @Override
public XContentBuilder getMapping() throws IOException { public XContentBuilder getMapping() throws IOException {
return XContentFactory.jsonBuilder().startObject().startObject("type1") return XContentFactory.jsonBuilder()
.startObject("properties").startObject("location").field("type", "geo_shape").field("strategy", "recursive") .startObject()
.endObject().endObject().endObject().endObject(); .startObject("type1")
.startObject("properties")
.startObject("location")
.field("type", "geo_shape")
.field("strategy", "recursive")
.endObject()
.endObject()
.endObject()
.endObject();
} }
} }

View file

@ -6,14 +6,15 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.search.geo; package org.elasticsearch.legacygeo.search;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.Circle; import org.elasticsearch.geometry.Circle;
import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestLegacyGeoShapeFieldMapperPlugin; import org.elasticsearch.search.geo.GeoShapeIntegTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@ -51,21 +52,23 @@ public class LegacyGeoShapeIT extends GeoShapeIntegTestCase {
*/ */
public void testLegacyCircle() throws Exception { public void testLegacyCircle() throws Exception {
// create index // create index
assertAcked(prepareCreate("test") assertAcked(prepareCreate("test").addMapping("shape", "shape", "type=geo_shape,strategy=recursive,tree=geohash").get());
.addMapping("shape", "shape", "type=geo_shape,strategy=recursive,tree=geohash").get());
ensureGreen(); ensureGreen();
indexRandom(true, client().prepareIndex("test", "shape").setId("0").setSource("shape", (ToXContent) (builder, params) -> { indexRandom(true, client().prepareIndex("test", "shape").setId("0").setSource("shape", (ToXContent) (builder, params) -> {
builder.startObject().field("type", "circle") builder.startObject()
.startArray("coordinates").value(30).value(50).endArray() .field("type", "circle")
.field("radius","77km") .startArray("coordinates")
.value(30)
.value(50)
.endArray()
.field("radius", "77km")
.endObject(); .endObject();
return builder; return builder;
})); }));
// test self crossing of circles // test self crossing of circles
SearchResponse searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", SearchResponse searchResponse = client().prepareSearch("test").setQuery(geoShapeQuery("shape", new Circle(30, 50, 77000))).get();
new Circle(30, 50, 77000))).get();
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L));
} }
} }

View file

@ -5,23 +5,24 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.legacygeo;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.builders.CircleBuilder; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.CoordinateNode;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.legacygeo.builders.CircleBuilder;
import org.elasticsearch.legacygeo.builders.CoordinatesBuilder;
import org.elasticsearch.legacygeo.builders.EnvelopeBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiPointBuilder;
import org.elasticsearch.legacygeo.builders.MultiPolygonBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.legacygeo.parsers.CoordinateNode;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import java.util.ArrayList; import java.util.ArrayList;
@ -36,8 +37,7 @@ import java.util.Map;
public enum GeoShapeType { public enum GeoShapeType {
POINT("point") { POINT("point") {
@Override @Override
public PointBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public PointBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, Orientation orientation, boolean coerce) {
Orientation orientation, boolean coerce) {
return new PointBuilder().coordinate(validate(coordinates, coerce).coordinate); return new PointBuilder().coordinate(validate(coordinates, coerce).coordinate);
} }
@ -45,7 +45,8 @@ public enum GeoShapeType {
CoordinateNode validate(CoordinateNode coordinates, boolean coerce) { CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
if (coordinates.isEmpty()) { if (coordinates.isEmpty()) {
throw new ElasticsearchParseException( throw new ElasticsearchParseException(
"invalid number of points (0) provided when expecting a single coordinate ([lat, lng])"); "invalid number of points (0) provided when expecting a single coordinate ([lat, lng])"
);
} else if (coordinates.children != null) { } else if (coordinates.children != null) {
throw new ElasticsearchParseException("multipoint data provided when single point data expected."); throw new ElasticsearchParseException("multipoint data provided when single point data expected.");
} }
@ -54,8 +55,12 @@ public enum GeoShapeType {
}, },
MULTIPOINT("multipoint") { MULTIPOINT("multipoint") {
@Override @Override
public MultiPointBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public MultiPointBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder(); CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder();
for (CoordinateNode node : coordinates.children) { for (CoordinateNode node : coordinates.children) {
@ -68,11 +73,14 @@ public enum GeoShapeType {
CoordinateNode validate(CoordinateNode coordinates, boolean coerce) { CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
if (coordinates.children == null || coordinates.children.isEmpty()) { if (coordinates.children == null || coordinates.children.isEmpty()) {
if (coordinates.coordinate != null) { if (coordinates.coordinate != null) {
throw new ElasticsearchParseException("single coordinate found when expecting an array of " + throw new ElasticsearchParseException(
"coordinates. change type to point or change data to an array of >0 coordinates"); "single coordinate found when expecting an array of "
+ "coordinates. change type to point or change data to an array of >0 coordinates"
);
} }
throw new ElasticsearchParseException("no data provided for multipoint object when expecting " + throw new ElasticsearchParseException(
">0 points (e.g., [[lat, lng]] or [[lat, lng], ...])"); "no data provided for multipoint object when expecting " + ">0 points (e.g., [[lat, lng]] or [[lat, lng], ...])"
);
} else { } else {
for (CoordinateNode point : coordinates.children) { for (CoordinateNode point : coordinates.children) {
POINT.validate(point, coerce); POINT.validate(point, coerce);
@ -84,8 +92,12 @@ public enum GeoShapeType {
}, },
LINESTRING("linestring") { LINESTRING("linestring") {
@Override @Override
public LineStringBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public LineStringBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
CoordinatesBuilder line = new CoordinatesBuilder(); CoordinatesBuilder line = new CoordinatesBuilder();
for (CoordinateNode node : coordinates.children) { for (CoordinateNode node : coordinates.children) {
@ -97,16 +109,22 @@ public enum GeoShapeType {
@Override @Override
CoordinateNode validate(CoordinateNode coordinates, boolean coerce) { CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
if (coordinates.children.size() < 2) { if (coordinates.children.size() < 2) {
throw new ElasticsearchParseException("invalid number of points in LineString (found [{}] - must be >= 2)", throw new ElasticsearchParseException(
coordinates.children.size()); "invalid number of points in LineString (found [{}] - must be >= 2)",
coordinates.children.size()
);
} }
return coordinates; return coordinates;
} }
}, },
MULTILINESTRING("multilinestring") { MULTILINESTRING("multilinestring") {
@Override @Override
public MultiLineStringBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public MultiLineStringBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
MultiLineStringBuilder multiline = new MultiLineStringBuilder(); MultiLineStringBuilder multiline = new MultiLineStringBuilder();
for (CoordinateNode node : coordinates.children) { for (CoordinateNode node : coordinates.children) {
@ -118,20 +136,27 @@ public enum GeoShapeType {
@Override @Override
CoordinateNode validate(CoordinateNode coordinates, boolean coerce) { CoordinateNode validate(CoordinateNode coordinates, boolean coerce) {
if (coordinates.children.size() < 1) { if (coordinates.children.size() < 1) {
throw new ElasticsearchParseException("invalid number of lines in MultiLineString (found [{}] - must be >= 1)", throw new ElasticsearchParseException(
coordinates.children.size()); "invalid number of lines in MultiLineString (found [{}] - must be >= 1)",
coordinates.children.size()
);
} }
return coordinates; return coordinates;
} }
}, },
POLYGON("polygon") { POLYGON("polygon") {
@Override @Override
public PolygonBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public PolygonBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
// build shell // build shell
LineStringBuilder shell = LineStringBuilder.class.cast(LINESTRING.getBuilder(coordinates.children.get(0), LineStringBuilder shell = LineStringBuilder.class.cast(
radius, orientation, coerce)); LINESTRING.getBuilder(coordinates.children.get(0), radius, orientation, coerce)
);
// build polygon with shell and holes // build polygon with shell and holes
PolygonBuilder polygon = new PolygonBuilder(shell, orientation); PolygonBuilder polygon = new PolygonBuilder(shell, orientation);
for (int i = 1; i < coordinates.children.size(); ++i) { for (int i = 1; i < coordinates.children.size(); ++i) {
@ -145,19 +170,24 @@ public enum GeoShapeType {
void validateLinearRing(CoordinateNode coordinates, boolean coerce) { void validateLinearRing(CoordinateNode coordinates, boolean coerce) {
if (coordinates.children == null || coordinates.children.isEmpty()) { if (coordinates.children == null || coordinates.children.isEmpty()) {
String error = "Invalid LinearRing found."; String error = "Invalid LinearRing found.";
error += (coordinates.coordinate == null) ? error += (coordinates.coordinate == null)
" No coordinate array provided" : " Found a single coordinate when expecting a coordinate array"; ? " No coordinate array provided"
: " Found a single coordinate when expecting a coordinate array";
throw new ElasticsearchParseException(error); throw new ElasticsearchParseException(error);
} }
int numValidPts = coerce ? 3 : 4; int numValidPts = coerce ? 3 : 4;
if (coordinates.children.size() < numValidPts) { if (coordinates.children.size() < numValidPts) {
throw new ElasticsearchParseException("invalid number of points in LinearRing (found [{}] - must be >= [{}])", throw new ElasticsearchParseException(
coordinates.children.size(), numValidPts); "invalid number of points in LinearRing (found [{}] - must be >= [{}])",
coordinates.children.size(),
numValidPts
);
} }
// close linear ring iff coerce is set and ring is open, otherwise throw parse exception // close linear ring iff coerce is set and ring is open, otherwise throw parse exception
if (coordinates.children.get(0).coordinate.equals( if (coordinates.children.get(0).coordinate.equals(
coordinates.children.get(coordinates.children.size() - 1).coordinate) == false) { coordinates.children.get(coordinates.children.size() - 1).coordinate
) == false) {
if (coerce) { if (coerce) {
coordinates.children.add(coordinates.children.get(0)); coordinates.children.add(coordinates.children.get(0));
} else { } else {
@ -176,7 +206,8 @@ public enum GeoShapeType {
*/ */
if (coordinates.children == null || coordinates.children.isEmpty()) { if (coordinates.children == null || coordinates.children.isEmpty()) {
throw new ElasticsearchParseException( throw new ElasticsearchParseException(
"invalid LinearRing provided for type polygon. Linear ring must be an array of coordinates"); "invalid LinearRing provided for type polygon. Linear ring must be an array of coordinates"
);
} }
for (CoordinateNode ring : coordinates.children) { for (CoordinateNode ring : coordinates.children) {
validateLinearRing(ring, coerce); validateLinearRing(ring, coerce);
@ -187,8 +218,12 @@ public enum GeoShapeType {
}, },
MULTIPOLYGON("multipolygon") { MULTIPOLYGON("multipolygon") {
@Override @Override
public MultiPolygonBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public MultiPolygonBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
MultiPolygonBuilder polygons = new MultiPolygonBuilder(orientation); MultiPolygonBuilder polygons = new MultiPolygonBuilder(orientation);
for (CoordinateNode node : coordinates.children) { for (CoordinateNode node : coordinates.children) {
@ -205,8 +240,12 @@ public enum GeoShapeType {
}, },
ENVELOPE("envelope") { ENVELOPE("envelope") {
@Override @Override
public EnvelopeBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public EnvelopeBuilder getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
validate(coordinates, coerce); validate(coordinates, coerce);
// verify coordinate bounds, correct if necessary // verify coordinate bounds, correct if necessary
Coordinate uL = coordinates.children.get(0).coordinate; Coordinate uL = coordinates.children.get(0).coordinate;
@ -220,7 +259,9 @@ public enum GeoShapeType {
if (coordinates.children.size() != 2) { if (coordinates.children.size() != 2) {
throw new ElasticsearchParseException( throw new ElasticsearchParseException(
"invalid number of points [{}] provided for geo_shape [{}] when expecting an array of 2 coordinates", "invalid number of points [{}] provided for geo_shape [{}] when expecting an array of 2 coordinates",
coordinates.children.size(), GeoShapeType.ENVELOPE.shapename); coordinates.children.size(),
GeoShapeType.ENVELOPE.shapename
);
} }
return coordinates; return coordinates;
} }
@ -232,8 +273,7 @@ public enum GeoShapeType {
}, },
CIRCLE("circle") { CIRCLE("circle") {
@Override @Override
public CircleBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public CircleBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, Orientation orientation, boolean coerce) {
Orientation orientation, boolean coerce) {
return new CircleBuilder().center(coordinates.coordinate).radius(radius); return new CircleBuilder().center(coordinates.coordinate).radius(radius);
} }
@ -246,8 +286,12 @@ public enum GeoShapeType {
}, },
GEOMETRYCOLLECTION("geometrycollection") { GEOMETRYCOLLECTION("geometrycollection") {
@Override @Override
public ShapeBuilder<?, ?, ?> getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public ShapeBuilder<?, ?, ?> getBuilder(
Orientation orientation, boolean coerce) { CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
) {
// noop, handled in parser // noop, handled in parser
return null; return null;
} }
@ -283,11 +327,16 @@ public enum GeoShapeType {
if (shapeTypeMap.containsKey(typename)) { if (shapeTypeMap.containsKey(typename)) {
return shapeTypeMap.get(typename); return shapeTypeMap.get(typename);
} }
throw new IllegalArgumentException("unknown geo_shape ["+geoshapename+"]"); throw new IllegalArgumentException("unknown geo_shape [" + geoshapename + "]");
} }
public abstract ShapeBuilder<?, ?, ?> getBuilder(CoordinateNode coordinates, DistanceUnit.Distance radius, public abstract ShapeBuilder<?, ?, ?> getBuilder(
Orientation orientation, boolean coerce); CoordinateNode coordinates,
DistanceUnit.Distance radius,
Orientation orientation,
boolean coerce
);
abstract CoordinateNode validate(CoordinateNode coordinates, boolean coerce); abstract CoordinateNode validate(CoordinateNode coordinates, boolean coerce);
/** wkt shape name */ /** wkt shape name */

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.plugins.ExtensiblePlugin;
import org.elasticsearch.plugins.Plugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class LegacyGeoPlugin extends Plugin implements ExtensiblePlugin {
@Override
public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) {
return new ArrayList<>(GeoShapeType.getShapeWriteables());
}
return Collections.emptyList();
}
}

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.legacygeo;
public class ShapesAvailability { public class ShapesAvailability {

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.legacygeo;
import org.locationtech.spatial4j.context.SpatialContext; import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
@ -19,17 +19,17 @@ import java.util.List;
*/ */
public class XShapeCollection<S extends Shape> extends ShapeCollection<S> { public class XShapeCollection<S extends Shape> extends ShapeCollection<S> {
private boolean pointsOnly = false; private boolean pointsOnly = false;
public XShapeCollection(List<S> shapes, SpatialContext ctx) { public XShapeCollection(List<S> shapes, SpatialContext ctx) {
super(shapes, ctx); super(shapes, ctx);
} }
public boolean pointsOnly() { public boolean pointsOnly() {
return this.pointsOnly; return this.pointsOnly;
} }
public void setPointsOnly(boolean pointsOnly) { public void setPointsOnly(boolean pointsOnly) {
this.pointsOnly = pointsOnly; this.pointsOnly = pointsOnly;
} }
} }

View file

@ -6,19 +6,18 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.xcontent.ParseField;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.unit.DistanceUnit.Distance; import org.elasticsearch.common.unit.DistanceUnit.Distance;
import org.elasticsearch.common.xcontent.ParseField;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Circle;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -154,7 +153,7 @@ public class CircleBuilder extends ShapeBuilder<Circle, org.elasticsearch.geomet
@Override @Override
public org.elasticsearch.geometry.Circle buildGeometry() { public org.elasticsearch.geometry.Circle buildGeometry() {
return new org.elasticsearch.geometry.Circle(center.x, center.y, unit.toMeters(radius)); return new org.elasticsearch.geometry.Circle(center.x, center.y, unit.toMeters(radius));
} }
@Override @Override
@ -185,8 +184,8 @@ public class CircleBuilder extends ShapeBuilder<Circle, org.elasticsearch.geomet
return false; return false;
} }
CircleBuilder other = (CircleBuilder) obj; CircleBuilder other = (CircleBuilder) obj;
return Objects.equals(center, other.center) && return Objects.equals(center, other.center)
Objects.equals(radius, other.radius) && && Objects.equals(radius, other.radius)
Objects.equals(unit.ordinal(), other.unit.ordinal()); && Objects.equals(unit.ordinal(), other.unit.ordinal());
} }
} }

View file

@ -6,10 +6,10 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.locationtech.jts.geom.Coordinate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -34,9 +34,12 @@ public class CoordinatesBuilder {
int expectedDims; int expectedDims;
int actualDims; int actualDims;
if (points.isEmpty() == false if (points.isEmpty() == false
&& (expectedDims = Double.isNaN(points.get(0).z) ? 2 : 3) != (actualDims = Double.isNaN(coordinate.z) ? 2 : 3)) { && (expectedDims = Double.isNaN(points.get(0).z) ? 2 : 3) != (actualDims = Double.isNaN(coordinate.z) ? 2 : 3)) {
throw new ElasticsearchException("unable to add coordinate to CoordinateBuilder: " + throw new ElasticsearchException(
"coordinate dimensions do not match. Expected [{}] but found [{}]", expectedDims, actualDims); "unable to add coordinate to CoordinateBuilder: " + "coordinate dimensions do not match. Expected [{}] but found [{}]",
expectedDims,
actualDims
);
} else { } else {
this.points.add(coordinate); this.points.add(coordinate);
@ -60,7 +63,7 @@ public class CoordinatesBuilder {
* @param coordinates array of {@link Coordinate}s to add * @param coordinates array of {@link Coordinate}s to add
* @return this * @return this
*/ */
public CoordinatesBuilder coordinates(Coordinate...coordinates) { public CoordinatesBuilder coordinates(Coordinate... coordinates) {
return this.coordinates(Arrays.asList(coordinates)); return this.coordinates(Arrays.asList(coordinates));
} }
@ -81,8 +84,8 @@ public class CoordinatesBuilder {
*/ */
public CoordinatesBuilder close() { public CoordinatesBuilder close() {
Coordinate start = points.get(0); Coordinate start = points.get(0);
Coordinate end = points.get(points.size()-1); Coordinate end = points.get(points.size() - 1);
if(start.x != end.x || start.y != end.y) { if (start.x != end.x || start.y != end.y) {
points.add(start); points.add(start);
} }
return this; return this;

View file

@ -6,17 +6,16 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Rectangle;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -130,7 +129,6 @@ public class EnvelopeBuilder extends ShapeBuilder<Rectangle, org.elasticsearch.g
return false; return false;
} }
EnvelopeBuilder other = (EnvelopeBuilder) obj; EnvelopeBuilder other = (EnvelopeBuilder) obj;
return Objects.equals(topLeft, other.topLeft) && return Objects.equals(topLeft, other.topLeft) && Objects.equals(bottomRight, other.bottomRight);
Objects.equals(bottomRight, other.bottomRight);
} }
} }

View file

@ -6,18 +6,18 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.XShapeCollection;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection; import org.elasticsearch.geometry.GeometryCollection;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.XShapeCollection;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
import java.io.IOException; import java.io.IOException;
@ -25,8 +25,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public class GeometryCollectionBuilder extends ShapeBuilder<Shape, public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryCollection<Geometry>, GeometryCollectionBuilder> {
GeometryCollection<Geometry>, GeometryCollectionBuilder> {
public static final GeoShapeType TYPE = GeoShapeType.GEOMETRYCOLLECTION; public static final GeoShapeType TYPE = GeoShapeType.GEOMETRYCOLLECTION;
@ -38,8 +37,7 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape,
/** /**
* Build and empty GeometryCollectionBuilder. * Build and empty GeometryCollectionBuilder.
*/ */
public GeometryCollectionBuilder() { public GeometryCollectionBuilder() {}
}
/** /**
* Read from a stream. * Read from a stream.
@ -106,8 +104,9 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape,
public ShapeBuilder<?, ?, ?> getShapeAt(int i) { public ShapeBuilder<?, ?, ?> getShapeAt(int i) {
if (i >= this.shapes.size() || i < 0) { if (i >= this.shapes.size() || i < 0) {
throw new ElasticsearchException("GeometryCollection contains " + this.shapes.size() + " shapes. + " + throw new ElasticsearchException(
"No shape found at index " + i); "GeometryCollection contains " + this.shapes.size() + " shapes. + " + "No shape found at index " + i
);
} }
return this.shapes.get(i); return this.shapes.get(i);
} }
@ -153,8 +152,7 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape,
@Override @Override
public int numDimensions() { public int numDimensions() {
if (shapes == null || shapes.isEmpty()) { if (shapes == null || shapes.isEmpty()) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "GeometryCollection has not yet been initialized");
"GeometryCollection has not yet been initialized");
} }
return shapes.get(0).numDimensions(); return shapes.get(0).numDimensions();
} }
@ -168,11 +166,9 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape,
shapes.add(shape.buildS4J()); shapes.add(shape.buildS4J());
} }
if (shapes.size() == 1) if (shapes.size() == 1) return shapes.get(0);
return shapes.get(0); else return new XShapeCollection<>(shapes, SPATIAL_CONTEXT);
else // note: ShapeCollection is probably faster than a Multi* geom.
return new XShapeCollection<>(shapes, SPATIAL_CONTEXT);
//note: ShapeCollection is probably faster than a Multi* geom.
} }
@Override @Override

View file

@ -6,13 +6,13 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.Line; import org.elasticsearch.geometry.Line;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.GeometryFactory;
@ -37,7 +37,9 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
public LineStringBuilder(List<Coordinate> coordinates) { public LineStringBuilder(List<Coordinate> coordinates) {
super(coordinates); super(coordinates);
if (coordinates.size() < 2) { if (coordinates.size() < 2) {
throw new IllegalArgumentException("invalid number of points in LineString (found [" + coordinates.size()+ "] - must be >= 2)"); throw new IllegalArgumentException(
"invalid number of points in LineString (found [" + coordinates.size() + "] - must be >= 2)"
);
} }
} }
@ -69,7 +71,7 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
public LineStringBuilder close() { public LineStringBuilder close() {
Coordinate start = coordinates.get(0); Coordinate start = coordinates.get(0);
Coordinate end = coordinates.get(coordinates.size() - 1); Coordinate end = coordinates.get(coordinates.size() - 1);
if(start.x != end.x || start.y != end.y) { if (start.x != end.x || start.y != end.y) {
coordinates.add(start); coordinates.add(start);
} }
return this; return this;
@ -83,8 +85,7 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
@Override @Override
public int numDimensions() { public int numDimensions() {
if (coordinates == null || coordinates.isEmpty()) { if (coordinates == null || coordinates.isEmpty()) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "LineString has not yet been initialized");
"LineString has not yet been initialized");
} }
return Double.isNaN(coordinates.get(0).z) ? 2 : 3; return Double.isNaN(coordinates.get(0).z) ? 2 : 3;
} }
@ -93,10 +94,10 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
public JtsGeometry buildS4J() { public JtsGeometry buildS4J() {
Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]); Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]);
Geometry geometry; Geometry geometry;
if(wrapdateline) { if (wrapdateline) {
ArrayList<LineString> strings = decomposeS4J(FACTORY, coordinates, new ArrayList<LineString>()); ArrayList<LineString> strings = decomposeS4J(FACTORY, coordinates, new ArrayList<LineString>());
if(strings.size() == 1) { if (strings.size() == 1) {
geometry = strings.get(0); geometry = strings.get(0);
} else { } else {
LineString[] linestrings = strings.toArray(new LineString[strings.size()]); LineString[] linestrings = strings.toArray(new LineString[strings.size()]);
@ -111,13 +112,12 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
@Override @Override
public org.elasticsearch.geometry.Geometry buildGeometry() { public org.elasticsearch.geometry.Geometry buildGeometry() {
return new Line(coordinates.stream().mapToDouble(i->i.x).toArray(), coordinates.stream().mapToDouble(i->i.y).toArray() return new Line(coordinates.stream().mapToDouble(i -> i.x).toArray(), coordinates.stream().mapToDouble(i -> i.y).toArray());
);
} }
static ArrayList<LineString> decomposeS4J(GeometryFactory factory, Coordinate[] coordinates, ArrayList<LineString> strings) { static ArrayList<LineString> decomposeS4J(GeometryFactory factory, Coordinate[] coordinates, ArrayList<LineString> strings) {
for(Coordinate[] part : decompose(+DATELINE, coordinates)) { for (Coordinate[] part : decompose(+DATELINE, coordinates)) {
for(Coordinate[] line : decompose(-DATELINE, part)) { for (Coordinate[] line : decompose(-DATELINE, part)) {
strings.add(factory.createLineString(line)); strings.add(factory.createLineString(line));
} }
} }
@ -138,35 +138,35 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasticsear
double shift = coordinates[0].x > DATELINE ? DATELINE : (coordinates[0].x < -DATELINE ? -DATELINE : 0); double shift = coordinates[0].x > DATELINE ? DATELINE : (coordinates[0].x < -DATELINE ? -DATELINE : 0);
for (int i = 1; i < coordinates.length; i++) { for (int i = 1; i < coordinates.length; i++) {
double t = intersection(coordinates[i-1], coordinates[i], dateline); double t = intersection(coordinates[i - 1], coordinates[i], dateline);
if(Double.isNaN(t) == false) { if (Double.isNaN(t) == false) {
Coordinate[] part; Coordinate[] part;
if(t<1) { if (t < 1) {
part = Arrays.copyOfRange(coordinates, offset, i+1); part = Arrays.copyOfRange(coordinates, offset, i + 1);
part[part.length-1] = Edge.position(coordinates[i-1], coordinates[i], t); part[part.length - 1] = Edge.position(coordinates[i - 1], coordinates[i], t);
coordinates[offset+i-1] = Edge.position(coordinates[i-1], coordinates[i], t); coordinates[offset + i - 1] = Edge.position(coordinates[i - 1], coordinates[i], t);
shift(shift, part); shift(shift, part);
offset = i-1; offset = i - 1;
shift = coordinates[i].x > DATELINE ? DATELINE : (coordinates[i].x < -DATELINE ? -DATELINE : 0); shift = coordinates[i].x > DATELINE ? DATELINE : (coordinates[i].x < -DATELINE ? -DATELINE : 0);
} else { } else {
part = shift(shift, Arrays.copyOfRange(coordinates, offset, i+1)); part = shift(shift, Arrays.copyOfRange(coordinates, offset, i + 1));
offset = i; offset = i;
} }
parts.add(part); parts.add(part);
} }
} }
if(offset == 0) { if (offset == 0) {
parts.add(shift(shift, coordinates)); parts.add(shift(shift, coordinates));
} else if(offset < coordinates.length-1) { } else if (offset < coordinates.length - 1) {
Coordinate[] part = Arrays.copyOfRange(coordinates, offset, coordinates.length); Coordinate[] part = Arrays.copyOfRange(coordinates, offset, coordinates.length);
parts.add(shift(shift, part)); parts.add(shift(shift, part));
} }
return parts.toArray(new Coordinate[parts.size()][]); return parts.toArray(new Coordinate[parts.size()][]);
} }
private static Coordinate[] shift(double shift, Coordinate...coordinates) { private static Coordinate[] shift(double shift, Coordinate... coordinates) {
if(shift != 0) { if (shift != 0) {
for (int j = 0; j < coordinates.length; j++) { for (int j = 0; j < coordinates.length; j++) {
coordinates[j] = new Coordinate(coordinates[j].x - 2 * shift, coordinates[j].y); coordinates[j] = new Coordinate(coordinates[j].x - 2 * shift, coordinates[j].y);
} }

View file

@ -6,16 +6,16 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.Line; import org.elasticsearch.geometry.Line;
import org.elasticsearch.geometry.MultiLine; import org.elasticsearch.geometry.MultiLine;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LineString;
@ -94,8 +94,7 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasti
public int numDimensions() { public int numDimensions() {
if (lines == null || lines.isEmpty()) { if (lines == null || lines.isEmpty()) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "LineStrings have not yet been initialized");
"LineStrings have not yet been initialized");
} }
return lines.get(0).numDimensions(); return lines.get(0).numDimensions();
} }
@ -106,7 +105,7 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasti
builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName()); builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName());
builder.field(ShapeParser.FIELD_COORDINATES.getPreferredName()); builder.field(ShapeParser.FIELD_COORDINATES.getPreferredName());
builder.startArray(); builder.startArray();
for(LineStringBuilder line : lines) { for (LineStringBuilder line : lines) {
line.coordinatesToXcontent(builder, false); line.coordinatesToXcontent(builder, false);
} }
builder.endArray(); builder.endArray();
@ -117,12 +116,12 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasti
@Override @Override
public JtsGeometry buildS4J() { public JtsGeometry buildS4J() {
final Geometry geometry; final Geometry geometry;
if(wrapdateline) { if (wrapdateline) {
ArrayList<LineString> parts = new ArrayList<>(); ArrayList<LineString> parts = new ArrayList<>();
for (LineStringBuilder line : lines) { for (LineStringBuilder line : lines) {
LineStringBuilder.decomposeS4J(FACTORY, line.coordinates(false), parts); LineStringBuilder.decomposeS4J(FACTORY, line.coordinates(false), parts);
} }
if(parts.size() == 1) { if (parts.size() == 1) {
geometry = parts.get(0); geometry = parts.get(0);
} else { } else {
LineString[] lineStrings = parts.toArray(new LineString[parts.size()]); LineString[] lineStrings = parts.toArray(new LineString[parts.size()]);
@ -147,9 +146,9 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, org.elasti
List<Line> linestrings = new ArrayList<>(lines.size()); List<Line> linestrings = new ArrayList<>(lines.size());
for (int i = 0; i < lines.size(); ++i) { for (int i = 0; i < lines.size(); ++i) {
LineStringBuilder lsb = lines.get(i); LineStringBuilder lsb = lines.get(i);
linestrings.add(new Line(lsb.coordinates.stream().mapToDouble(c->c.x).toArray(), linestrings.add(
lsb.coordinates.stream().mapToDouble(c->c.y).toArray() new Line(lsb.coordinates.stream().mapToDouble(c -> c.x).toArray(), lsb.coordinates.stream().mapToDouble(c -> c.y).toArray())
)); );
} }
return new MultiLine(linestrings); return new MultiLine(linestrings);
} }

View file

@ -6,14 +6,14 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.XShapeCollection;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.XShapeCollection;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
@ -60,8 +60,8 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
@Override @Override
public XShapeCollection<Point> buildS4J() { public XShapeCollection<Point> buildS4J() {
//Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate() // Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate()
//MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()])); // MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()]));
List<Point> shapes = new ArrayList<>(coordinates.size()); List<Point> shapes = new ArrayList<>(coordinates.size());
for (Coordinate coord : coordinates) { for (Coordinate coord : coordinates) {
shapes.add(SPATIAL_CONTEXT.makePoint(coord.x, coord.y)); shapes.add(SPATIAL_CONTEXT.makePoint(coord.x, coord.y));
@ -76,8 +76,9 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
if (coordinates.isEmpty()) { if (coordinates.isEmpty()) {
return MultiPoint.EMPTY; return MultiPoint.EMPTY;
} }
return new MultiPoint(coordinates.stream().map(coord -> new org.elasticsearch.geometry.Point(coord.x, coord.y)) return new MultiPoint(
.collect(Collectors.toList())); coordinates.stream().map(coord -> new org.elasticsearch.geometry.Point(coord.x, coord.y)).collect(Collectors.toList())
);
} }
@Override @Override
@ -88,8 +89,7 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
@Override @Override
public int numDimensions() { public int numDimensions() {
if (coordinates == null || coordinates.isEmpty()) { if (coordinates == null || coordinates.isEmpty()) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "LineString has not yet been initialized");
"LineString has not yet been initialized");
} }
return Double.isNaN(coordinates.get(0).z) ? 2 : 3; return Double.isNaN(coordinates.get(0).z) ? 2 : 3;
} }

View file

@ -6,18 +6,18 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.XShapeCollection;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.geometry.MultiPolygon; import org.elasticsearch.geometry.MultiPolygon;
import org.elasticsearch.geometry.Polygon; import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.XShapeCollection;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
@ -130,7 +130,7 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName()); builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName());
builder.field(ShapeParser.FIELD_ORIENTATION.getPreferredName(), orientation.name().toLowerCase(Locale.ROOT)); builder.field(ShapeParser.FIELD_ORIENTATION.getPreferredName(), orientation.name().toLowerCase(Locale.ROOT));
builder.startArray(ShapeParser.FIELD_COORDINATES.getPreferredName()); builder.startArray(ShapeParser.FIELD_COORDINATES.getPreferredName());
for(PolygonBuilder polygon : polygons) { for (PolygonBuilder polygon : polygons) {
builder.startArray(); builder.startArray();
polygon.coordinatesArray(builder, params); polygon.coordinatesArray(builder, params);
builder.endArray(); builder.endArray();
@ -147,8 +147,7 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
@Override @Override
public int numDimensions() { public int numDimensions() {
if (polygons == null || polygons.isEmpty()) { if (polygons == null || polygons.isEmpty()) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "Polygons have not yet been initialized");
"Polygons have not yet been initialized");
} }
return polygons.get(0).numDimensions(); return polygons.get(0).numDimensions();
} }
@ -158,9 +157,9 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
List<Shape> shapes = new ArrayList<>(this.polygons.size()); List<Shape> shapes = new ArrayList<>(this.polygons.size());
if(wrapdateline) { if (wrapdateline) {
for (PolygonBuilder polygon : this.polygons) { for (PolygonBuilder polygon : this.polygons) {
for(Coordinate[][] part : polygon.coordinates()) { for (Coordinate[][] part : polygon.coordinates()) {
shapes.add(jtsGeometry(PolygonBuilder.polygonS4J(FACTORY, part))); shapes.add(jtsGeometry(PolygonBuilder.polygonS4J(FACTORY, part)));
} }
} }
@ -169,14 +168,12 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
shapes.add(jtsGeometry(polygon.toPolygonS4J(FACTORY))); shapes.add(jtsGeometry(polygon.toPolygonS4J(FACTORY)));
} }
} }
if (shapes.size() == 1) if (shapes.size() == 1) return shapes.get(0);
return shapes.get(0); else return new XShapeCollection<>(shapes, SPATIAL_CONTEXT);
else // note: ShapeCollection is probably faster than a Multi* geom.
return new XShapeCollection<>(shapes, SPATIAL_CONTEXT);
//note: ShapeCollection is probably faster than a Multi* geom.
} }
@SuppressWarnings({"unchecked"}) @SuppressWarnings({ "unchecked" })
@Override @Override
public MultiPolygon buildGeometry() { public MultiPolygon buildGeometry() {
List<Polygon> shapes = new ArrayList<>(this.polygons.size()); List<Polygon> shapes = new ArrayList<>(this.polygons.size());
@ -186,7 +183,7 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
if (poly instanceof List) { if (poly instanceof List) {
shapes.addAll((List<Polygon>) poly); shapes.addAll((List<Polygon>) poly);
} else { } else {
shapes.add((Polygon)poly); shapes.add((Polygon) poly);
} }
} }
if (shapes.isEmpty()) { if (shapes.isEmpty()) {
@ -209,7 +206,6 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygon, Multi
return false; return false;
} }
MultiPolygonBuilder other = (MultiPolygonBuilder) obj; MultiPolygonBuilder other = (MultiPolygonBuilder) obj;
return Objects.equals(polygons, other.polygons) && return Objects.equals(polygons, other.polygons) && Objects.equals(orientation, other.orientation);
Objects.equals(orientation, other.orientation);
} }
} }

View file

@ -6,12 +6,12 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
@ -29,7 +29,7 @@ public class PointBuilder extends ShapeBuilder<Point, org.elasticsearch.geometry
} }
public PointBuilder(double lon, double lat) { public PointBuilder(double lon, double lat) {
//super(new ArrayList<>(1)); // super(new ArrayList<>(1));
super(); super();
this.coordinates.add(new Coordinate(lon, lat)); this.coordinates.add(new Coordinate(lon, lat));
} }
@ -64,11 +64,11 @@ public class PointBuilder extends ShapeBuilder<Point, org.elasticsearch.geometry
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); builder.startObject();
builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName()); builder.field(ShapeParser.FIELD_TYPE.getPreferredName(), TYPE.shapeName());
builder.field(ShapeParser.FIELD_COORDINATES.getPreferredName()); builder.field(ShapeParser.FIELD_COORDINATES.getPreferredName());
toXContent(builder, coordinates.get(0)); toXContent(builder, coordinates.get(0));
return builder.endObject(); return builder.endObject();
} }
@Override @Override

View file

@ -6,16 +6,16 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.GeometryFactory;
@ -157,12 +157,11 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
*/ */
List<Coordinate> points = lineString.coordinates; List<Coordinate> points = lineString.coordinates;
if (points.size() < 4) { if (points.size() < 4) {
throw new IllegalArgumentException( throw new IllegalArgumentException("invalid number of points in LinearRing (found [" + points.size() + "] - must be >= 4)");
"invalid number of points in LinearRing (found [" + points.size() + "] - must be >= 4)");
} }
if (points.get(0).equals(points.get(points.size() - 1)) == false) { if (points.get(0).equals(points.get(points.size() - 1)) == false) {
throw new IllegalArgumentException("invalid LinearRing found (coordinates are not closed)"); throw new IllegalArgumentException("invalid LinearRing found (coordinates are not closed)");
} }
} }
@ -203,7 +202,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
final AtomicBoolean translated = new AtomicBoolean(false); final AtomicBoolean translated = new AtomicBoolean(false);
int offset = createEdges(0, orientation, shell, null, edges, 0, translated); int offset = createEdges(0, orientation, shell, null, edges, 0, translated);
for (int i = 0; i < holes.length; i++) { for (int i = 0; i < holes.length; i++) {
int length = createEdges(i+1, orientation, shell, holes[i], edges, offset, translated); int length = createEdges(i + 1, orientation, shell, holes[i], edges, offset, translated);
holeComponents[i] = edges[offset]; holeComponents[i] = edges[offset];
offset += length; offset += length;
} }
@ -230,9 +229,10 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// same point // same point
continue; continue;
} }
if (linearRing.coordinates.get(i - 1).x == linearRing.coordinates.get(i + 1).x && if (linearRing.coordinates.get(i - 1).x == linearRing.coordinates.get(i + 1).x
linearRing.coordinates.get(i - 1).y > linearRing.coordinates.get(i).y != && linearRing.coordinates.get(i - 1).y > linearRing.coordinates.get(i).y != linearRing.coordinates.get(
linearRing.coordinates.get(i + 1).y > linearRing.coordinates.get(i).y) { i + 1
).y > linearRing.coordinates.get(i).y) {
// coplanar // coplanar
continue; continue;
} }
@ -255,7 +255,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
protected XContentBuilder coordinatesArray(XContentBuilder builder, Params params) throws IOException { protected XContentBuilder coordinatesArray(XContentBuilder builder, Params params) throws IOException {
shell.coordinatesToXcontent(builder, true); shell.coordinatesToXcontent(builder, true);
for(LineStringBuilder hole : holes) { for (LineStringBuilder hole : holes) {
hole.coordinatesToXcontent(builder, true); hole.coordinatesToXcontent(builder, true);
} }
return builder; return builder;
@ -274,11 +274,9 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
} }
public Geometry buildS4JGeometry(GeometryFactory factory, boolean fixDateline) { public Geometry buildS4JGeometry(GeometryFactory factory, boolean fixDateline) {
if(fixDateline) { if (fixDateline) {
Coordinate[][][] polygons = coordinates(); Coordinate[][][] polygons = coordinates();
return polygons.length == 1 return polygons.length == 1 ? polygonS4J(factory, polygons[0]) : multipolygonS4J(factory, polygons);
? polygonS4J(factory, polygons[0])
: multipolygonS4J(factory, polygons);
} else { } else {
return toPolygonS4J(factory); return toPolygonS4J(factory);
} }
@ -307,7 +305,8 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
} }
protected static org.elasticsearch.geometry.LinearRing linearRing(List<Coordinate> coordinates) { protected static org.elasticsearch.geometry.LinearRing linearRing(List<Coordinate> coordinates) {
return new org.elasticsearch.geometry.LinearRing(coordinates.stream().mapToDouble(i -> i.x).toArray(), return new org.elasticsearch.geometry.LinearRing(
coordinates.stream().mapToDouble(i -> i.x).toArray(),
coordinates.stream().mapToDouble(i -> i.y).toArray() coordinates.stream().mapToDouble(i -> i.y).toArray()
); );
} }
@ -324,8 +323,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
@Override @Override
public int numDimensions() { public int numDimensions() {
if (shell == null) { if (shell == null) {
throw new IllegalStateException("unable to get number of dimensions, " + throw new IllegalStateException("unable to get number of dimensions, " + "Polygon has not yet been initialized");
"Polygon has not yet been initialized");
} }
return shell.numDimensions(); return shell.numDimensions();
} }
@ -334,10 +332,10 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
LinearRing shell = factory.createLinearRing(polygon[0]); LinearRing shell = factory.createLinearRing(polygon[0]);
LinearRing[] holes; LinearRing[] holes;
if(polygon.length > 1) { if (polygon.length > 1) {
holes = new LinearRing[polygon.length-1]; holes = new LinearRing[polygon.length - 1];
for (int i = 0; i < holes.length; i++) { for (int i = 0; i < holes.length; i++) {
holes[i] = factory.createLinearRing(polygon[i+1]); holes[i] = factory.createLinearRing(polygon[i + 1]);
} }
} else { } else {
holes = null; holes = null;
@ -375,8 +373,8 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
private static int component(final Edge edge, final int id, final ArrayList<Edge> edges, double[] partitionPoint) { private static int component(final Edge edge, final int id, final ArrayList<Edge> edges, double[] partitionPoint) {
// find a coordinate that is not part of the dateline // find a coordinate that is not part of the dateline
Edge any = edge; Edge any = edge;
while(any.coordinate.x == +DATELINE || any.coordinate.x == -DATELINE) { while (any.coordinate.x == +DATELINE || any.coordinate.x == -DATELINE) {
if((any = any.next) == edge) { if ((any = any.next) == edge) {
break; break;
} }
} }
@ -437,9 +435,9 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
prev = current; prev = current;
} }
length++; length++;
} while(connectedComponents == 0 && (current = current.next) != edge); } while (connectedComponents == 0 && (current = current.next) != edge);
return (splitIndex != 1) ? length-splitIndex: length; return (splitIndex != 1) ? length - splitIndex : length;
} }
/** /**
@ -455,11 +453,11 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// First and last coordinates must be equal // First and last coordinates must be equal
if (coordinates[0].equals(coordinates[coordinates.length - 1]) == false) { if (coordinates[0].equals(coordinates[coordinates.length - 1]) == false) {
if (Double.isNaN(partitionPoint[2])) { if (Double.isNaN(partitionPoint[2])) {
throw new InvalidShapeException("Self-intersection at or near point [" throw new InvalidShapeException("Self-intersection at or near point [" + partitionPoint[0] + "," + partitionPoint[1] + "]");
+ partitionPoint[0] + "," + partitionPoint[1] + "]");
} else { } else {
throw new InvalidShapeException("Self-intersection at or near point [" throw new InvalidShapeException(
+ partitionPoint[0] + "," + partitionPoint[1] + "," + partitionPoint[2] + "]"); "Self-intersection at or near point [" + partitionPoint[0] + "," + partitionPoint[1] + "," + partitionPoint[2] + "]"
);
} }
} }
return coordinates; return coordinates;
@ -472,7 +470,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
result[i] = component.toArray(new Coordinate[component.size()][]); result[i] = component.toArray(new Coordinate[component.size()][]);
} }
if(debugEnabled()) { if (debugEnabled()) {
for (int i = 0; i < result.length; i++) { for (int i = 0; i < result.length; i++) {
LOGGER.debug("Component [{}]:", i); LOGGER.debug("Component [{}]:", i);
for (int j = 0; j < result[i].length; j++) { for (int j = 0; j < result[i].length; j++) {
@ -491,9 +489,9 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
final Coordinate[][] points = new Coordinate[numHoles][]; final Coordinate[][] points = new Coordinate[numHoles][];
for (int i = 0; i < numHoles; i++) { for (int i = 0; i < numHoles; i++) {
double[] partitionPoint = new double[3]; double[] partitionPoint = new double[3];
int length = component(holes[i], -(i+1), null, partitionPoint); // mark as visited by inverting the sign int length = component(holes[i], -(i + 1), null, partitionPoint); // mark as visited by inverting the sign
points[i] = coordinates(holes[i], new Coordinate[length+1], partitionPoint); points[i] = coordinates(holes[i], new Coordinate[length + 1], partitionPoint);
} }
return points; return points;
@ -504,10 +502,10 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
for (int i = 0; i < edges.length; i++) { for (int i = 0; i < edges.length; i++) {
if (edges[i].component >= 0) { if (edges[i].component >= 0) {
double[] partitionPoint = new double[3]; double[] partitionPoint = new double[3];
int length = component(edges[i], -(components.size()+numHoles+1), mainEdges, partitionPoint); int length = component(edges[i], -(components.size() + numHoles + 1), mainEdges, partitionPoint);
List<Coordinate[]> component = new ArrayList<>(); List<Coordinate[]> component = new ArrayList<>();
component.add(coordinates(edges[i], new Coordinate[length+1], partitionPoint)); component.add(coordinates(edges[i], new Coordinate[length + 1], partitionPoint));
components.add(component); components.add(component);
} }
} }
@ -583,7 +581,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
final int component = -edges[index].component - numHoles - 1; final int component = -edges[index].component - numHoles - 1;
if(debugEnabled()) { if (debugEnabled()) {
LOGGER.debug("\tposition ({}) of edge {}: {}", index, current, edges[index]); LOGGER.debug("\tposition ({}) of edge {}: {}", index, current, edges[index]);
LOGGER.debug("\tComponent: {}", component); LOGGER.debug("\tComponent: {}", component);
LOGGER.debug("\tHole intersections ({}): {}", current.coordinate.x, Arrays.toString(edges)); LOGGER.debug("\tHole intersections ({}): {}", current.coordinate.x, Arrays.toString(edges));
@ -608,14 +606,14 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// the second edge only (the first edge is either polygon or // the second edge only (the first edge is either polygon or
// already handled) // already handled)
if (e2.component > 0) { if (e2.component > 0) {
//TODO: Check if we could save the set null step // TODO: Check if we could save the set null step
numHoles--; numHoles--;
holes[e2.component-1] = holes[numHoles]; holes[e2.component - 1] = holes[numHoles];
holes[numHoles] = null; holes[numHoles] = null;
} }
// only connect edges if intersections are pairwise // only connect edges if intersections are pairwise
// 1. per the comment above, the edge array is sorted by y-value of the intersection // 1. per the comment above, the edge array is sorted by y-value of the intersection
// with the dateline. Two edges have the same y intercept when they cross the // with the dateline. Two edges have the same y intercept when they cross the
// dateline thus they appear sequentially (pairwise) in the edge array. Two edges // dateline thus they appear sequentially (pairwise) in the edge array. Two edges
// do not have the same y intercept when we're forming a multi-poly from a poly // do not have the same y intercept when we're forming a multi-poly from a poly
// that wraps the dateline (but there are 2 ordered intercepts). // that wraps the dateline (but there are 2 ordered intercepts).
@ -623,12 +621,14 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// For boundary conditions (e.g., intersect but not crossing) there is no sibling edge // For boundary conditions (e.g., intersect but not crossing) there is no sibling edge
// to connect. Thus the first logic check enforces the pairwise rule // to connect. Thus the first logic check enforces the pairwise rule
// 2. the second logic check ensures the two candidate edges aren't already connected by an // 2. the second logic check ensures the two candidate edges aren't already connected by an
// existing edge along the dateline - this is necessary due to a logic change in // existing edge along the dateline - this is necessary due to a logic change in
// ShapeBuilder.intersection that computes dateline edges as valid intersect points // ShapeBuilder.intersection that computes dateline edges as valid intersect points
// in support of OGC standards // in support of OGC standards
if (e1.intersect != Edge.MAX_COORDINATE && e2.intersect != Edge.MAX_COORDINATE if (e1.intersect != Edge.MAX_COORDINATE
&& (e1.next.next.coordinate.equals3D(e2.coordinate) && Math.abs(e1.next.coordinate.x) == DATELINE && e2.intersect != Edge.MAX_COORDINATE
&& Math.abs(e2.coordinate.x) == DATELINE) == false ) { && (e1.next.next.coordinate.equals3D(e2.coordinate)
&& Math.abs(e1.next.coordinate.x) == DATELINE
&& Math.abs(e2.coordinate.x) == DATELINE) == false) {
connect(e1, e2); connect(e1, e2);
} }
} }
@ -641,12 +641,12 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// Connecting two Edges by inserting the point at // Connecting two Edges by inserting the point at
// dateline intersection and connect these by adding // dateline intersection and connect these by adding
// two edges between this points. One per direction // two edges between this points. One per direction
if(in.intersect != in.next.coordinate) { if (in.intersect != in.next.coordinate) {
// NOTE: the order of the object creation is crucial here! Don't change it! // NOTE: the order of the object creation is crucial here! Don't change it!
// first edge has no point on dateline // first edge has no point on dateline
Edge e1 = new Edge(in.intersect, in.next); Edge e1 = new Edge(in.intersect, in.next);
if(out.intersect != out.next.coordinate) { if (out.intersect != out.next.coordinate) {
// second edge has no point on dateline // second edge has no point on dateline
Edge e2 = new Edge(out.intersect, out.next); Edge e2 = new Edge(out.intersect, out.next);
in.next = new Edge(in.intersect, e2, in.intersect); in.next = new Edge(in.intersect, e2, in.intersect);
@ -659,7 +659,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// first edge intersects with dateline // first edge intersects with dateline
Edge e2 = new Edge(out.intersect, in.next, out.intersect); Edge e2 = new Edge(out.intersect, in.next, out.intersect);
if(out.intersect != out.next.coordinate) { if (out.intersect != out.next.coordinate) {
// second edge has no point on dateline // second edge has no point on dateline
Edge e1 = new Edge(out.intersect, out.next); Edge e1 = new Edge(out.intersect, out.next);
in.next = new Edge(in.intersect, e1, in.intersect); in.next = new Edge(in.intersect, e1, in.intersect);
@ -672,15 +672,22 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
} }
} }
private static int createEdges(int component, Orientation orientation, LineStringBuilder shell, private static int createEdges(
LineStringBuilder hole, Edge[] edges, int offset, final AtomicBoolean translated) { int component,
Orientation orientation,
LineStringBuilder shell,
LineStringBuilder hole,
Edge[] edges,
int offset,
final AtomicBoolean translated
) {
// inner rings (holes) have an opposite direction than the outer rings // inner rings (holes) have an opposite direction than the outer rings
// XOR will invert the orientation for outer ring cases (Truth Table:, T/T = F, T/F = T, F/T = T, F/F = F) // XOR will invert the orientation for outer ring cases (Truth Table:, T/T = F, T/F = T, F/T = T, F/F = F)
boolean direction = (component == 0 ^ orientation == Orientation.RIGHT); boolean direction = (component == 0 ^ orientation == Orientation.RIGHT);
// set the points array accordingly (shell or hole) // set the points array accordingly (shell or hole)
Coordinate[] points = (hole != null) ? hole.coordinates(false) : shell.coordinates(false); Coordinate[] points = (hole != null) ? hole.coordinates(false) : shell.coordinates(false);
ring(component, direction, orientation == Orientation.LEFT, points, 0, edges, offset, points.length-1, translated); ring(component, direction, orientation == Orientation.LEFT, points, 0, edges, offset, points.length - 1, translated);
return points.length-1; return points.length - 1;
} }
/** /**
@ -694,8 +701,17 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
* number of points * number of points
* @return Array of edges * @return Array of edges
*/ */
private static Edge[] ring(int component, boolean direction, boolean handedness, private static Edge[] ring(
Coordinate[] points, int offset, Edge[] edges, int toffset, int length, final AtomicBoolean translated) { int component,
boolean direction,
boolean handedness,
Coordinate[] points,
int offset,
Edge[] edges,
int toffset,
int length,
final AtomicBoolean translated
) {
double signedArea = 0; double signedArea = 0;
double minX = Double.POSITIVE_INFINITY; double minX = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY; double maxX = Double.NEGATIVE_INFINITY;
@ -718,11 +734,11 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
// calculate range // calculate range
final double rng = maxX - minX; final double rng = maxX - minX;
// translate the points if the following is true // translate the points if the following is true
// 1. shell orientation is cw and range is greater than a hemisphere (180 degrees) but not spanning 2 hemispheres // 1. shell orientation is cw and range is greater than a hemisphere (180 degrees) but not spanning 2 hemispheres
// (translation would result in a collapsed poly) // (translation would result in a collapsed poly)
// 2. the shell of the candidate hole has been translated (to preserve the coordinate system) // 2. the shell of the candidate hole has been translated (to preserve the coordinate system)
boolean incorrectOrientation = component == 0 && handedness != orientation; boolean incorrectOrientation = component == 0 && handedness != orientation;
if ( (incorrectOrientation && (rng > DATELINE && rng != 2*DATELINE)) || (translated.get() && component != 0)) { if ((incorrectOrientation && (rng > DATELINE && rng != 2 * DATELINE)) || (translated.get() && component != 0)) {
translate(points); translate(points);
// flip the translation bit if the shell is being translated // flip the translation bit if the shell is being translated
if (component == 0) { if (component == 0) {
@ -755,10 +771,17 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
* number of points to use * number of points to use
* @return the edges creates * @return the edges creates
*/ */
private static Edge[] concat(int component, boolean direction, Coordinate[] points, final int pointOffset, Edge[] edges, private static Edge[] concat(
final int edgeOffset, int length) { int component,
assert edges.length >= length+edgeOffset; boolean direction,
assert points.length >= length+pointOffset; Coordinate[] points,
final int pointOffset,
Edge[] edges,
final int edgeOffset,
int length
) {
assert edges.length >= length + edgeOffset;
assert points.length >= length + pointOffset;
edges[edgeOffset] = new Edge(points[pointOffset], null); edges[edgeOffset] = new Edge(points[pointOffset], null);
for (int i = 1; i < length; i++) { for (int i = 1; i < length; i++) {
if (direction) { if (direction) {
@ -789,7 +812,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
private static void translate(Coordinate[] points) { private static void translate(Coordinate[] points) {
for (Coordinate c : points) { for (Coordinate c : points) {
if (c.x < 0) { if (c.x < 0) {
c.x += 2*DATELINE; c.x += 2 * DATELINE;
} }
} }
} }
@ -821,8 +844,6 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, org.elasticsearch.
return false; return false;
} }
PolygonBuilder other = (PolygonBuilder) obj; PolygonBuilder other = (PolygonBuilder) obj;
return Objects.equals(shell, other.shell) && return Objects.equals(shell, other.shell) && Objects.equals(holes, other.holes) && Objects.equals(orientation, other.orientation);
Objects.equals(holes, other.holes) &&
Objects.equals(orientation, other.orientation);
} }
} }

View file

@ -6,24 +6,23 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.Assertions; import org.elasticsearch.Assertions;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
@ -40,8 +39,10 @@ import java.util.Objects;
/** /**
* Basic class for building GeoJSON shapes like Polygons, Linestrings, etc * Basic class for building GeoJSON shapes like Polygons, Linestrings, etc
*/ */
public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.geometry.Geometry, public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.geometry.Geometry, E extends ShapeBuilder<T, G, E>>
E extends ShapeBuilder<T, G, E>> implements NamedWriteable, ToXContentObject { implements
NamedWriteable,
ToXContentObject {
protected static final Logger LOGGER = LogManager.getLogger(ShapeBuilder.class); protected static final Logger LOGGER = LogManager.getLogger(ShapeBuilder.class);
@ -74,7 +75,7 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
/** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#validate() */ /** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#validate() */
protected static final boolean AUTO_VALIDATE_JTS_GEOMETRY = true; protected static final boolean AUTO_VALIDATE_JTS_GEOMETRY = true;
/** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#index() */ /** @see org.locationtech.spatial4j.shape.jts.JtsGeometry#index() */
protected static final boolean AUTO_INDEX_JTS_GEOMETRY = true;//may want to turn off once SpatialStrategy impls do it. protected static final boolean AUTO_INDEX_JTS_GEOMETRY = true;// may want to turn off once SpatialStrategy impls do it.
/** default ctor */ /** default ctor */
protected ShapeBuilder() { protected ShapeBuilder() {
@ -93,7 +94,7 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
protected ShapeBuilder(StreamInput in) throws IOException { protected ShapeBuilder(StreamInput in) throws IOException {
int size = in.readVInt(); int size = in.readVInt();
coordinates = new ArrayList<>(size); coordinates = new ArrayList<>(size);
for (int i=0; i < size; i++) { for (int i = 0; i < size; i++) {
coordinates.add(readFromStream(in)); coordinates.add(readFromStream(in));
} }
} }
@ -126,7 +127,7 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private E thisRef() { private E thisRef() {
return (E)this; return (E) this;
} }
/** /**
@ -155,7 +156,7 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
* @param coordinates array of {@link Coordinate}s to add * @param coordinates array of {@link Coordinate}s to add
* @return this * @return this
*/ */
public E coordinates(Coordinate...coordinates) { public E coordinates(Coordinate... coordinates) {
return this.coordinates(Arrays.asList(coordinates)); return this.coordinates(Arrays.asList(coordinates));
} }
@ -177,20 +178,18 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
* @return Array of coordinates * @return Array of coordinates
*/ */
protected Coordinate[] coordinates(boolean closed) { protected Coordinate[] coordinates(boolean closed) {
Coordinate[] result = coordinates.toArray(new Coordinate[coordinates.size() + (closed?1:0)]); Coordinate[] result = coordinates.toArray(new Coordinate[coordinates.size() + (closed ? 1 : 0)]);
if(closed) { if (closed) {
result[result.length-1] = result[0]; result[result.length - 1] = result[0];
} }
return result; return result;
} }
protected JtsGeometry jtsGeometry(Geometry geom) { protected JtsGeometry jtsGeometry(Geometry geom) {
//dateline180Check is false because ElasticSearch does it's own dateline wrapping // dateline180Check is false because ElasticSearch does it's own dateline wrapping
JtsGeometry jtsGeometry = new JtsGeometry(geom, SPATIAL_CONTEXT, false, MULTI_POLYGON_MAY_OVERLAP); JtsGeometry jtsGeometry = new JtsGeometry(geom, SPATIAL_CONTEXT, false, MULTI_POLYGON_MAY_OVERLAP);
if (AUTO_VALIDATE_JTS_GEOMETRY) if (AUTO_VALIDATE_JTS_GEOMETRY) jtsGeometry.validate();
jtsGeometry.validate(); if (AUTO_INDEX_JTS_GEOMETRY) jtsGeometry.index();
if (AUTO_INDEX_JTS_GEOMETRY)
jtsGeometry.index();
return jtsGeometry; return jtsGeometry;
} }
@ -457,13 +456,13 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
*/ */
protected XContentBuilder coordinatesToXcontent(XContentBuilder builder, boolean closed) throws IOException { protected XContentBuilder coordinatesToXcontent(XContentBuilder builder, boolean closed) throws IOException {
builder.startArray(); builder.startArray();
for(Coordinate coord : coordinates) { for (Coordinate coord : coordinates) {
toXContent(builder, coord); toXContent(builder, coord);
} }
if(closed) { if (closed) {
Coordinate start = coordinates.get(0); Coordinate start = coordinates.get(0);
Coordinate end = coordinates.get(coordinates.size()-1); Coordinate end = coordinates.get(coordinates.size() - 1);
if(start.x != end.x || start.y != end.y) { if (start.x != end.x || start.y != end.y) {
toXContent(builder, coordinates.get(0)); toXContent(builder, coordinates.get(0));
} }
} }
@ -476,7 +475,7 @@ public abstract class ShapeBuilder<T extends Shape, G extends org.elasticsearch.
if (this == o) return true; if (this == o) return true;
if ((o instanceof ShapeBuilder) == false) return false; if ((o instanceof ShapeBuilder) == false) return false;
ShapeBuilder<?,?,?> that = (ShapeBuilder<?,?,?>) o; ShapeBuilder<?, ?, ?> that = (ShapeBuilder<?, ?, ?>) o;
return Objects.equals(coordinates, that.coordinates); return Objects.equals(coordinates, that.coordinates);
} }

View file

@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.index.mapper; package org.elasticsearch.legacygeo.mapper;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy; import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
@ -20,13 +20,9 @@ import org.elasticsearch.Version;
import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.geo.GeometryFormatterFactory; import org.elasticsearch.common.geo.GeometryFormatterFactory;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.ShapesAvailability;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.XShapeCollection;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
@ -34,8 +30,20 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.core.CheckedConsumer;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.query.LegacyGeoShapeQueryProcessor; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
import org.elasticsearch.index.mapper.DocumentParserContext;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.GeoShapeQueryable;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperBuilderContext;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.legacygeo.ShapesAvailability;
import org.elasticsearch.legacygeo.XShapeCollection;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.elasticsearch.legacygeo.query.LegacyGeoShapeQueryProcessor;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.locationtech.spatial4j.shape.jts.JtsGeometry;
@ -71,15 +79,16 @@ import java.util.function.Function;
* <p> * <p>
* "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0)) * "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
* *
* @deprecated use {@link GeoShapeFieldMapper} * @deprecated use {@link org.elasticsearch.index.mapper.GeoShapeFieldMapper}
*/ */
@Deprecated @Deprecated
public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<ShapeBuilder<?, ?, ?>> { public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<ShapeBuilder<?, ?, ?>> {
public static final String CONTENT_TYPE = "geo_shape"; public static final String CONTENT_TYPE = "geo_shape";
public static final Set<String> DEPRECATED_PARAMETERS public static final Set<String> DEPRECATED_PARAMETERS = new HashSet<>(
= new HashSet<>(Arrays.asList("strategy", "tree", "tree_levels", "precision", "distance_error_pct", "points_only")); Arrays.asList("strategy", "tree", "tree_levels", "precision", "distance_error_pct", "points_only")
);
public static boolean containsDeprecatedParameter(Set<String> paramKeys) { public static boolean containsDeprecatedParameter(Set<String> paramKeys) {
return DEPRECATED_PARAMETERS.stream().anyMatch(paramKeys::contains); return DEPRECATED_PARAMETERS.stream().anyMatch(paramKeys::contains);
@ -115,8 +124,19 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
public static final String GEOHASH = "geohash"; public static final String GEOHASH = "geohash";
} }
@Deprecated
public static class DeprecatedParameters {
private static void checkPrefixTreeSupport(String fieldName) {
if (ShapesAvailability.JTS_AVAILABLE == false || ShapesAvailability.SPATIAL4J_AVAILABLE == false) {
throw new ElasticsearchParseException("Field parameter [{}] is not supported for [{}] field type", fieldName, CONTENT_TYPE);
}
}
}
private static Builder builder(FieldMapper in) { private static Builder builder(FieldMapper in) {
return ((LegacyGeoShapeFieldMapper)in).builder; return ((LegacyGeoShapeFieldMapper) in).builder;
} }
public static class Builder extends FieldMapper.Builder { public static class Builder extends FieldMapper.Builder {
@ -128,28 +148,42 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
final Parameter<Explicit<Boolean>> coerce; final Parameter<Explicit<Boolean>> coerce;
Parameter<Explicit<Orientation>> orientation = orientationParam(m -> builder(m).orientation.get()); Parameter<Explicit<Orientation>> orientation = orientationParam(m -> builder(m).orientation.get());
Parameter<SpatialStrategy> strategy = new Parameter<>("strategy", false, () -> SpatialStrategy.RECURSIVE, Parameter<SpatialStrategy> strategy = new Parameter<>(
(n, c, o) -> SpatialStrategy.fromString(o.toString(), DEPRECATION_LOGGER), m -> builder(m).strategy.get()) "strategy",
.deprecated(); false,
Parameter<String> tree = Parameter.stringParam("tree", false, m -> builder(m).tree.get(), Defaults.TREE) () -> SpatialStrategy.RECURSIVE,
.deprecated(); (n, c, o) -> SpatialStrategy.fromString(o.toString(), DEPRECATION_LOGGER),
Parameter<Integer> treeLevels = new Parameter<>("tree_levels", false, () -> null, m -> builder(m).strategy.get()
(n, c, o) -> o == null ? null : XContentMapValues.nodeIntegerValue(o), ).deprecated();
m -> builder(m).treeLevels.get()) Parameter<String> tree = Parameter.stringParam("tree", false, m -> builder(m).tree.get(), Defaults.TREE).deprecated();
.deprecated(); Parameter<Integer> treeLevels = new Parameter<>(
Parameter<DistanceUnit.Distance> precision = new Parameter<>("precision", false, () -> null, "tree_levels",
(n, c, o) -> o == null ? null : DistanceUnit.Distance.parseDistance(o.toString()), false,
m -> builder(m).precision.get())
.deprecated();
Parameter<Double> distanceErrorPct = new Parameter<>("distance_error_pct", true, () -> null,
(n, c, o) -> o == null ? null : XContentMapValues.nodeDoubleValue(o),
m -> builder(m).distanceErrorPct.get())
.deprecated()
.acceptsNull();
Parameter<Boolean> pointsOnly = new Parameter<>("points_only", false,
() -> null, () -> null,
(n, c, o) -> XContentMapValues.nodeBooleanValue(o), m -> builder(m).pointsOnly.get()) (n, c, o) -> o == null ? null : XContentMapValues.nodeIntegerValue(o),
.deprecated().acceptsNull(); m -> builder(m).treeLevels.get()
).deprecated();
Parameter<DistanceUnit.Distance> precision = new Parameter<>(
"precision",
false,
() -> null,
(n, c, o) -> o == null ? null : DistanceUnit.Distance.parseDistance(o.toString()),
m -> builder(m).precision.get()
).deprecated();
Parameter<Double> distanceErrorPct = new Parameter<>(
"distance_error_pct",
true,
() -> null,
(n, c, o) -> o == null ? null : XContentMapValues.nodeDoubleValue(o),
m -> builder(m).distanceErrorPct.get()
).deprecated().acceptsNull();
Parameter<Boolean> pointsOnly = new Parameter<>(
"points_only",
false,
() -> null,
(n, c, o) -> XContentMapValues.nodeBooleanValue(o),
m -> builder(m).pointsOnly.get()
).deprecated().acceptsNull();
Parameter<Map<String, String>> meta = Parameter.metaParam(); Parameter<Map<String, String>> meta = Parameter.metaParam();
@ -209,8 +243,20 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
@Override @Override
protected List<Parameter<?>> getParameters() { protected List<Parameter<?>> getParameters() {
return Arrays.asList(indexed, ignoreMalformed, ignoreZValue, coerce, orientation, return Arrays.asList(
strategy, tree, treeLevels, precision, distanceErrorPct, pointsOnly, meta); indexed,
ignoreMalformed,
ignoreZValue,
coerce,
orientation,
strategy,
tree,
treeLevels,
precision,
distanceErrorPct,
pointsOnly,
meta
);
} }
public Builder coerce(boolean coerce) { public Builder coerce(boolean coerce) {
@ -241,14 +287,20 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
private void setupPrefixTrees(GeoShapeFieldType ft) { private void setupPrefixTrees(GeoShapeFieldType ft) {
SpatialPrefixTree prefixTree; SpatialPrefixTree prefixTree;
if (ft.tree().equals(PrefixTrees.GEOHASH)) { if (ft.tree().equals(PrefixTrees.GEOHASH)) {
prefixTree = new GeohashPrefixTree(ShapeBuilder.SPATIAL_CONTEXT, prefixTree = new GeohashPrefixTree(
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.GEOHASH_TREE_LEVELS, true)); ShapeBuilder.SPATIAL_CONTEXT,
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.GEOHASH_TREE_LEVELS, true)
);
} else if (ft.tree().equals(PrefixTrees.LEGACY_QUADTREE)) { } else if (ft.tree().equals(PrefixTrees.LEGACY_QUADTREE)) {
prefixTree = new QuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT, prefixTree = new QuadPrefixTree(
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.QUADTREE_LEVELS, false)); ShapeBuilder.SPATIAL_CONTEXT,
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.QUADTREE_LEVELS, false)
);
} else if (ft.tree().equals(PrefixTrees.QUADTREE)) { } else if (ft.tree().equals(PrefixTrees.QUADTREE)) {
prefixTree = new PackedQuadPrefixTree(ShapeBuilder.SPATIAL_CONTEXT, prefixTree = new PackedQuadPrefixTree(
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.QUADTREE_LEVELS, false)); ShapeBuilder.SPATIAL_CONTEXT,
getLevels(ft.treeLevels(), ft.precisionInMeters(), Defaults.QUADTREE_LEVELS, false)
);
} else { } else {
throw new IllegalArgumentException("Unknown prefix tree type [" + ft.tree() + "]"); throw new IllegalArgumentException("Unknown prefix tree type [" + ft.tree() + "]");
} }
@ -271,8 +323,13 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
} }
private GeoShapeFieldType buildFieldType(LegacyGeoShapeParser parser, MapperBuilderContext context) { private GeoShapeFieldType buildFieldType(LegacyGeoShapeParser parser, MapperBuilderContext context) {
GeoShapeFieldType ft = GeoShapeFieldType ft = new GeoShapeFieldType(
new GeoShapeFieldType(context.buildFullName(name), indexed.get(), orientation.get().value(), parser, meta.get()); context.buildFullName(name),
indexed.get(),
orientation.get().value(),
parser,
meta.get()
);
setupFieldTypeDeprecatedParameters(ft); setupFieldTypeDeprecatedParameters(ft);
setupPrefixTrees(ft); setupPrefixTrees(ft);
return ft; return ft;
@ -280,8 +337,14 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
private static int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) { private static int getLevels(int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) {
if (treeLevels > 0 || precisionInMeters >= 0) { if (treeLevels > 0 || precisionInMeters >= 0) {
return Math.max(treeLevels, precisionInMeters >= 0 ? (geoHash ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters) return Math.max(
: GeoUtils.quadTreeLevelsForPrecision(precisionInMeters)) : 0); treeLevels,
precisionInMeters >= 0
? (geoHash
? GeoUtils.geoHashLevelsForPrecision(precisionInMeters)
: GeoUtils.quadTreeLevelsForPrecision(precisionInMeters))
: 0
);
} }
return defaultLevels; return defaultLevels;
} }
@ -294,9 +357,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
} }
LegacyGeoShapeParser parser = new LegacyGeoShapeParser(); LegacyGeoShapeParser parser = new LegacyGeoShapeParser();
GeoShapeFieldType ft = buildFieldType(parser, context); GeoShapeFieldType ft = buildFieldType(parser, context);
return new LegacyGeoShapeFieldMapper(name, ft, return new LegacyGeoShapeFieldMapper(name, ft, multiFieldsBuilder.build(this, context), copyTo.build(), parser, this);
multiFieldsBuilder.build(this, context), copyTo.build(),
parser, this);
} }
} }
@ -305,18 +366,18 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING.get(parserContext.getSettings()); boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING.get(parserContext.getSettings());
boolean coerceByDefault = COERCE_SETTING.get(parserContext.getSettings()); boolean coerceByDefault = COERCE_SETTING.get(parserContext.getSettings());
FieldMapper.Builder builder = new LegacyGeoShapeFieldMapper.Builder( FieldMapper.Builder builder = new LegacyGeoShapeFieldMapper.Builder(
name, name,
parserContext.indexVersionCreated(), parserContext.indexVersionCreated(),
ignoreMalformedByDefault, ignoreMalformedByDefault,
coerceByDefault); coerceByDefault
);
builder.parse(name, parserContext, node); builder.parse(name, parserContext, node);
return builder; return builder;
}; };
private static class LegacyGeoShapeParser extends Parser<ShapeBuilder<?, ?, ?>> { private static class LegacyGeoShapeParser extends Parser<ShapeBuilder<?, ?, ?>> {
private LegacyGeoShapeParser() { private LegacyGeoShapeParser() {}
}
@Override @Override
public void parse( public void parse(
@ -355,8 +416,13 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
private final LegacyGeoShapeQueryProcessor queryProcessor; private final LegacyGeoShapeQueryProcessor queryProcessor;
private GeoShapeFieldType(String name, boolean indexed, Orientation orientation, private GeoShapeFieldType(
LegacyGeoShapeParser parser, Map<String, String> meta) { String name,
boolean indexed,
Orientation orientation,
LegacyGeoShapeParser parser,
Map<String, String> meta
) {
super(name, indexed, false, false, parser, orientation, meta); super(name, indexed, false, false, parser, orientation, meta);
this.queryProcessor = new LegacyGeoShapeQueryProcessor(this); this.queryProcessor = new LegacyGeoShapeQueryProcessor(this);
} }
@ -371,8 +437,13 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
} }
@Override @Override
public Query geoShapeQuery(Geometry shape, String fieldName, SpatialStrategy strategy, ShapeRelation relation, public Query geoShapeQuery(
SearchExecutionContext context) { Geometry shape,
String fieldName,
SpatialStrategy strategy,
ShapeRelation relation,
SearchExecutionContext context
) {
return queryProcessor.geoShapeQuery(shape, fieldName, strategy, relation, context); return queryProcessor.geoShapeQuery(shape, fieldName, strategy, relation, context);
} }
@ -407,6 +478,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
public void setPointsOnly(boolean pointsOnly) { public void setPointsOnly(boolean pointsOnly) {
this.pointsOnly = pointsOnly; this.pointsOnly = pointsOnly;
} }
public int treeLevels() { public int treeLevels() {
return treeLevels; return treeLevels;
} }
@ -462,13 +534,26 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
private final Version indexCreatedVersion; private final Version indexCreatedVersion;
private final Builder builder; private final Builder builder;
public LegacyGeoShapeFieldMapper(String simpleName, MappedFieldType mappedFieldType, public LegacyGeoShapeFieldMapper(
MultiFields multiFields, CopyTo copyTo, String simpleName,
LegacyGeoShapeParser parser, MappedFieldType mappedFieldType,
Builder builder) { MultiFields multiFields,
super(simpleName, mappedFieldType, Collections.singletonMap(mappedFieldType.name(), Lucene.KEYWORD_ANALYZER), CopyTo copyTo,
builder.ignoreMalformed.get(), builder.coerce.get(), builder.ignoreZValue.get(), builder.orientation.get(), LegacyGeoShapeParser parser,
multiFields, copyTo, parser); Builder builder
) {
super(
simpleName,
mappedFieldType,
Collections.singletonMap(mappedFieldType.name(), Lucene.KEYWORD_ANALYZER),
builder.ignoreMalformed.get(),
builder.coerce.get(),
builder.ignoreZValue.get(),
builder.orientation.get(),
multiFields,
copyTo,
parser
);
this.indexCreatedVersion = builder.indexCreatedVersion; this.indexCreatedVersion = builder.indexCreatedVersion;
this.builder = builder; this.builder = builder;
} }
@ -478,7 +563,7 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
return (GeoShapeFieldType) super.fieldType(); return (GeoShapeFieldType) super.fieldType();
} }
String strategy() { public String strategy() {
return fieldType().strategy().getStrategyName(); return fieldType().strategy().getStrategyName();
} }
@ -492,15 +577,20 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
// index configured for pointsOnly // index configured for pointsOnly
if (shape instanceof XShapeCollection && ((XShapeCollection<?>) shape).pointsOnly()) { if (shape instanceof XShapeCollection && ((XShapeCollection<?>) shape).pointsOnly()) {
// MULTIPOINT data: index each point separately // MULTIPOINT data: index each point separately
@SuppressWarnings("unchecked") List<Shape> shapes = ((XShapeCollection<Shape>) shape).getShapes(); @SuppressWarnings("unchecked")
List<Shape> shapes = ((XShapeCollection<Shape>) shape).getShapes();
for (Shape s : shapes) { for (Shape s : shapes) {
context.doc().addAll(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(s))); context.doc().addAll(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(s)));
} }
return; return;
} else if (shape instanceof Point == false) { } else if (shape instanceof Point == false) {
throw new MapperParsingException("[{" + fieldType().name() + "}] is configured for points only but a " throw new MapperParsingException(
+ ((shape instanceof JtsGeometry) ? ((JtsGeometry)shape).getGeom().getGeometryType() : shape.getClass()) "[{"
+ " was found"); + fieldType().name()
+ "}] is configured for points only but a "
+ ((shape instanceof JtsGeometry) ? ((JtsGeometry) shape).getGeom().getGeometryType() : shape.getClass())
+ " was found"
);
} }
} }
context.doc().addAll(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape))); context.doc().addAll(Arrays.asList(fieldType().defaultPrefixTreeStrategy().createIndexableFields(shape)));
@ -514,16 +604,20 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
@Override @Override
public FieldMapper.Builder getMergeBuilder() { public FieldMapper.Builder getMergeBuilder() {
return new Builder(simpleName(), indexCreatedVersion, return new Builder(
builder.ignoreMalformed.getDefaultValue().value(), builder.coerce.getDefaultValue().value()).init(this); simpleName(),
indexCreatedVersion,
builder.ignoreMalformed.getDefaultValue().value(),
builder.coerce.getDefaultValue().value()
).init(this);
} }
@Override @Override
protected void checkIncomingMergeType(FieldMapper mergeWith) { protected void checkIncomingMergeType(FieldMapper mergeWith) {
if (mergeWith instanceof AbstractShapeGeometryFieldMapper<?> if (mergeWith instanceof LegacyGeoShapeFieldMapper == false && CONTENT_TYPE.equals(mergeWith.typeName())) {
&& (mergeWith instanceof LegacyGeoShapeFieldMapper) == false) { throw new IllegalArgumentException(
throw new IllegalArgumentException("mapper [" + name() "mapper [" + name() + "] of type [geo_shape] cannot change strategy from [recursive] to [BKD]"
+ "] of type [geo_shape] cannot change strategy from [recursive] to [BKD]"); );
} }
super.checkIncomingMergeType(mergeWith); super.checkIncomingMergeType(mergeWith);
} }

View file

@ -5,12 +5,12 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.parsers; package org.elasticsearch.legacygeo.parsers;
import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.locationtech.jts.geom.Coordinate;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;

View file

@ -5,19 +5,19 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.parsers; package org.elasticsearch.legacygeo.parsers;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.builders.CircleBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentSubParser; import org.elasticsearch.common.xcontent.XContentSubParser;
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.builders.CircleBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import java.io.IOException; import java.io.IOException;
@ -37,9 +37,7 @@ abstract class GeoJsonParser {
CoordinateNode coordinateNode = null; CoordinateNode coordinateNode = null;
GeometryCollectionBuilder geometryCollections = null; GeometryCollectionBuilder geometryCollections = null;
Orientation orientation = (shapeMapper == null) Orientation orientation = (shapeMapper == null) ? Orientation.RIGHT : shapeMapper.orientation();
? Orientation.RIGHT
: shapeMapper.orientation();
boolean coerce = shapeMapper != null && shapeMapper.coerce(); boolean coerce = shapeMapper != null && shapeMapper.coerce();
boolean ignoreZValue = shapeMapper == null || shapeMapper.ignoreZValue(); boolean ignoreZValue = shapeMapper == null || shapeMapper.ignoreZValue();
@ -55,8 +53,12 @@ abstract class GeoJsonParser {
subParser.nextToken(); subParser.nextToken();
final GeoShapeType type = GeoShapeType.forName(subParser.text()); final GeoShapeType type = GeoShapeType.forName(subParser.text());
if (shapeType != null && shapeType.equals(type) == false) { if (shapeType != null && shapeType.equals(type) == false) {
malformedException = ShapeParser.FIELD_TYPE + " already parsed as [" malformedException = ShapeParser.FIELD_TYPE
+ shapeType + "] cannot redefine as [" + type + "]"; + " already parsed as ["
+ shapeType
+ "] cannot redefine as ["
+ type
+ "]";
} else { } else {
shapeType = type; shapeType = type;
} }
@ -64,16 +66,14 @@ abstract class GeoJsonParser {
subParser.nextToken(); subParser.nextToken();
CoordinateNode tempNode = parseCoordinates(subParser, ignoreZValue); CoordinateNode tempNode = parseCoordinates(subParser, ignoreZValue);
if (coordinateNode != null && tempNode.numDimensions() != coordinateNode.numDimensions()) { if (coordinateNode != null && tempNode.numDimensions() != coordinateNode.numDimensions()) {
throw new ElasticsearchParseException("Exception parsing coordinates: " + throw new ElasticsearchParseException("Exception parsing coordinates: " + "number of dimensions do not match");
"number of dimensions do not match");
} }
coordinateNode = tempNode; coordinateNode = tempNode;
} else if (ShapeParser.FIELD_GEOMETRIES.match(fieldName, subParser.getDeprecationHandler())) { } else if (ShapeParser.FIELD_GEOMETRIES.match(fieldName, subParser.getDeprecationHandler())) {
if (shapeType == null) { if (shapeType == null) {
shapeType = GeoShapeType.GEOMETRYCOLLECTION; shapeType = GeoShapeType.GEOMETRYCOLLECTION;
} else if (shapeType.equals(GeoShapeType.GEOMETRYCOLLECTION) == false) { } else if (shapeType.equals(GeoShapeType.GEOMETRYCOLLECTION) == false) {
malformedException = "cannot have [" + ShapeParser.FIELD_GEOMETRIES + "] with type set to [" malformedException = "cannot have [" + ShapeParser.FIELD_GEOMETRIES + "] with type set to [" + shapeType + "]";
+ shapeType + "]";
} }
subParser.nextToken(); subParser.nextToken();
geometryCollections = parseGeometries(subParser, shapeMapper); geometryCollections = parseGeometries(subParser, shapeMapper);
@ -81,8 +81,7 @@ abstract class GeoJsonParser {
if (shapeType == null) { if (shapeType == null) {
shapeType = GeoShapeType.CIRCLE; shapeType = GeoShapeType.CIRCLE;
} else if (shapeType.equals(GeoShapeType.CIRCLE) == false) { } else if (shapeType.equals(GeoShapeType.CIRCLE) == false) {
malformedException = "cannot have [" + CircleBuilder.FIELD_RADIUS + "] with type set to [" malformedException = "cannot have [" + CircleBuilder.FIELD_RADIUS + "] with type set to [" + shapeType + "]";
+ shapeType + "]";
} }
subParser.nextToken(); subParser.nextToken();
radius = DistanceUnit.Distance.parseDistance(subParser.text()); radius = DistanceUnit.Distance.parseDistance(subParser.text());
@ -110,8 +109,7 @@ abstract class GeoJsonParser {
} else if (geometryCollections == null && GeoShapeType.GEOMETRYCOLLECTION == shapeType) { } else if (geometryCollections == null && GeoShapeType.GEOMETRYCOLLECTION == shapeType) {
throw new ElasticsearchParseException("geometries not included"); throw new ElasticsearchParseException("geometries not included");
} else if (radius != null && GeoShapeType.CIRCLE != shapeType) { } else if (radius != null && GeoShapeType.CIRCLE != shapeType) {
throw new ElasticsearchParseException("field [{}] is supported for [{}] only", CircleBuilder.FIELD_RADIUS, throw new ElasticsearchParseException("field [{}] is supported for [{}] only", CircleBuilder.FIELD_RADIUS, CircleBuilder.TYPE);
CircleBuilder.TYPE);
} }
if (shapeType.equals(GeoShapeType.GEOMETRYCOLLECTION)) { if (shapeType.equals(GeoShapeType.GEOMETRYCOLLECTION)) {
@ -141,9 +139,9 @@ abstract class GeoJsonParser {
XContentParser.Token token = parser.nextToken(); XContentParser.Token token = parser.nextToken();
// Base cases // Base cases
if (token != XContentParser.Token.START_ARRAY && if (token != XContentParser.Token.START_ARRAY
token != XContentParser.Token.END_ARRAY && && token != XContentParser.Token.END_ARRAY
token != XContentParser.Token.VALUE_NULL) { && token != XContentParser.Token.VALUE_NULL) {
return new CoordinateNode(parseCoordinate(parser, ignoreZValue)); return new CoordinateNode(parseCoordinate(parser, ignoreZValue));
} else if (token == XContentParser.Token.VALUE_NULL) { } else if (token == XContentParser.Token.VALUE_NULL) {
throw new IllegalArgumentException("coordinates cannot contain NULL values)"); throw new IllegalArgumentException("coordinates cannot contain NULL values)");
@ -192,8 +190,7 @@ abstract class GeoJsonParser {
* @return Geometry[] geometries of the GeometryCollection * @return Geometry[] geometries of the GeometryCollection
* @throws IOException Thrown if an error occurs while reading from the XContentParser * @throws IOException Thrown if an error occurs while reading from the XContentParser
*/ */
static GeometryCollectionBuilder parseGeometries(XContentParser parser, AbstractShapeGeometryFieldMapper<?> mapper) throws static GeometryCollectionBuilder parseGeometries(XContentParser parser, AbstractShapeGeometryFieldMapper<?> mapper) throws IOException {
IOException {
if (parser.currentToken() != XContentParser.Token.START_ARRAY) { if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
throw new ElasticsearchParseException("geometries must be an array of geojson objects"); throw new ElasticsearchParseException("geometries must be an array of geojson objects");
} }

View file

@ -5,25 +5,25 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.parsers; package org.elasticsearch.legacygeo.parsers;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.builders.CoordinatesBuilder;
import org.elasticsearch.legacygeo.builders.EnvelopeBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiPointBuilder;
import org.elasticsearch.legacygeo.builders.MultiPolygonBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import java.io.IOException; import java.io.IOException;
@ -53,19 +53,21 @@ public class GeoWKTParser {
private GeoWKTParser() {} private GeoWKTParser() {}
public static ShapeBuilder<?, ?, ?> parse(XContentParser parser, final AbstractShapeGeometryFieldMapper<?> shapeMapper) public static ShapeBuilder<?, ?, ?> parse(XContentParser parser, final AbstractShapeGeometryFieldMapper<?> shapeMapper)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
return parseExpectedType(parser, null, shapeMapper); return parseExpectedType(parser, null, shapeMapper);
} }
public static ShapeBuilder<?, ?, ?> parseExpectedType(XContentParser parser, final GeoShapeType shapeType) public static ShapeBuilder<?, ?, ?> parseExpectedType(XContentParser parser, final GeoShapeType shapeType) throws IOException,
throws IOException, ElasticsearchParseException { ElasticsearchParseException {
return parseExpectedType(parser, shapeType, null); return parseExpectedType(parser, shapeType, null);
} }
/** throws an exception if the parsed geometry type does not match the expected shape type */ /** throws an exception if the parsed geometry type does not match the expected shape type */
public static ShapeBuilder<?, ?, ?> parseExpectedType(XContentParser parser, final GeoShapeType shapeType, public static ShapeBuilder<?, ?, ?> parseExpectedType(
final AbstractShapeGeometryFieldMapper<?> shapeMapper) XContentParser parser,
throws IOException, ElasticsearchParseException { final GeoShapeType shapeType,
final AbstractShapeGeometryFieldMapper<?> shapeMapper
) throws IOException, ElasticsearchParseException {
try (StringReader reader = new StringReader(parser.text())) { try (StringReader reader = new StringReader(parser.text())) {
boolean coerce = shapeMapper != null && shapeMapper.coerce(); boolean coerce = shapeMapper != null && shapeMapper.coerce();
boolean ignoreZValue = shapeMapper == null || shapeMapper.ignoreZValue(); boolean ignoreZValue = shapeMapper == null || shapeMapper.ignoreZValue();
@ -88,9 +90,12 @@ public class GeoWKTParser {
} }
/** parse geometry from the stream tokenizer */ /** parse geometry from the stream tokenizer */
private static ShapeBuilder<?, ?, ?> parseGeometry(StreamTokenizer stream, GeoShapeType shapeType, final boolean ignoreZValue, private static ShapeBuilder<?, ?, ?> parseGeometry(
final boolean coerce) StreamTokenizer stream,
throws IOException, ElasticsearchParseException { GeoShapeType shapeType,
final boolean ignoreZValue,
final boolean coerce
) throws IOException, ElasticsearchParseException {
final GeoShapeType type = GeoShapeType.forName(nextWord(stream)); final GeoShapeType type = GeoShapeType.forName(nextWord(stream));
if (shapeType != null && shapeType != GeoShapeType.GEOMETRYCOLLECTION) { if (shapeType != null && shapeType != GeoShapeType.GEOMETRYCOLLECTION) {
if (type.wktName().equals(shapeType.wktName()) == false) { if (type.wktName().equals(shapeType.wktName()) == false) {
@ -134,8 +139,8 @@ public class GeoWKTParser {
return new EnvelopeBuilder(new Coordinate(minLon, maxLat), new Coordinate(maxLon, minLat)); return new EnvelopeBuilder(new Coordinate(minLon, maxLat), new Coordinate(maxLon, minLat));
} }
private static PointBuilder parsePoint(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static PointBuilder parsePoint(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) throws IOException,
throws IOException, ElasticsearchParseException { ElasticsearchParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) { if (nextEmptyOrOpen(stream).equals(EMPTY)) {
return null; return null;
} }
@ -148,7 +153,7 @@ public class GeoWKTParser {
} }
private static List<Coordinate> parseCoordinateList(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static List<Coordinate> parseCoordinateList(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
CoordinatesBuilder coordinates = new CoordinatesBuilder(); CoordinatesBuilder coordinates = new CoordinatesBuilder();
boolean isOpenParen = false; boolean isOpenParen = false;
if (isNumberNext(stream) || (isOpenParen = nextWord(stream).equals(LPAREN))) { if (isNumberNext(stream) || (isOpenParen = nextWord(stream).equals(LPAREN))) {
@ -171,8 +176,8 @@ public class GeoWKTParser {
return coordinates.build(); return coordinates.build();
} }
private static Coordinate parseCoordinate(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static Coordinate parseCoordinate(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) throws IOException,
throws IOException, ElasticsearchParseException { ElasticsearchParseException {
final double lon = nextNumber(stream); final double lon = nextNumber(stream);
final double lat = nextNumber(stream); final double lat = nextNumber(stream);
Double z = null; Double z = null;
@ -183,7 +188,7 @@ public class GeoWKTParser {
} }
private static MultiPointBuilder parseMultiPoint(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static MultiPointBuilder parseMultiPoint(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
String token = nextEmptyOrOpen(stream); String token = nextEmptyOrOpen(stream);
if (token.equals(EMPTY)) { if (token.equals(EMPTY)) {
return new MultiPointBuilder(); return new MultiPointBuilder();
@ -191,8 +196,8 @@ public class GeoWKTParser {
return new MultiPointBuilder(parseCoordinateList(stream, ignoreZValue, coerce)); return new MultiPointBuilder(parseCoordinateList(stream, ignoreZValue, coerce));
} }
private static LineStringBuilder parseLine(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static LineStringBuilder parseLine(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) throws IOException,
throws IOException, ElasticsearchParseException { ElasticsearchParseException {
String token = nextEmptyOrOpen(stream); String token = nextEmptyOrOpen(stream);
if (token.equals(EMPTY)) { if (token.equals(EMPTY)) {
return null; return null;
@ -203,7 +208,7 @@ public class GeoWKTParser {
// A LinearRing is closed LineString with 4 or more positions. The first and last positions // A LinearRing is closed LineString with 4 or more positions. The first and last positions
// are equivalent (they represent equivalent points). // are equivalent (they represent equivalent points).
private static LineStringBuilder parseLinearRing(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static LineStringBuilder parseLinearRing(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
String token = nextEmptyOrOpen(stream); String token = nextEmptyOrOpen(stream);
if (token.equals(EMPTY)) { if (token.equals(EMPTY)) {
return null; return null;
@ -220,14 +225,13 @@ public class GeoWKTParser {
} }
} }
if (coordinates.size() < 4) { if (coordinates.size() < 4) {
throw new ElasticsearchParseException("invalid number of points in LinearRing (found [{}] - must be >= 4)", throw new ElasticsearchParseException("invalid number of points in LinearRing (found [{}] - must be >= 4)", coordinates.size());
coordinates.size());
} }
return new LineStringBuilder(coordinates); return new LineStringBuilder(coordinates);
} }
private static MultiLineStringBuilder parseMultiLine(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static MultiLineStringBuilder parseMultiLine(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
String token = nextEmptyOrOpen(stream); String token = nextEmptyOrOpen(stream);
if (token.equals(EMPTY)) { if (token.equals(EMPTY)) {
return new MultiLineStringBuilder(); return new MultiLineStringBuilder();
@ -240,13 +244,12 @@ public class GeoWKTParser {
return builder; return builder;
} }
private static PolygonBuilder parsePolygon(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static PolygonBuilder parsePolygon(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) throws IOException,
throws IOException, ElasticsearchParseException { ElasticsearchParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) { if (nextEmptyOrOpen(stream).equals(EMPTY)) {
return null; return null;
} }
PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce), PolygonBuilder builder = new PolygonBuilder(parseLinearRing(stream, ignoreZValue, coerce), Orientation.RIGHT);
Orientation.RIGHT);
while (nextCloserOrComma(stream).equals(COMMA)) { while (nextCloserOrComma(stream).equals(COMMA)) {
builder.hole(parseLinearRing(stream, ignoreZValue, coerce)); builder.hole(parseLinearRing(stream, ignoreZValue, coerce));
} }
@ -254,7 +257,7 @@ public class GeoWKTParser {
} }
private static MultiPolygonBuilder parseMultiPolygon(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce) private static MultiPolygonBuilder parseMultiPolygon(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
throws IOException, ElasticsearchParseException { throws IOException, ElasticsearchParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) { if (nextEmptyOrOpen(stream).equals(EMPTY)) {
return null; return null;
} }
@ -265,14 +268,17 @@ public class GeoWKTParser {
return builder; return builder;
} }
private static GeometryCollectionBuilder parseGeometryCollection(StreamTokenizer stream, final boolean ignoreZValue, private static GeometryCollectionBuilder parseGeometryCollection(
final boolean coerce) StreamTokenizer stream,
throws IOException, ElasticsearchParseException { final boolean ignoreZValue,
final boolean coerce
) throws IOException, ElasticsearchParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) { if (nextEmptyOrOpen(stream).equals(EMPTY)) {
return null; return null;
} }
GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape( GeometryCollectionBuilder builder = new GeometryCollectionBuilder().shape(
parseGeometry(stream, GeoShapeType.GEOMETRYCOLLECTION, ignoreZValue, coerce)); parseGeometry(stream, GeoShapeType.GEOMETRYCOLLECTION, ignoreZValue, coerce)
);
while (nextCloserOrComma(stream).equals(COMMA)) { while (nextCloserOrComma(stream).equals(COMMA)) {
builder.shape(parseGeometry(stream, null, ignoreZValue, coerce)); builder.shape(parseGeometry(stream, null, ignoreZValue, coerce));
} }
@ -285,9 +291,12 @@ public class GeoWKTParser {
case StreamTokenizer.TT_WORD: case StreamTokenizer.TT_WORD:
final String word = stream.sval; final String word = stream.sval;
return word.equalsIgnoreCase(EMPTY) ? EMPTY : word; return word.equalsIgnoreCase(EMPTY) ? EMPTY : word;
case '(': return LPAREN; case '(':
case ')': return RPAREN; return LPAREN;
case ',': return COMMA; case ')':
return RPAREN;
case ',':
return COMMA;
} }
throw new ElasticsearchParseException("expected word but found: " + tokenString(stream), stream.lineno()); throw new ElasticsearchParseException("expected word but found: " + tokenString(stream), stream.lineno());
} }
@ -309,10 +318,14 @@ public class GeoWKTParser {
private static String tokenString(StreamTokenizer stream) { private static String tokenString(StreamTokenizer stream) {
switch (stream.ttype) { switch (stream.ttype) {
case StreamTokenizer.TT_WORD: return stream.sval; case StreamTokenizer.TT_WORD:
case StreamTokenizer.TT_EOF: return EOF; return stream.sval;
case StreamTokenizer.TT_EOL: return EOL; case StreamTokenizer.TT_EOF:
case StreamTokenizer.TT_NUMBER: return NUMBER; return EOF;
case StreamTokenizer.TT_EOL:
return EOL;
case StreamTokenizer.TT_NUMBER:
return NUMBER;
} }
return "'" + (char) stream.ttype + "'"; return "'" + (char) stream.ttype + "'";
} }
@ -328,8 +341,10 @@ public class GeoWKTParser {
if (next.equals(EMPTY) || next.equals(LPAREN)) { if (next.equals(EMPTY) || next.equals(LPAREN)) {
return next; return next;
} }
throw new ElasticsearchParseException("expected " + EMPTY + " or " + LPAREN throw new ElasticsearchParseException(
+ " but found: " + tokenString(stream), stream.lineno()); "expected " + EMPTY + " or " + LPAREN + " but found: " + tokenString(stream),
stream.lineno()
);
} }
private static String nextCloser(StreamTokenizer stream) throws IOException, ElasticsearchParseException { private static String nextCloser(StreamTokenizer stream) throws IOException, ElasticsearchParseException {
@ -351,15 +366,19 @@ public class GeoWKTParser {
if (token.equals(COMMA) || token.equals(RPAREN)) { if (token.equals(COMMA) || token.equals(RPAREN)) {
return token; return token;
} }
throw new ElasticsearchParseException("expected " + COMMA + " or " + RPAREN throw new ElasticsearchParseException(
+ " but found: " + tokenString(stream), stream.lineno()); "expected " + COMMA + " or " + RPAREN + " but found: " + tokenString(stream),
stream.lineno()
);
} }
/** next word in the stream */ /** next word in the stream */
private static void checkEOF(StreamTokenizer stream) throws ElasticsearchParseException, IOException { private static void checkEOF(StreamTokenizer stream) throws ElasticsearchParseException, IOException {
if (stream.nextToken() != StreamTokenizer.TT_EOF) { if (stream.nextToken() != StreamTokenizer.TT_EOF) {
throw new ElasticsearchParseException("expected end of WKT string but found additional text: " throw new ElasticsearchParseException(
+ tokenString(stream), stream.lineno()); "expected end of WKT string but found additional text: " + tokenString(stream),
stream.lineno()
);
} }
} }
} }

View file

@ -5,18 +5,18 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.parsers; package org.elasticsearch.legacygeo.parsers;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.xcontent.ParseField;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ParseField;
import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.MapXContentParser; import org.elasticsearch.common.xcontent.support.MapXContentParser;
import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractGeometryFieldMapper;
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper; import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -46,11 +46,12 @@ public interface ShapeParser {
if (geometryMapper instanceof AbstractShapeGeometryFieldMapper == false) { if (geometryMapper instanceof AbstractShapeGeometryFieldMapper == false) {
throw new IllegalArgumentException("geometry must be a shape type"); throw new IllegalArgumentException("geometry must be a shape type");
} }
shapeMapper = (AbstractShapeGeometryFieldMapper<?>) geometryMapper; shapeMapper = (AbstractShapeGeometryFieldMapper<?>) geometryMapper;
} }
if (parser.currentToken() == XContentParser.Token.VALUE_NULL) { if (parser.currentToken() == XContentParser.Token.VALUE_NULL) {
return null; return null;
} if (parser.currentToken() == XContentParser.Token.START_OBJECT) { }
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
return GeoJsonParser.parse(parser, shapeMapper); return GeoJsonParser.parse(parser, shapeMapper);
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) { } else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
return GeoWKTParser.parse(parser, shapeMapper); return GeoWKTParser.parse(parser, shapeMapper);
@ -70,8 +71,14 @@ public interface ShapeParser {
} }
static ShapeBuilder<?, ?, ?> parse(Object value) throws IOException { static ShapeBuilder<?, ?, ?> parse(Object value) throws IOException {
try (XContentParser parser = new MapXContentParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, try (
Collections.singletonMap("value", value), null)) { XContentParser parser = new MapXContentParser(
NamedXContentRegistry.EMPTY,
LoggingDeprecationHandler.INSTANCE,
Collections.singletonMap("value", value),
null
)
) {
parser.nextToken(); // start object parser.nextToken(); // start object
parser.nextToken(); // field name parser.nextToken(); // field name
parser.nextToken(); // field value parser.nextToken(); // field value

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.index.query; package org.elasticsearch.legacygeo.query;
import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
@ -16,19 +16,9 @@ import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation; import org.apache.lucene.spatial.query.SpatialOperation;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.geo.builders.CircleBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.geometry.Circle; import org.elasticsearch.geometry.Circle;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
@ -42,7 +32,19 @@ import org.elasticsearch.geometry.MultiPolygon;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon; import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle; import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper; import org.elasticsearch.index.query.ExistsQueryBuilder;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.legacygeo.builders.CircleBuilder;
import org.elasticsearch.legacygeo.builders.EnvelopeBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiPointBuilder;
import org.elasticsearch.legacygeo.builders.MultiPolygonBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
@ -51,7 +53,7 @@ import java.util.List;
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;
public class LegacyGeoShapeQueryProcessor { public class LegacyGeoShapeQueryProcessor {
private final LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType; private final LegacyGeoShapeFieldMapper.GeoShapeFieldType shapeFieldType;
@ -59,11 +61,19 @@ public class LegacyGeoShapeQueryProcessor {
this.shapeFieldType = shapeFieldType; this.shapeFieldType = shapeFieldType;
} }
public Query geoShapeQuery(Geometry shape, String fieldName, SpatialStrategy strategy, public Query geoShapeQuery(
ShapeRelation relation, SearchExecutionContext context) { Geometry shape,
String fieldName,
SpatialStrategy strategy,
ShapeRelation relation,
SearchExecutionContext context
) {
if (context.allowExpensiveQueries() == false) { if (context.allowExpensiveQueries() == false) {
throw new ElasticsearchException("[geo-shape] queries on [PrefixTree geo shapes] cannot be executed when '" throw new ElasticsearchException(
+ ALLOW_EXPENSIVE_QUERIES.getKey() + "' is set to false."); "[geo-shape] queries on [PrefixTree geo shapes] cannot be executed when '"
+ ALLOW_EXPENSIVE_QUERIES.getKey()
+ "' is set to false."
);
} }
SpatialStrategy spatialStrategy = shapeFieldType.strategy(); SpatialStrategy spatialStrategy = shapeFieldType.strategy();
@ -175,9 +185,11 @@ public class LegacyGeoShapeQueryProcessor {
@Override @Override
public ShapeBuilder<?, ?, ?> visit(Polygon polygon) { public ShapeBuilder<?, ?, ?> visit(Polygon polygon) {
PolygonBuilder polygonBuilder = PolygonBuilder polygonBuilder = new PolygonBuilder(
new PolygonBuilder((LineStringBuilder) visit((Line) polygon.getPolygon()), (LineStringBuilder) visit((Line) polygon.getPolygon()),
Orientation.RIGHT, false); Orientation.RIGHT,
false
);
for (int i = 0; i < polygon.getNumberOfHoles(); i++) { for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
polygonBuilder.hole((LineStringBuilder) visit((Line) polygon.getHole(i))); polygonBuilder.hole((LineStringBuilder) visit((Line) polygon.getHole(i)));
} }
@ -186,8 +198,10 @@ public class LegacyGeoShapeQueryProcessor {
@Override @Override
public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) { public ShapeBuilder<?, ?, ?> visit(Rectangle rectangle) {
return new EnvelopeBuilder(new Coordinate(rectangle.getMinX(), rectangle.getMaxY()), return new EnvelopeBuilder(
new Coordinate(rectangle.getMaxX(), rectangle.getMinY())); new Coordinate(rectangle.getMinX(), rectangle.getMaxY()),
new Coordinate(rectangle.getMaxX(), rectangle.getMinY())
);
} }
}); });
return shapeBuilder; return shapeBuilder;

View file

@ -5,15 +5,15 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.legacygeo;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.GeometryParser;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.utils.GeographyValidator;
import org.elasticsearch.index.mapper.GeoShapeIndexer; import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.elasticsearch.legacygeo.test.ElasticsearchGeoAssertions;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.Shape;
@ -26,19 +26,26 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT; import static org.elasticsearch.legacygeo.builders.ShapeBuilder.SPATIAL_CONTEXT;
/** Base class for all geo parsing tests */ /** Base class for all geo parsing tests */
abstract class BaseGeoParsingTestCase extends ESTestCase { abstract class BaseGeoParsingTestCase extends ESTestCase {
protected static final GeometryFactory GEOMETRY_FACTORY = SPATIAL_CONTEXT.getGeometryFactory(); protected static final GeometryFactory GEOMETRY_FACTORY = SPATIAL_CONTEXT.getGeometryFactory();
public abstract void testParsePoint() throws IOException, ParseException; public abstract void testParsePoint() throws IOException, ParseException;
public abstract void testParseMultiPoint() throws IOException, ParseException; public abstract void testParseMultiPoint() throws IOException, ParseException;
public abstract void testParseLineString() throws IOException, ParseException; public abstract void testParseLineString() throws IOException, ParseException;
public abstract void testParseMultiLineString() throws IOException, ParseException; public abstract void testParseMultiLineString() throws IOException, ParseException;
public abstract void testParsePolygon() throws IOException, ParseException; public abstract void testParsePolygon() throws IOException, ParseException;
public abstract void testParseMultiPolygon() throws IOException, ParseException; public abstract void testParseMultiPolygon() throws IOException, ParseException;
public abstract void testParseEnvelope() throws IOException, ParseException; public abstract void testParseEnvelope() throws IOException, ParseException;
public abstract void testParseGeometryCollection() throws IOException, ParseException; public abstract void testParseGeometryCollection() throws IOException, ParseException;
protected void assertValidException(XContentBuilder builder, Class<?> expectedException) throws IOException { protected void assertValidException(XContentBuilder builder, Class<?> expectedException) throws IOException {
@ -62,13 +69,6 @@ abstract class BaseGeoParsingTestCase extends ESTestCase {
} }
} }
protected void assertGeometryEquals(org.elasticsearch.geometry.Geometry expected, XContentBuilder geoJson) throws IOException {
try (XContentParser parser = createParser(geoJson)) {
parser.nextToken();
assertEquals(expected, GeoJson.fromXContent(GeographyValidator.instance(false), false, true, parser));
}
}
protected ShapeCollection<Shape> shapeCollection(Shape... shapes) { protected ShapeCollection<Shape> shapeCollection(Shape... shapes) {
return new ShapeCollection<>(Arrays.asList(shapes), SPATIAL_CONTEXT); return new ShapeCollection<>(Arrays.asList(shapes), SPATIAL_CONTEXT);
} }

View file

@ -5,24 +5,13 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo; package org.elasticsearch.legacygeo;
import org.apache.lucene.geo.GeoTestUtil; import org.apache.lucene.geo.GeoTestUtil;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.MultiPolygonBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
@ -33,9 +22,21 @@ import org.elasticsearch.geometry.MultiLine;
import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer; import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MapperBuilderContext; import org.elasticsearch.index.mapper.MapperBuilderContext;
import org.elasticsearch.test.geo.RandomShapeGenerator; import org.elasticsearch.legacygeo.builders.CoordinatesBuilder;
import org.elasticsearch.legacygeo.builders.EnvelopeBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiPointBuilder;
import org.elasticsearch.legacygeo.builders.MultiPolygonBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.legacygeo.parsers.GeoWKTParser;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.LinearRing;
@ -55,7 +56,7 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT; import static org.elasticsearch.legacygeo.builders.ShapeBuilder.SPATIAL_CONTEXT;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.hasToString;
@ -64,8 +65,7 @@ import static org.hamcrest.Matchers.hasToString;
*/ */
public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
private static XContentBuilder toWKTContent(ShapeBuilder<?, ?, ?> builder, boolean generateMalformed) private static XContentBuilder toWKTContent(ShapeBuilder<?, ?, ?> builder, boolean generateMalformed) throws IOException {
throws IOException {
String wkt = builder.toWKT(); String wkt = builder.toWKT();
if (generateMalformed) { if (generateMalformed) {
// malformed - extra paren // malformed - extra paren
@ -172,7 +172,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
MultiLineStringBuilder builder = new MultiLineStringBuilder(); MultiLineStringBuilder builder = new MultiLineStringBuilder();
for (int j = 0; j < numLineStrings; ++j) { for (int j = 0; j < numLineStrings; ++j) {
List<Coordinate> lsc = randomLineStringCoords(); List<Coordinate> lsc = randomLineStringCoords();
Coordinate [] coords = lsc.toArray(new Coordinate[lsc.size()]); Coordinate[] coords = lsc.toArray(new Coordinate[lsc.size()]);
lineStrings.add(GEOMETRY_FACTORY.createLineString(coords)); lineStrings.add(GEOMETRY_FACTORY.createLineString(coords));
builder.linestring(new LineStringBuilder(lsc)); builder.linestring(new LineStringBuilder(lsc));
} }
@ -180,8 +180,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
List<Line> lines = new ArrayList<>(lineStrings.size()); List<Line> lines = new ArrayList<>(lineStrings.size());
for (int j = 0; j < lineStrings.size(); ++j) { for (int j = 0; j < lineStrings.size(); ++j) {
Coordinate[] c = lineStrings.get(j).getCoordinates(); Coordinate[] c = lineStrings.get(j).getCoordinates();
lines.add(new Line(Arrays.stream(c).mapToDouble(i->i.x).toArray(), Arrays.stream(c).mapToDouble(i->i.y).toArray() lines.add(new Line(Arrays.stream(c).mapToDouble(i -> i.x).toArray(), Arrays.stream(c).mapToDouble(i -> i.y).toArray()));
));
} }
Geometry expectedGeom; Geometry expectedGeom;
if (lines.isEmpty()) { if (lines.isEmpty()) {
@ -194,8 +193,7 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
assertExpected(expectedGeom, builder, false); assertExpected(expectedGeom, builder, false);
assertMalformed(builder); assertMalformed(builder);
MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString( MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(lineStrings.toArray(new LineString[lineStrings.size()]));
lineStrings.toArray(new LineString[lineStrings.size()]));
assumeTrue("JTS test path cannot handle empty multilinestrings", numLineStrings > 1); assumeTrue("JTS test path cannot handle empty multilinestrings", numLineStrings > 1);
assertExpected(jtsGeom(expected), builder, true); assertExpected(jtsGeom(expected), builder, true);
} }
@ -203,7 +201,8 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
@Override @Override
public void testParsePolygon() throws IOException, ParseException { public void testParsePolygon() throws IOException, ParseException {
PolygonBuilder builder = PolygonBuilder.class.cast( PolygonBuilder builder = PolygonBuilder.class.cast(
RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON)); RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON)
);
Coordinate[] coords = builder.coordinates()[0][0]; Coordinate[] coords = builder.coordinates()[0][0];
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords);
@ -252,20 +251,20 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
PolygonBuilder polygonWithHole = new PolygonBuilder(new CoordinatesBuilder().coordinates(shellCoordinates)); PolygonBuilder polygonWithHole = new PolygonBuilder(new CoordinatesBuilder().coordinates(shellCoordinates));
polygonWithHole.hole(new LineStringBuilder(holeCoordinates)); polygonWithHole.hole(new LineStringBuilder(holeCoordinates));
LinearRing shell = GEOMETRY_FACTORY.createLinearRing( LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
LinearRing[] holes = new LinearRing[1]; LinearRing[] holes = new LinearRing[1];
holes[0] = GEOMETRY_FACTORY.createLinearRing( holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes);
assertExpected(jtsGeom(expected), polygonWithHole, true); assertExpected(jtsGeom(expected), polygonWithHole, true);
org.elasticsearch.geometry.LinearRing hole = org.elasticsearch.geometry.LinearRing hole = new org.elasticsearch.geometry.LinearRing(
new org.elasticsearch.geometry.LinearRing( new double[] { 100.2d, 100.8d, 100.8d, 100.2d, 100.2d },
new double[] {100.2d, 100.8d, 100.8d, 100.2d, 100.2d}, new double[] {0.8d, 0.8d, 0.2d, 0.2d, 0.8d}); new double[] { 0.8d, 0.8d, 0.2d, 0.2d, 0.8d }
org.elasticsearch.geometry.Polygon p = );
new org.elasticsearch.geometry.Polygon(new org.elasticsearch.geometry.LinearRing( org.elasticsearch.geometry.Polygon p = new org.elasticsearch.geometry.Polygon(
new double[] {101d, 101d, 100d, 100d, 101d}, new double[] {0d, 1d, 1d, 0d, 0d}), Collections.singletonList(hole)); new org.elasticsearch.geometry.LinearRing(new double[] { 101d, 101d, 100d, 100d, 101d }, new double[] { 0d, 1d, 1d, 0d, 0d }),
Collections.singletonList(hole)
);
assertExpected(p, polygonWithHole, false); assertExpected(p, polygonWithHole, false);
assertMalformed(polygonWithHole); assertMalformed(polygonWithHole);
} }
@ -293,13 +292,11 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
XContentParser parser = createParser(xContentBuilder); XContentParser parser = createParser(xContentBuilder);
parser.nextToken(); parser.nextToken();
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test", false, true) final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test", false, true).ignoreZValue(false)
.ignoreZValue(false) .build(MapperBuilderContext.ROOT);
.build(MapperBuilderContext.ROOT);
// test store z disabled // test store z disabled
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> ShapeParser.parse(parser, mapperBuilder));
() -> ShapeParser.parse(parser, mapperBuilder));
assertThat(e, hasToString(containsString("but [ignore_z_value] parameter is [false]"))); assertThat(e, hasToString(containsString("but [ignore_z_value] parameter is [false]")));
} }
@ -326,13 +323,12 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
XContentParser parser = createParser(xContentBuilder); XContentParser parser = createParser(xContentBuilder);
parser.nextToken(); parser.nextToken();
final LegacyGeoShapeFieldMapper mapperBuilder = final LegacyGeoShapeFieldMapper mapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true).build(
new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true) MapperBuilderContext.ROOT
.build(MapperBuilderContext.ROOT); );
// test store z disabled // test store z disabled
ElasticsearchException e = expectThrows(ElasticsearchException.class, ElasticsearchException e = expectThrows(ElasticsearchException.class, () -> ShapeParser.parse(parser, mapperBuilder));
() -> ShapeParser.parse(parser, mapperBuilder));
assertThat(e, hasToString(containsString("unable to add coordinate to CoordinateBuilder: coordinate dimensions do not match"))); assertThat(e, hasToString(containsString("unable to add coordinate to CoordinateBuilder: coordinate dimensions do not match")));
} }
@ -350,9 +346,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
XContentParser parser = createParser(xContentBuilder); XContentParser parser = createParser(xContentBuilder);
parser.nextToken(); parser.nextToken();
final LegacyGeoShapeFieldMapper mapperBuilder = final LegacyGeoShapeFieldMapper mapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true).build(
new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true) MapperBuilderContext.ROOT
.build(MapperBuilderContext.ROOT); );
ShapeBuilder<?, ?, ?> shapeBuilder = ShapeParser.parse(parser, mapperBuilder); ShapeBuilder<?, ?, ?> shapeBuilder = ShapeParser.parse(parser, mapperBuilder);
assertEquals(shapeBuilder.numDimensions(), 3); assertEquals(shapeBuilder.numDimensions(), 3);
@ -365,16 +361,18 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
XContentParser parser = createParser(xContentBuilder); XContentParser parser = createParser(xContentBuilder);
parser.nextToken(); parser.nextToken();
final LegacyGeoShapeFieldMapper defaultMapperBuilder = final LegacyGeoShapeFieldMapper defaultMapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true)
new LegacyGeoShapeFieldMapper.Builder("test", Version.V_6_3_0, false, true) .coerce(false)
.coerce(false).build(MapperBuilderContext.ROOT); .build(MapperBuilderContext.ROOT);
ElasticsearchParseException exception = expectThrows(ElasticsearchParseException.class, ElasticsearchParseException exception = expectThrows(
() -> ShapeParser.parse(parser, defaultMapperBuilder)); ElasticsearchParseException.class,
() -> ShapeParser.parse(parser, defaultMapperBuilder)
);
assertEquals("invalid LinearRing found (coordinates are not closed)", exception.getMessage()); assertEquals("invalid LinearRing found (coordinates are not closed)", exception.getMessage());
final LegacyGeoShapeFieldMapper coercingMapperBuilder = final LegacyGeoShapeFieldMapper coercingMapperBuilder = new LegacyGeoShapeFieldMapper.Builder("test", Version.CURRENT, false, true)
new LegacyGeoShapeFieldMapper.Builder("test", Version.CURRENT, false, true) .coerce(true)
.coerce(true).build(MapperBuilderContext.ROOT); .build(MapperBuilderContext.ROOT);
ShapeBuilder<?, ?, ?> shapeBuilder = ShapeParser.parse(parser, coercingMapperBuilder); ShapeBuilder<?, ?, ?> shapeBuilder = ShapeParser.parse(parser, coercingMapperBuilder);
assertNotNull(shapeBuilder); assertNotNull(shapeBuilder);
assertEquals("polygon ((100.0 5.0, 100.0 10.0, 90.0 10.0, 90.0 5.0, 100.0 5.0))", shapeBuilder.toWKT()); assertEquals("polygon ((100.0 5.0, 100.0 10.0, 90.0 10.0, 90.0 5.0, 100.0 5.0))", shapeBuilder.toWKT());
@ -441,8 +439,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
XContentBuilder builder = toWKTContent(new PointBuilder(-1, 2), false); XContentBuilder builder = toWKTContent(new PointBuilder(-1, 2), false);
XContentParser parser = createParser(builder); XContentParser parser = createParser(builder);
parser.nextToken(); parser.nextToken();
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, ElasticsearchParseException e = expectThrows(
() -> GeoWKTParser.parseExpectedType(parser, GeoShapeType.POLYGON)); ElasticsearchParseException.class,
() -> GeoWKTParser.parseExpectedType(parser, GeoShapeType.POLYGON)
);
assertThat(e, hasToString(containsString("Expected geometry type [polygon] but found [point]"))); assertThat(e, hasToString(containsString("Expected geometry type [polygon] but found [point]")));
} }
} }

View file

@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo;
import org.elasticsearch.common.geo.GeometryIO;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection;
import org.elasticsearch.geometry.ShapeType;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.test.ESTestCase;
import static org.elasticsearch.geo.GeometryTestUtils.randomGeometry;
import static org.elasticsearch.legacygeo.query.LegacyGeoShapeQueryProcessor.geometryToShapeBuilder;
public class GeometryIOTests extends ESTestCase {
public void testRandomSerialization() throws Exception {
for (int i = 0; i < randomIntBetween(1, 20); i++) {
boolean hasAlt = randomBoolean();
Geometry geometry = randomGeometry(hasAlt);
if (shapeSupported(geometry) && randomBoolean()) {
// Shape builder conversion doesn't support altitude
ShapeBuilder<?, ?, ?> shapeBuilder = geometryToShapeBuilder(geometry);
if (randomBoolean()) {
Geometry actual = shapeBuilder.buildGeometry();
assertEquals(geometry, actual);
}
if (randomBoolean()) {
// Test ShapeBuilder -> Geometry Serialization
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeNamedWriteable(shapeBuilder);
try (StreamInput in = out.bytes().streamInput()) {
Geometry actual = GeometryIO.readGeometry(in);
assertEquals(geometry, actual);
assertEquals(0, in.available());
}
}
} else {
// Test Geometry -> ShapeBuilder Serialization
try (BytesStreamOutput out = new BytesStreamOutput()) {
GeometryIO.writeGeometry(out, geometry);
try (StreamInput in = out.bytes().streamInput()) {
try (StreamInput nin = new NamedWriteableAwareStreamInput(in, this.writableRegistry())) {
ShapeBuilder<?, ?, ?> actual = nin.readNamedWriteable(ShapeBuilder.class);
assertEquals(shapeBuilder, actual);
assertEquals(0, in.available());
}
}
}
}
// Test Geometry -> Geometry
try (BytesStreamOutput out = new BytesStreamOutput()) {
GeometryIO.writeGeometry(out, geometry);
;
try (StreamInput in = out.bytes().streamInput()) {
Geometry actual = GeometryIO.readGeometry(in);
assertEquals(geometry, actual);
assertEquals(0, in.available());
}
}
}
}
}
private boolean shapeSupported(Geometry geometry) {
if (geometry.hasZ()) {
return false;
}
if (geometry.type() == ShapeType.GEOMETRYCOLLECTION) {
GeometryCollection<?> collection = (GeometryCollection<?>) geometry;
for (Geometry g : collection) {
if (shapeSupported(g) == false) {
return false;
}
}
}
return true;
}
@Override
protected NamedWriteableRegistry writableRegistry() {
return new NamedWriteableRegistry(GeoShapeType.getShapeWriteables());
}
}

View file

@ -0,0 +1,805 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.legacygeo.builders.CircleBuilder;
import org.elasticsearch.legacygeo.builders.CoordinatesBuilder;
import org.elasticsearch.legacygeo.builders.EnvelopeBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.elasticsearch.legacygeo.test.ElasticsearchGeoAssertions;
import org.elasticsearch.test.ESTestCase;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.impl.PointImpl;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
/**
* Tests for {@link ShapeBuilder}
*/
public class ShapeBuilderTests extends ESTestCase {
public void testNewPoint() {
PointBuilder pb = new PointBuilder().coordinate(-100, 45);
Point point = pb.buildS4J();
assertEquals(-100D, point.getX(), 0.0d);
assertEquals(45D, point.getY(), 0.0d);
org.elasticsearch.geometry.Point geoPoint = pb.buildGeometry();
assertEquals(-100D, geoPoint.getX(), 0.0d);
assertEquals(45D, geoPoint.getY(), 0.0d);
}
public void testNewRectangle() {
EnvelopeBuilder eb = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30));
Rectangle rectangle = eb.buildS4J();
assertEquals(-45D, rectangle.getMinX(), 0.0d);
assertEquals(-30D, rectangle.getMinY(), 0.0d);
assertEquals(45D, rectangle.getMaxX(), 0.0d);
assertEquals(30D, rectangle.getMaxY(), 0.0d);
org.elasticsearch.geometry.Rectangle luceneRectangle = eb.buildGeometry();
assertEquals(-45D, luceneRectangle.getMinX(), 0.0d);
assertEquals(-30D, luceneRectangle.getMinY(), 0.0d);
assertEquals(45D, luceneRectangle.getMaxX(), 0.0d);
assertEquals(30D, luceneRectangle.getMaxY(), 0.0d);
}
public void testNewPolygon() {
PolygonBuilder pb = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-45, 30).coordinate(45, 30).coordinate(45, -30).coordinate(-45, -30).coordinate(-45, 30)
);
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testNewPolygon_coordinate() {
PolygonBuilder pb = new PolygonBuilder(
new CoordinatesBuilder().coordinate(new Coordinate(-45, 30))
.coordinate(new Coordinate(45, 30))
.coordinate(new Coordinate(45, -30))
.coordinate(new Coordinate(-45, -30))
.coordinate(new Coordinate(-45, 30))
);
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testNewPolygon_coordinates() {
PolygonBuilder pb = new PolygonBuilder(
new CoordinatesBuilder().coordinates(
new Coordinate(-45, 30),
new Coordinate(45, 30),
new Coordinate(45, -30),
new Coordinate(-45, -30),
new Coordinate(-45, 30)
)
);
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testLineStringBuilder() {
// Building a simple LineString
LineStringBuilder lsb = new LineStringBuilder(
new CoordinatesBuilder().coordinate(-130.0, 55.0)
.coordinate(-130.0, -40.0)
.coordinate(-15.0, -40.0)
.coordinate(-20.0, 50.0)
.coordinate(-45.0, 50.0)
.coordinate(-45.0, -15.0)
.coordinate(-110.0, -15.0)
.coordinate(-110.0, 55.0)
);
lsb.buildS4J();
buildGeometry(lsb);
// Building a linestring that needs to be wrapped
lsb = new LineStringBuilder(
new CoordinatesBuilder().coordinate(100.0, 50.0)
.coordinate(110.0, -40.0)
.coordinate(240.0, -40.0)
.coordinate(230.0, 60.0)
.coordinate(200.0, 60.0)
.coordinate(200.0, -30.0)
.coordinate(130.0, -30.0)
.coordinate(130.0, 60.0)
);
lsb.buildS4J();
buildGeometry(lsb);
// Building a lineString on the dateline
lsb = new LineStringBuilder(
new CoordinatesBuilder().coordinate(-180.0, 80.0).coordinate(-180.0, 40.0).coordinate(-180.0, -40.0).coordinate(-180.0, -80.0)
);
lsb.buildS4J();
buildGeometry(lsb);
// Building a lineString on the dateline
lsb = new LineStringBuilder(
new CoordinatesBuilder().coordinate(180.0, 80.0).coordinate(180.0, 40.0).coordinate(180.0, -40.0).coordinate(180.0, -80.0)
);
lsb.buildS4J();
buildGeometry(lsb);
}
public void testMultiLineString() {
MultiLineStringBuilder mlsb = new MultiLineStringBuilder().linestring(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-100.0, 50.0).coordinate(50.0, 50.0).coordinate(50.0, 20.0).coordinate(-100.0, 20.0)
)
)
.linestring(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-100.0, 20.0).coordinate(50.0, 20.0).coordinate(50.0, 0.0).coordinate(-100.0, 0.0)
)
);
mlsb.buildS4J();
buildGeometry(mlsb);
// LineString that needs to be wrapped
new MultiLineStringBuilder().linestring(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(150.0, 60.0).coordinate(200.0, 60.0).coordinate(200.0, 40.0).coordinate(150.0, 40.0)
)
)
.linestring(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(150.0, 20.0).coordinate(200.0, 20.0).coordinate(200.0, 0.0).coordinate(150.0, 0.0)
)
);
mlsb.buildS4J();
buildGeometry(mlsb);
}
public void testPolygonSelfIntersection() {
PolygonBuilder newPolygon = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-40.0, 50.0).coordinate(40.0, 50.0).coordinate(-40.0, -50.0).coordinate(40.0, -50.0).close()
);
Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.buildS4J());
assertThat(e.getMessage(), containsString("Cannot determine orientation: signed area equal to 0"));
}
/** note: only supported by S4J at the moment */
public void testGeoCircle() {
double earthCircumference = 40075016.69;
Circle circle = new CircleBuilder().center(0, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(+180, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(-180, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(-180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(0, 90).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(0, -90).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
double randomLat = (randomDouble() * 180) - 90;
double randomLon = (randomDouble() * 360) - 180;
double randomRadius = randomIntBetween(1, (int) earthCircumference / 4);
circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").buildS4J();
assertEquals((360 * randomRadius) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
}
public void testPolygonWrapping() {
PolygonBuilder pb = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0)
.close()
);
ElasticsearchGeoAssertions.assertMultiPolygon(pb.buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(pb), false);
}
public void testLineStringWrapping() {
LineStringBuilder lsb = new LineStringBuilder(
new CoordinatesBuilder().coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0)
.close()
);
ElasticsearchGeoAssertions.assertMultiLineString(lsb.buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiLineString(buildGeometry(lsb), false);
}
public void testDatelineOGC() {
// tests that the following shape (defined in counterclockwise OGC order)
// https://gist.github.com/anonymous/7f1bb6d7e9cd72f5977c crosses the dateline
// expected results: 3 polygons, 1 with a hole
// a giant c shape
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(174, 0)
.coordinate(-176, 0)
.coordinate(-176, 3)
.coordinate(177, 3)
.coordinate(177, 5)
.coordinate(-176, 5)
.coordinate(-176, 8)
.coordinate(174, 8)
.coordinate(174, 0)
);
// 3/4 of an embedded 'c', crossing dateline once
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(175, 1)
.coordinate(175, 7)
.coordinate(-178, 7)
.coordinate(-178, 6)
.coordinate(176, 6)
.coordinate(176, 2)
.coordinate(179, 2)
.coordinate(179, 1)
.coordinate(175, 1)
)
);
// embedded hole right of the dateline
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-179, 1).coordinate(-179, 2).coordinate(-177, 2).coordinate(-177, 1).coordinate(-179, 1)
)
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testDateline() {
// tests that the following shape (defined in clockwise non-OGC order)
// https://gist.github.com/anonymous/7f1bb6d7e9cd72f5977c crosses the dateline
// expected results: 3 polygons, 1 with a hole
// a giant c shape
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-186, 0)
.coordinate(-176, 0)
.coordinate(-176, 3)
.coordinate(-183, 3)
.coordinate(-183, 5)
.coordinate(-176, 5)
.coordinate(-176, 8)
.coordinate(-186, 8)
.coordinate(-186, 0)
);
// 3/4 of an embedded 'c', crossing dateline once
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-185, 1)
.coordinate(-181, 1)
.coordinate(-181, 2)
.coordinate(-184, 2)
.coordinate(-184, 6)
.coordinate(-178, 6)
.coordinate(-178, 7)
.coordinate(-185, 7)
.coordinate(-185, 1)
)
);
// embedded hole right of the dateline
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-179, 1).coordinate(-177, 1).coordinate(-177, 2).coordinate(-179, 2).coordinate(-179, 1)
)
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testComplexShapeWithHole() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-85.0018514, 37.1311314)
.coordinate(-85.0016645, 37.1315293)
.coordinate(-85.0016246, 37.1317069)
.coordinate(-85.0016526, 37.1318183)
.coordinate(-85.0017119, 37.1319196)
.coordinate(-85.0019371, 37.1321182)
.coordinate(-85.0019972, 37.1322115)
.coordinate(-85.0019942, 37.1323234)
.coordinate(-85.0019543, 37.1324336)
.coordinate(-85.001906, 37.1324985)
.coordinate(-85.001834, 37.1325497)
.coordinate(-85.0016965, 37.1325907)
.coordinate(-85.0016011, 37.1325873)
.coordinate(-85.0014816, 37.1325353)
.coordinate(-85.0011755, 37.1323509)
.coordinate(-85.000955, 37.1322802)
.coordinate(-85.0006241, 37.1322529)
.coordinate(-85.0000002, 37.1322307)
.coordinate(-84.9994, 37.1323001)
.coordinate(-84.999109, 37.1322864)
.coordinate(-84.998934, 37.1322415)
.coordinate(-84.9988639, 37.1321888)
.coordinate(-84.9987841, 37.1320944)
.coordinate(-84.9987208, 37.131954)
.coordinate(-84.998736, 37.1316611)
.coordinate(-84.9988091, 37.131334)
.coordinate(-84.9989283, 37.1311337)
.coordinate(-84.9991943, 37.1309198)
.coordinate(-84.9993573, 37.1308459)
.coordinate(-84.9995888, 37.1307924)
.coordinate(-84.9998746, 37.130806)
.coordinate(-85.0000002, 37.1308358)
.coordinate(-85.0004984, 37.1310658)
.coordinate(-85.0008008, 37.1311625)
.coordinate(-85.0009461, 37.1311684)
.coordinate(-85.0011373, 37.1311515)
.coordinate(-85.0016455, 37.1310491)
.coordinate(-85.0018514, 37.1311314)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-85.0000002, 37.1317672)
.coordinate(-85.0001983, 37.1317538)
.coordinate(-85.0003378, 37.1317582)
.coordinate(-85.0004697, 37.131792)
.coordinate(-85.0008048, 37.1319439)
.coordinate(-85.0009342, 37.1319838)
.coordinate(-85.0010184, 37.1319463)
.coordinate(-85.0010618, 37.13184)
.coordinate(-85.0010057, 37.1315102)
.coordinate(-85.000977, 37.1314403)
.coordinate(-85.0009182, 37.1313793)
.coordinate(-85.0005366, 37.1312209)
.coordinate(-85.000224, 37.1311466)
.coordinate(-85.000087, 37.1311356)
.coordinate(-85.0000002, 37.1311433)
.coordinate(-84.9995021, 37.1312336)
.coordinate(-84.9993308, 37.1312859)
.coordinate(-84.9992567, 37.1313252)
.coordinate(-84.9991868, 37.1314277)
.coordinate(-84.9991593, 37.1315381)
.coordinate(-84.9991841, 37.1316527)
.coordinate(-84.9992329, 37.1317117)
.coordinate(-84.9993527, 37.1317788)
.coordinate(-84.9994931, 37.1318061)
.coordinate(-84.9996815, 37.1317979)
.coordinate(-85.0000002, 37.1317672)
)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithHoleAtEdgeEndPoints() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-4, 2)
.coordinate(4, 2)
.coordinate(6, 0)
.coordinate(4, -2)
.coordinate(-4, -2)
.coordinate(-6, 0)
.coordinate(-4, 2)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(4, 1).coordinate(4, -1).coordinate(-4, -1).coordinate(-4, 1).coordinate(4, 1)
)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithPointOnDateline() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, 0).coordinate(176, 4).coordinate(176, -4).coordinate(180, 0)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithEdgeAlongDateline() {
// test case 1: test the positive side of the dateline
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, 0).coordinate(176, 4).coordinate(180, -4).coordinate(180, 0)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
// test case 2: test the negative side of the dateline
builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-176, 4).coordinate(-180, 0).coordinate(-180, -4).coordinate(-176, 4)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithBoundaryHoles() {
// test case 1: test the positive side of the dateline
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(176, 10).coordinate(180, 5).coordinate(180, -5).coordinate(176, -10).coordinate(176, 10)
)
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
// test case 2: test the negative side of the dateline
builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-176, 15)
.coordinate(179, 10)
.coordinate(179, -10)
.coordinate(-176, -15)
.coordinate(-172, 0)
.close()
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-176, 10)
.coordinate(-176, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-176, 10)
.close()
)
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithHoleTouchingAtDateline() throws Exception {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-180, 90)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, 90)
.coordinate(-180, 90)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(180.0, -16.14)
.coordinate(178.53, -16.64)
.coordinate(178.49, -16.82)
.coordinate(178.73, -17.02)
.coordinate(178.86, -16.86)
.coordinate(180.0, -16.14)
)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithTangentialHole() {
// test a shape with one tangential (shared) vertex (should pass)
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(179, 10)
.coordinate(168, 15)
.coordinate(164, 0)
.coordinate(166, -15)
.coordinate(179, -10)
.coordinate(179, 10)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(-178, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-177, 10)
)
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithInvalidTangentialHole() {
// test a shape with one invalid tangential (shared) vertex (should throw exception)
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(179, 10)
.coordinate(168, 15)
.coordinate(164, 0)
.coordinate(166, -15)
.coordinate(179, -10)
.coordinate(179, 10)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(164, 0).coordinate(175, 10).coordinate(175, 5).coordinate(179, -10).coordinate(164, 0)
)
);
Exception e;
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
e = expectThrows(IllegalArgumentException.class, () -> buildGeometry(builder.close()));
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
}
public void testBoundaryShapeWithTangentialHole() {
// test a shape with one tangential (shared) vertex for each hole (should pass)
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(-178, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-177, 10)
)
);
builder.hole(
new LineStringBuilder(new CoordinatesBuilder().coordinate(172, 0).coordinate(176, 10).coordinate(176, -5).coordinate(172, 0))
);
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testBoundaryShapeWithInvalidTangentialHole() {
// test shape with two tangential (shared) vertices (should throw exception)
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-177, 10)
.coordinate(172, 0)
.coordinate(180, -5)
.coordinate(176, -10)
.coordinate(-177, 10)
)
);
Exception e;
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
e = expectThrows(IllegalArgumentException.class, () -> buildGeometry(builder.close()));
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
}
/**
* Test an enveloping polygon around the max mercator bounds
*/
public void testBoundaryShape() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(-180, 90).coordinate(180, 90).coordinate(180, -90).coordinate(-180, 90)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithAlternateOrientation() {
// cw: should produce a multi polygon spanning hemispheres
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, 0).coordinate(176, 4).coordinate(-176, 4).coordinate(180, 0)
);
ElasticsearchGeoAssertions.assertPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertPolygon(buildGeometry(builder.close()), false);
// cw: geo core will convert to ccw across the dateline
builder = new PolygonBuilder(new CoordinatesBuilder().coordinate(180, 0).coordinate(-176, 4).coordinate(176, 4).coordinate(180, 0));
ElasticsearchGeoAssertions.assertMultiPolygon(builder.close().buildS4J(), true);
ElasticsearchGeoAssertions.assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithConsecutiveDuplicatePoints() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, 0).coordinate(176, 4).coordinate(176, 4).coordinate(-176, 4).coordinate(180, 0)
);
// duplicated points are removed
PolygonBuilder expected = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, 0).coordinate(176, 4).coordinate(-176, 4).coordinate(180, 0)
);
assertEquals(buildGeometry(expected.close()), buildGeometry(builder.close()));
assertEquals(expected.close().buildS4J(), builder.close().buildS4J());
}
public void testShapeWithCoplanarVerticalPoints() throws Exception {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, -36)
.coordinate(180, 90)
.coordinate(-180, 90)
.coordinate(-180, 79)
.coordinate(16, 58)
.coordinate(8, 13)
.coordinate(-180, 74)
.coordinate(-180, -85)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, -85)
.coordinate(26, 6)
.coordinate(33, 62)
.coordinate(180, -36)
);
// coplanar points on vertical edge are removed.
PolygonBuilder expected = new PolygonBuilder(
new CoordinatesBuilder().coordinate(180, -36)
.coordinate(180, 90)
.coordinate(-180, 90)
.coordinate(-180, 79)
.coordinate(16, 58)
.coordinate(8, 13)
.coordinate(-180, 74)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, -85)
.coordinate(26, 6)
.coordinate(33, 62)
.coordinate(180, -36)
);
assertEquals(buildGeometry(expected.close()), buildGeometry(builder.close()));
assertEquals(expected.close().buildS4J(), builder.close().buildS4J());
}
public void testPolygon3D() {
String expected = "{\n"
+ " \"type\" : \"polygon\",\n"
+ " \"orientation\" : \"right\",\n"
+ " \"coordinates\" : [\n"
+ " [\n"
+ " [\n"
+ " -45.0,\n"
+ " 30.0,\n"
+ " 100.0\n"
+ " ],\n"
+ " [\n"
+ " 45.0,\n"
+ " 30.0,\n"
+ " 75.0\n"
+ " ],\n"
+ " [\n"
+ " 45.0,\n"
+ " -30.0,\n"
+ " 77.0\n"
+ " ],\n"
+ " [\n"
+ " -45.0,\n"
+ " -30.0,\n"
+ " 101.0\n"
+ " ],\n"
+ " [\n"
+ " -45.0,\n"
+ " 30.0,\n"
+ " 110.0\n"
+ " ]\n"
+ " ]\n"
+ " ]\n"
+ "}";
PolygonBuilder pb = new PolygonBuilder(
new CoordinatesBuilder().coordinate(new Coordinate(-45, 30, 100))
.coordinate(new Coordinate(45, 30, 75))
.coordinate(new Coordinate(45, -30, 77))
.coordinate(new Coordinate(-45, -30, 101))
.coordinate(new Coordinate(-45, 30, 110))
);
assertEquals(expected, pb.toString());
}
public void testInvalidSelfCrossingPolygon() {
PolygonBuilder builder = new PolygonBuilder(
new CoordinatesBuilder().coordinate(0, 0)
.coordinate(0, 2)
.coordinate(1, 1.9)
.coordinate(0.5, 1.8)
.coordinate(1.5, 1.8)
.coordinate(1, 1.9)
.coordinate(2, 2)
.coordinate(2, 0)
.coordinate(0, 0)
);
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("Self-intersection at or near point ["));
assertThat(e.getMessage(), not(containsString("NaN")));
}
public Object buildGeometry(ShapeBuilder<?, ?, ?> builder) {
return new GeoShapeIndexer(true, "name").prepareForIndexing(builder.buildGeometry());
}
}

View file

@ -6,10 +6,8 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.io.stream.Writeable.Reader;
@ -18,6 +16,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.legacygeo.GeoShapeType;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -26,7 +26,7 @@ import java.io.IOException;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder<?,?,?>> extends ESTestCase { public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder<?, ?, ?>> extends ESTestCase {
private static final int NUMBER_OF_TESTBUILDERS = 20; private static final int NUMBER_OF_TESTBUILDERS = 20;
private static NamedWriteableRegistry namedWriteableRegistry; private static NamedWriteableRegistry namedWriteableRegistry;

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
@ -32,14 +32,14 @@ public class CircleBuilderTests extends AbstractShapeBuilderTestCase<CircleBuild
if (randomBoolean()) { if (randomBoolean()) {
if (original.center().x > 0.0 || original.center().y > 0.0) { if (original.center().x > 0.0 || original.center().y > 0.0) {
mutation.center(new Coordinate(original.center().x/2, original.center().y/2)); mutation.center(new Coordinate(original.center().x / 2, original.center().y / 2));
} else { } else {
// original center was 0.0, 0.0 // original center was 0.0, 0.0
mutation.center(randomDouble() + 0.1, randomDouble() + 0.1); mutation.center(randomDouble() + 0.1, randomDouble() + 0.1);
} }
} else if (randomBoolean()) { } else if (randomBoolean()) {
if (radius > 0) { if (radius > 0) {
radius = radius/2; radius = radius / 2;
} else { } else {
radius = randomDouble() + 0.1; radius = randomDouble() + 0.1;
} }

View file

@ -6,11 +6,10 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.locationtech.spatial4j.shape.Rectangle; import org.locationtech.spatial4j.shape.Rectangle;
import java.io.IOException; import java.io.IOException;
@ -39,31 +38,40 @@ public class EnvelopeBuilderTests extends AbstractShapeBuilderTestCase<EnvelopeB
EnvelopeBuilder mutation = copyShape(original); EnvelopeBuilder mutation = copyShape(original);
// move one corner to the middle of original // move one corner to the middle of original
switch (randomIntBetween(0, 3)) { switch (randomIntBetween(0, 3)) {
case 0: case 0:
mutation = new EnvelopeBuilder( mutation = new EnvelopeBuilder(
new Coordinate(randomDoubleBetween(-180.0, original.bottomRight().x, true), original.topLeft().y), new Coordinate(randomDoubleBetween(-180.0, original.bottomRight().x, true), original.topLeft().y),
original.bottomRight()); original.bottomRight()
break; );
case 1: break;
mutation = new EnvelopeBuilder(new Coordinate(original.topLeft().x, randomDoubleBetween(original.bottomRight().y, 90.0, true)), case 1:
original.bottomRight()); mutation = new EnvelopeBuilder(
break; new Coordinate(original.topLeft().x, randomDoubleBetween(original.bottomRight().y, 90.0, true)),
case 2: original.bottomRight()
mutation = new EnvelopeBuilder(original.topLeft(), );
new Coordinate(randomDoubleBetween(original.topLeft().x, 180.0, true), original.bottomRight().y)); break;
break; case 2:
case 3: mutation = new EnvelopeBuilder(
mutation = new EnvelopeBuilder(original.topLeft(), original.topLeft(),
new Coordinate(original.bottomRight().x, randomDoubleBetween(-90.0, original.topLeft().y, true))); new Coordinate(randomDoubleBetween(original.topLeft().x, 180.0, true), original.bottomRight().y)
break; );
break;
case 3:
mutation = new EnvelopeBuilder(
original.topLeft(),
new Coordinate(original.bottomRight().x, randomDoubleBetween(-90.0, original.topLeft().y, true))
);
break;
} }
return mutation; return mutation;
} }
static EnvelopeBuilder createRandomShape() { static EnvelopeBuilder createRandomShape() {
Rectangle box = RandomShapeGenerator.xRandomRectangle(random(), RandomShapeGenerator.xRandomPoint(random())); Rectangle box = RandomShapeGenerator.xRandomRectangle(random(), RandomShapeGenerator.xRandomPoint(random()));
EnvelopeBuilder envelope = new EnvelopeBuilder(new Coordinate(box.getMinX(), box.getMaxY()), EnvelopeBuilder envelope = new EnvelopeBuilder(
new Coordinate(box.getMaxX(), box.getMinY())); new Coordinate(box.getMinX(), box.getMaxY()),
new Coordinate(box.getMaxX(), box.getMinY())
);
return envelope; return envelope;
} }
} }

View file

@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import java.io.IOException;
public class GeometryCollectionBuilderTests extends AbstractShapeBuilderTestCase<GeometryCollectionBuilder> {
@Override
protected GeometryCollectionBuilder createTestShapeBuilder() {
GeometryCollectionBuilder geometryCollection = new GeometryCollectionBuilder();
int shapes = randomIntBetween(0, 8);
for (int i = 0; i < shapes; i++) {
switch (randomIntBetween(0, 7)) {
case 0:
geometryCollection.shape(PointBuilderTests.createRandomShape());
break;
case 1:
geometryCollection.shape(CircleBuilderTests.createRandomShape());
break;
case 2:
geometryCollection.shape(EnvelopeBuilderTests.createRandomShape());
break;
case 3:
geometryCollection.shape(LineStringBuilderTests.createRandomShape());
break;
case 4:
geometryCollection.shape(MultiLineStringBuilderTests.createRandomShape());
break;
case 5:
geometryCollection.shape(MultiPolygonBuilderTests.createRandomShape());
break;
case 6:
geometryCollection.shape(MultiPointBuilderTests.createRandomShape());
break;
case 7:
geometryCollection.shape(PolygonBuilderTests.createRandomShape());
break;
}
}
return geometryCollection;
}
@Override
protected GeometryCollectionBuilder createMutation(GeometryCollectionBuilder original) throws IOException {
return mutate(original);
}
static GeometryCollectionBuilder mutate(GeometryCollectionBuilder original) throws IOException {
GeometryCollectionBuilder mutation = copyShape(original);
if (mutation.shapes.size() > 0) {
int shapePosition = randomIntBetween(0, mutation.shapes.size() - 1);
ShapeBuilder<?, ?, ?> shapeToChange = mutation.shapes.get(shapePosition);
switch (shapeToChange.type()) {
case POINT:
shapeToChange = PointBuilderTests.mutate((PointBuilder) shapeToChange);
break;
case CIRCLE:
shapeToChange = CircleBuilderTests.mutate((CircleBuilder) shapeToChange);
break;
case ENVELOPE:
shapeToChange = EnvelopeBuilderTests.mutate((EnvelopeBuilder) shapeToChange);
break;
case LINESTRING:
shapeToChange = LineStringBuilderTests.mutate((LineStringBuilder) shapeToChange);
break;
case MULTILINESTRING:
shapeToChange = MultiLineStringBuilderTests.mutate((MultiLineStringBuilder) shapeToChange);
break;
case MULTIPOLYGON:
shapeToChange = MultiPolygonBuilderTests.mutate((MultiPolygonBuilder) shapeToChange);
break;
case MULTIPOINT:
shapeToChange = MultiPointBuilderTests.mutate((MultiPointBuilder) shapeToChange);
break;
case POLYGON:
shapeToChange = PolygonBuilderTests.mutate((PolygonBuilder) shapeToChange);
break;
case GEOMETRYCOLLECTION:
throw new UnsupportedOperationException("GeometryCollection should not be nested inside each other");
}
mutation.shapes.set(shapePosition, shapeToChange);
} else {
mutation.shape(RandomShapeGenerator.createShape(random()));
}
return mutation;
}
}

View file

@ -6,10 +6,10 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.test.geo.RandomShapeGenerator; import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType; import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import java.io.IOException; import java.io.IOException;

View file

@ -6,11 +6,11 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType;
import java.io.IOException; import java.io.IOException;

View file

@ -6,13 +6,12 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@ -57,7 +56,7 @@ public class MultiPointBuilderTests extends AbstractShapeBuilderTestCase<MultiPo
} }
} }
} else { } else {
coordinates = new Coordinate[]{new Coordinate(1.0, 1.0)}; coordinates = new Coordinate[] { new Coordinate(1.0, 1.0) };
} }
return MultiPointBuilder.class.cast(mutation.coordinates(coordinates)); return MultiPointBuilder.class.cast(mutation.coordinates(coordinates));
} }

View file

@ -6,11 +6,11 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.test.geo.RandomShapeGenerator; import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType; import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.exception.InvalidShapeException;
import java.io.IOException; import java.io.IOException;
@ -59,82 +59,79 @@ public class MultiPolygonBuilderTests extends AbstractShapeBuilderTestCase<Multi
public void testInvalidPolygonBuilders() { public void testInvalidPolygonBuilders() {
try { try {
// self intersection polygon // self intersection polygon
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(
.coordinate(-10, -10) new CoordinatesBuilder().coordinate(-10, -10).coordinate(10, 10).coordinate(-10, 10).coordinate(10, -10).close()
.coordinate(10, 10) ).buildS4J();
.coordinate(-10, 10)
.coordinate(10, -10)
.close())
.buildS4J();
fail("Self intersection not detected"); fail("Self intersection not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {}
}
// polygon with hole // polygon with hole
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(new CoordinatesBuilder().coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close())
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .hole(
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5) new LineStringBuilder(
.coordinate(5, -5).close())) new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()
)
)
.buildS4J(); .buildS4J();
try { try {
// polygon with overlapping hole // polygon with overlapping hole
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) new CoordinatesBuilder().coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()
.hole(new LineStringBuilder(new CoordinatesBuilder() ).hole(
.coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close())) new LineStringBuilder(
.buildS4J(); new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close()
)
).buildS4J();
fail("Self intersection not detected"); fail("Self intersection not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {}
}
try { try {
// polygon with intersection holes // polygon with intersection holes
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) new CoordinatesBuilder().coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5) ).hole(
.coordinate(5, -5).close())) new LineStringBuilder(
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -6).coordinate(5, -6).coordinate(5, -4) new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()
.coordinate(-5, -4).close())) )
)
.hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-5, -6).coordinate(5, -6).coordinate(5, -4).coordinate(-5, -4).close()
)
)
.buildS4J(); .buildS4J();
fail("Intersection of holes not detected"); fail("Intersection of holes not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {}
}
try { try {
// Common line in polygon // Common line in polygon
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(
.coordinate(-10, -10)
.coordinate(-10, 10)
.coordinate(-5, 10)
.coordinate(-5, -5)
.coordinate(-5, 20)
.coordinate(10, 20)
.coordinate(10, -10)
.close())
.buildS4J();
fail("Self intersection not detected");
} catch (InvalidShapeException e) {
}
// Multipolygon: polygon with hole and polygon within the whole
new MultiPolygonBuilder()
.polygon(new PolygonBuilder(
new CoordinatesBuilder().coordinate(-10, -10) new CoordinatesBuilder().coordinate(-10, -10)
.coordinate(-10, 10) .coordinate(-10, 10)
.coordinate(10, 10) .coordinate(-5, 10)
.coordinate(10, -10).close()) .coordinate(-5, -5)
.hole(new LineStringBuilder( .coordinate(-5, 20)
new CoordinatesBuilder().coordinate(-5, -5) .coordinate(10, 20)
.coordinate(-5, 5) .coordinate(10, -10)
.coordinate(5, 5) .close()
.coordinate(5, -5).close()))) ).buildS4J();
.polygon(new PolygonBuilder( fail("Self intersection not detected");
new CoordinatesBuilder() } catch (InvalidShapeException e) {}
.coordinate(-4, -4)
.coordinate(-4, 4) // Multipolygon: polygon with hole and polygon within the whole
.coordinate(4, 4) new MultiPolygonBuilder().polygon(
.coordinate(4, -4).close())) new PolygonBuilder(
new CoordinatesBuilder().coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()
).hole(
new LineStringBuilder(
new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()
)
)
)
.polygon(
new PolygonBuilder(new CoordinatesBuilder().coordinate(-4, -4).coordinate(-4, 4).coordinate(4, 4).coordinate(4, -4).close())
)
.buildS4J(); .buildS4J();
} }
} }

View file

@ -6,11 +6,11 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType;
import java.io.IOException; import java.io.IOException;
@ -34,5 +34,4 @@ public class PointBuilderTests extends AbstractShapeBuilderTestCase<PointBuilder
return (PointBuilder) RandomShapeGenerator.createShape(random(), ShapeType.POINT); return (PointBuilder) RandomShapeGenerator.createShape(random(), ShapeType.POINT);
} }
} }

View file

@ -6,12 +6,12 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.common.geo.builders; package org.elasticsearch.legacygeo.builders;
import org.locationtech.jts.geom.Coordinate;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.test.geo.RandomShapeGenerator; import org.elasticsearch.legacygeo.test.RandomShapeGenerator;
import org.elasticsearch.test.geo.RandomShapeGenerator.ShapeType; import org.elasticsearch.legacygeo.test.RandomShapeGenerator.ShapeType;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.jts.JtsGeometry; import org.locationtech.spatial4j.shape.jts.JtsGeometry;
@ -70,8 +70,10 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
* This is done so we don't have to expose a setter for orientation in the actual class * This is done so we don't have to expose a setter for orientation in the actual class
*/ */
private static PolygonBuilder polyWithOposingOrientation(PolygonBuilder pb) { private static PolygonBuilder polyWithOposingOrientation(PolygonBuilder pb) {
PolygonBuilder mutation = new PolygonBuilder(pb.shell(), PolygonBuilder mutation = new PolygonBuilder(
pb.orientation() == Orientation.LEFT ? Orientation.RIGHT : Orientation.LEFT); pb.shell(),
pb.orientation() == Orientation.LEFT ? Orientation.RIGHT : Orientation.LEFT
);
for (LineStringBuilder hole : pb.holes()) { for (LineStringBuilder hole : pb.holes()) {
mutation.hole(hole); mutation.hole(hole);
} }
@ -87,30 +89,39 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
} }
public void testCoerceShell() { public void testCoerceShell() {
try{ try {
new PolygonBuilder(new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0) new PolygonBuilder(
.coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()), Orientation.RIGHT); new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()),
Orientation.RIGHT
);
fail("should raise validation exception"); fail("should raise validation exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertEquals("invalid number of points in LinearRing (found [3] - must be >= 4)", e.getMessage()); assertEquals("invalid number of points in LinearRing (found [3] - must be >= 4)", e.getMessage());
} }
PolygonBuilder pb = new PolygonBuilder(new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0) PolygonBuilder pb = new PolygonBuilder(
.coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()), Orientation.RIGHT, true); new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()),
Orientation.RIGHT,
true
);
assertThat("Shell should have been closed via coerce", pb.shell().coordinates(false).length, equalTo(4)); assertThat("Shell should have been closed via coerce", pb.shell().coordinates(false).length, equalTo(4));
} }
public void testCoerceHole() { public void testCoerceHole() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0) PolygonBuilder pb = new PolygonBuilder(
.coordinate(2.0, 0.0).coordinate(2.0, 2.0).coordinate(0.0, 0.0)); new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(2.0, 0.0).coordinate(2.0, 2.0).coordinate(0.0, 0.0)
try{ );
pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0,0.0).coordinate(1.0,0.0).coordinate(1.0,1.0).build())); try {
pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()));
fail("should raise validation exception"); fail("should raise validation exception");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
assertEquals("invalid number of points in LinearRing (found [3] - must be >= 4)", e.getMessage()); assertEquals("invalid number of points in LinearRing (found [3] - must be >= 4)", e.getMessage());
} }
pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0,0.0).coordinate(1.0,0.0).coordinate(1.0,1.0).build()), true); pb.hole(
new LineStringBuilder(new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(1.0, 0.0).coordinate(1.0, 1.0).build()),
true
);
assertThat("hole should have been closed via coerce", pb.holes().get(0).coordinates(false).length, equalTo(4)); assertThat("hole should have been closed via coerce", pb.holes().get(0).coordinates(false).length, equalTo(4));
} }
@ -140,26 +151,43 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
// ES to believe that it crosses the dateline and "fixing" it in a way // ES to believe that it crosses the dateline and "fixing" it in a way
// that self-intersects. // that self-intersects.
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() PolygonBuilder pb = new PolygonBuilder(
.coordinate(10, -20).coordinate(100, 0).coordinate(-100, 0).coordinate(20, -45).coordinate(40, -60).close()); new CoordinatesBuilder().coordinate(10, -20)
.coordinate(100, 0)
.coordinate(-100, 0)
.coordinate(20, -45)
.coordinate(40, -60)
.close()
);
pb.buildS4J(); // Should not throw an exception pb.buildS4J(); // Should not throw an exception
} }
public void testPolygonWithUndefinedOrientationDueToCollinearPoints() { public void testPolygonWithUndefinedOrientationDueToCollinearPoints() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() PolygonBuilder pb = new PolygonBuilder(
.coordinate(0.0, 0.0).coordinate(1.0, 1.0).coordinate(-1.0, -1.0).close()); new CoordinatesBuilder().coordinate(0.0, 0.0).coordinate(1.0, 1.0).coordinate(-1.0, -1.0).close()
);
InvalidShapeException e = expectThrows(InvalidShapeException.class, pb::buildS4J); InvalidShapeException e = expectThrows(InvalidShapeException.class, pb::buildS4J);
assertEquals("Cannot determine orientation: signed area equal to 0", e.getMessage()); assertEquals("Cannot determine orientation: signed area equal to 0", e.getMessage());
} }
public void testCrossingDateline() { public void testCrossingDateline() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() PolygonBuilder pb = new PolygonBuilder(
.coordinate(170, -10).coordinate(-170, -10).coordinate(-170, 10).coordinate(170, 10).coordinate(170, -10)); new CoordinatesBuilder().coordinate(170, -10)
.coordinate(-170, -10)
.coordinate(-170, 10)
.coordinate(170, 10)
.coordinate(170, -10)
);
JtsGeometry geometry = pb.buildS4J(); JtsGeometry geometry = pb.buildS4J();
assertTrue(geometry.getGeom() instanceof org.locationtech.jts.geom.MultiPolygon); assertTrue(geometry.getGeom() instanceof org.locationtech.jts.geom.MultiPolygon);
pb = new PolygonBuilder(new CoordinatesBuilder() pb = new PolygonBuilder(
.coordinate(180, -10).coordinate(-170, -5).coordinate(-170, 15).coordinate(170, -15).coordinate(180, -10)); new CoordinatesBuilder().coordinate(180, -10)
.coordinate(-170, -5)
.coordinate(-170, 15)
.coordinate(170, -15)
.coordinate(180, -10)
);
geometry = pb.buildS4J(); geometry = pb.buildS4J();
assertTrue(geometry.getGeom() instanceof org.locationtech.jts.geom.MultiPolygon); assertTrue(geometry.getGeom() instanceof org.locationtech.jts.geom.MultiPolygon);
} }

View file

@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.index.mapper; package org.elasticsearch.legacygeo.mapper;
import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableField;
import org.apache.lucene.spatial.prefix.PrefixTreeStrategy; import org.apache.lucene.spatial.prefix.PrefixTreeStrategy;
@ -13,19 +13,26 @@ import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree; import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree; import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.core.CheckedConsumer;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.core.List;
import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.geo.Orientation; import org.elasticsearch.common.geo.Orientation;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.core.CheckedConsumer;
import org.elasticsearch.core.List;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.MapperTestCase;
import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestLegacyGeoShapeFieldMapperPlugin;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@ -62,12 +69,10 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
@Override @Override
protected void registerParameters(ParameterChecker checker) throws IOException { protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("strategy", checker.registerConflictCheck("strategy", fieldMapping(this::minimalMapping), fieldMapping(b -> {
fieldMapping(this::minimalMapping), b.field("type", "geo_shape");
fieldMapping(b -> { b.field("strategy", "term");
b.field("type", "geo_shape"); }));
b.field("strategy", "term");
}));
checker.registerConflictCheck("tree", b -> b.field("tree", "geohash")); checker.registerConflictCheck("tree", b -> b.field("tree", "geohash"));
checker.registerConflictCheck("tree_levels", b -> b.field("tree_levels", 5)); checker.registerConflictCheck("tree_levels", b -> b.field("tree_levels", 5));
@ -87,7 +92,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
}); });
checker.registerUpdateCheck(b -> b.field("coerce", true), m -> { checker.registerUpdateCheck(b -> b.field("coerce", true), m -> {
LegacyGeoShapeFieldMapper gpfm = (LegacyGeoShapeFieldMapper) m; LegacyGeoShapeFieldMapper gpfm = (LegacyGeoShapeFieldMapper) m;
assertTrue(gpfm.coerce.value()); assertTrue(gpfm.coerce());
}); });
// TODO - distance_error_pct ends up being subsumed into a calculated value, how to test // TODO - distance_error_pct ends up being subsumed into a calculated value, how to test
checker.registerUpdateCheck(b -> b.field("distance_error_pct", 0.8), m -> {}); checker.registerUpdateCheck(b -> b.field("distance_error_pct", 0.8), m -> {});
@ -133,15 +138,11 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
assertEquals(Strings.toString(mapping), mapper.mappingSource().toString()); assertEquals(Strings.toString(mapping), mapper.mappingSource().toString());
LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper; LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
assertThat(geoShapeFieldMapper.fieldType().tree(), assertThat(geoShapeFieldMapper.fieldType().tree(), equalTo(LegacyGeoShapeFieldMapper.Defaults.TREE));
equalTo(LegacyGeoShapeFieldMapper.Defaults.TREE));
assertThat(geoShapeFieldMapper.fieldType().treeLevels(), equalTo(0)); assertThat(geoShapeFieldMapper.fieldType().treeLevels(), equalTo(0));
assertThat(geoShapeFieldMapper.fieldType().pointsOnly(), assertThat(geoShapeFieldMapper.fieldType().pointsOnly(), equalTo(LegacyGeoShapeFieldMapper.Defaults.POINTS_ONLY));
equalTo(LegacyGeoShapeFieldMapper.Defaults.POINTS_ONLY)); assertThat(geoShapeFieldMapper.fieldType().distanceErrorPct(), equalTo(LegacyGeoShapeFieldMapper.Defaults.DISTANCE_ERROR_PCT));
assertThat(geoShapeFieldMapper.fieldType().distanceErrorPct(), assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(Orientation.RIGHT));
equalTo(LegacyGeoShapeFieldMapper.Defaults.DISTANCE_ERROR_PCT));
assertThat(geoShapeFieldMapper.fieldType().orientation(),
equalTo(Orientation.RIGHT));
assertFieldWarnings("strategy"); assertFieldWarnings("strategy");
} }
@ -154,7 +155,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
Mapper fieldMapper = mapper.mappers().getMapper("field"); Mapper fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
Orientation orientation = ((LegacyGeoShapeFieldMapper)fieldMapper).fieldType().orientation(); Orientation orientation = ((LegacyGeoShapeFieldMapper) fieldMapper).fieldType().orientation();
assertThat(orientation, equalTo(Orientation.CLOCKWISE)); assertThat(orientation, equalTo(Orientation.CLOCKWISE));
assertThat(orientation, equalTo(Orientation.LEFT)); assertThat(orientation, equalTo(Orientation.LEFT));
assertThat(orientation, equalTo(Orientation.CW)); assertThat(orientation, equalTo(Orientation.CW));
@ -165,7 +166,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
fieldMapper = mapper.mappers().getMapper("field"); fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
orientation = ((LegacyGeoShapeFieldMapper)fieldMapper).fieldType().orientation(); orientation = ((LegacyGeoShapeFieldMapper) fieldMapper).fieldType().orientation();
assertThat(orientation, equalTo(Orientation.COUNTER_CLOCKWISE)); assertThat(orientation, equalTo(Orientation.COUNTER_CLOCKWISE));
assertThat(orientation, equalTo(Orientation.RIGHT)); assertThat(orientation, equalTo(Orientation.RIGHT));
assertThat(orientation, equalTo(Orientation.CCW)); assertThat(orientation, equalTo(Orientation.CCW));
@ -181,16 +182,14 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
Mapper fieldMapper = mapper.mappers().getMapper("field"); Mapper fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
boolean coerce = ((LegacyGeoShapeFieldMapper)fieldMapper).coerce(); boolean coerce = ((LegacyGeoShapeFieldMapper) fieldMapper).coerce();
assertThat(coerce, equalTo(true)); assertThat(coerce, equalTo(true));
// explicit false coerce test // explicit false coerce test
mapper = createDocumentMapper( mapper = createDocumentMapper(fieldMapping(b -> b.field("type", "geo_shape").field("tree", "quadtree").field("coerce", false)));
fieldMapping(b -> b.field("type", "geo_shape").field("tree", "quadtree").field("coerce", false))
);
fieldMapper = mapper.mappers().getMapper("field"); fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
coerce = ((LegacyGeoShapeFieldMapper)fieldMapper).coerce(); coerce = ((LegacyGeoShapeFieldMapper) fieldMapper).coerce();
assertThat(coerce, equalTo(false)); assertThat(coerce, equalTo(false));
assertFieldWarnings("tree", "strategy"); assertFieldWarnings("tree", "strategy");
} }
@ -204,7 +203,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
Mapper fieldMapper = mapper.mappers().getMapper("field"); Mapper fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
boolean ignoreZValue = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreZValue(); boolean ignoreZValue = ((LegacyGeoShapeFieldMapper) fieldMapper).ignoreZValue();
assertThat(ignoreZValue, equalTo(true)); assertThat(ignoreZValue, equalTo(true));
// explicit false accept_z_value test // explicit false accept_z_value test
@ -213,7 +212,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
fieldMapper = mapper.mappers().getMapper("field"); fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
ignoreZValue = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreZValue(); ignoreZValue = ((LegacyGeoShapeFieldMapper) fieldMapper).ignoreZValue();
assertThat(ignoreZValue, equalTo(false)); assertThat(ignoreZValue, equalTo(false));
assertFieldWarnings("strategy", "tree"); assertFieldWarnings("strategy", "tree");
} }
@ -227,7 +226,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
Mapper fieldMapper = mapper.mappers().getMapper("field"); Mapper fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
boolean ignoreMalformed = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreMalformed(); boolean ignoreMalformed = ((LegacyGeoShapeFieldMapper) fieldMapper).ignoreMalformed();
assertThat(ignoreMalformed, equalTo(true)); assertThat(ignoreMalformed, equalTo(true));
// explicit false ignore_malformed test // explicit false ignore_malformed test
@ -236,7 +235,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
); );
fieldMapper = mapper.mappers().getMapper("field"); fieldMapper = mapper.mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
ignoreMalformed = ((LegacyGeoShapeFieldMapper)fieldMapper).ignoreMalformed(); ignoreMalformed = ((LegacyGeoShapeFieldMapper) fieldMapper).ignoreMalformed();
assertThat(ignoreMalformed, equalTo(false)); assertThat(ignoreMalformed, equalTo(false));
assertFieldWarnings("tree", "strategy"); assertFieldWarnings("tree", "strategy");
} }
@ -465,11 +464,21 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper; LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(Orientation.CCW)); assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(Orientation.CCW));
Exception e = expectThrows(IllegalArgumentException.class, () -> merge(mapperService, fieldMapping(b -> b.field("type", "geo_shape") Exception e = expectThrows(
.field("tree", "quadtree") IllegalArgumentException.class,
.field("strategy", "term").field("precision", "1km") () -> merge(
.field("tree_levels", 26).field("distance_error_pct", 26) mapperService,
.field("orientation", "cw")))); fieldMapping(
b -> b.field("type", "geo_shape")
.field("tree", "quadtree")
.field("strategy", "term")
.field("precision", "1km")
.field("tree_levels", 26)
.field("distance_error_pct", 26)
.field("orientation", "cw")
)
)
);
assertThat(e.getMessage(), containsString("Cannot update parameter [strategy] from [recursive] to [term]")); assertThat(e.getMessage(), containsString("Cannot update parameter [strategy] from [recursive] to [term]"));
assertThat(e.getMessage(), containsString("Cannot update parameter [tree] from [geohash] to [quadtree]")); assertThat(e.getMessage(), containsString("Cannot update parameter [tree] from [geohash] to [quadtree]"));
assertThat(e.getMessage(), containsString("Cannot update parameter [tree_levels] from [8] to [26]")); assertThat(e.getMessage(), containsString("Cannot update parameter [tree_levels] from [8] to [26]"));
@ -488,12 +497,18 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(Orientation.CCW)); assertThat(geoShapeFieldMapper.fieldType().orientation(), equalTo(Orientation.CCW));
// correct mapping // correct mapping
merge(mapperService, fieldMapping(b -> b.field("type", "geo_shape") merge(
.field("tree", "geohash") mapperService,
.field("strategy", "recursive") fieldMapping(
.field("precision", "1m") b -> b.field("type", "geo_shape")
.field("tree_levels", 8).field("distance_error_pct", 0.001) .field("tree", "geohash")
.field("orientation", "cw"))); .field("strategy", "recursive")
.field("precision", "1m")
.field("tree_levels", 8)
.field("distance_error_pct", 0.001)
.field("orientation", "cw")
)
);
fieldMapper = mapperService.documentMapper().mappers().getMapper("field"); fieldMapper = mapperService.documentMapper().mappers().getMapper("field");
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
@ -594,29 +609,32 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class)); assertThat(fieldMapper, instanceOf(LegacyGeoShapeFieldMapper.class));
LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper; LegacyGeoShapeFieldMapper geoShapeFieldMapper = (LegacyGeoShapeFieldMapper) fieldMapper;
ElasticsearchException e = expectThrows(ElasticsearchException.class, ElasticsearchException e = expectThrows(
() -> geoShapeFieldMapper.fieldType().geoShapeQuery( ElasticsearchException.class,
new Point(-10, 10), "location", SpatialStrategy.TERM, ShapeRelation.INTERSECTS, searchExecutionContext)); () -> geoShapeFieldMapper.fieldType()
assertEquals("[geo-shape] queries on [PrefixTree geo shapes] cannot be executed when " + .geoShapeQuery(new Point(-10, 10), "location", SpatialStrategy.TERM, ShapeRelation.INTERSECTS, searchExecutionContext)
"'search.allow_expensive_queries' is set to false.", e.getMessage()); );
assertEquals(
"[geo-shape] queries on [PrefixTree geo shapes] cannot be executed when " + "'search.allow_expensive_queries' is set to false.",
e.getMessage()
);
assertFieldWarnings("tree", "strategy"); assertFieldWarnings("tree", "strategy");
} }
@Override @Override
protected String[] getParseMinimalWarnings() { protected String[] getParseMinimalWarnings() {
return new String[]{"Parameter [strategy] is deprecated and will be removed in a future version"}; return new String[] { "Parameter [strategy] is deprecated and will be removed in a future version" };
} }
@Override @Override
protected String[] getParseMaximalWarnings() { protected String[] getParseMaximalWarnings() {
return new String[]{ return new String[] {
"Parameter [strategy] is deprecated and will be removed in a future version", "Parameter [strategy] is deprecated and will be removed in a future version",
"Parameter [tree] is deprecated and will be removed in a future version", "Parameter [tree] is deprecated and will be removed in a future version",
"Parameter [tree_levels] is deprecated and will be removed in a future version", "Parameter [tree_levels] is deprecated and will be removed in a future version",
"Parameter [precision] is deprecated and will be removed in a future version", "Parameter [precision] is deprecated and will be removed in a future version",
"Parameter [distance_error_pct] is deprecated and will be removed in a future version", "Parameter [distance_error_pct] is deprecated and will be removed in a future version",
"Parameter [points_only] is deprecated and will be removed in a future version" "Parameter [points_only] is deprecated and will be removed in a future version" };
};
} }
public void testGeoShapeArrayParsing() throws Exception { public void testGeoShapeArrayParsing() throws Exception {
@ -636,7 +654,7 @@ public class LegacyGeoShapeFieldMapperTests extends MapperTestCase {
} }
protected void assertSearchable(MappedFieldType fieldType) { protected void assertSearchable(MappedFieldType fieldType) {
//always searchable even if it uses TextSearchInfo.NONE // always searchable even if it uses TextSearchInfo.NONE
assertTrue(fieldType.isSearchable()); assertTrue(fieldType.isSearchable());
} }

View file

@ -5,11 +5,14 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.index.mapper; package org.elasticsearch.legacygeo.mapper;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.common.geo.SpatialStrategy; import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper.GeoShapeFieldType; import org.elasticsearch.index.mapper.FieldTypeTestCase;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperBuilderContext;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper.GeoShapeFieldType;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -32,18 +35,18 @@ public class LegacyGeoShapeFieldTypeTests extends FieldTypeTestCase {
} }
public void testFetchSourceValue() throws IOException { public void testFetchSourceValue() throws IOException {
MappedFieldType mapper = new LegacyGeoShapeFieldMapper.Builder("field", Version.CURRENT, false, true).build(
MapperBuilderContext.ROOT
).fieldType();
MappedFieldType mapper = new LegacyGeoShapeFieldMapper.Builder("field", Version.CURRENT, false, true) Map<String, Object> jsonLineString = org.elasticsearch.core.Map.of(
.build(MapperBuilderContext.ROOT).fieldType(); "type",
"LineString",
Map<String, Object> jsonLineString = org.elasticsearch.core.Map.of("type", "LineString", "coordinates", "coordinates",
Arrays.asList(Arrays.asList(42.0, 27.1), Arrays.asList(30.0, 50.0))); Arrays.asList(Arrays.asList(42.0, 27.1), Arrays.asList(30.0, 50.0))
Map<String, Object> jsonPoint = org.elasticsearch.core.Map.of( );
"type", "Point", Map<String, Object> jsonPoint = org.elasticsearch.core.Map.of("type", "Point", "coordinates", Arrays.asList(14.0, 15.0));
"coordinates", Arrays.asList(14.0, 15.0)); Map<String, Object> jsonMalformed = org.elasticsearch.core.Map.of("type", "LineString", "coordinates", "foo");
Map<String, Object> jsonMalformed = org.elasticsearch.core.Map.of(
"type", "LineString",
"coordinates", "foo");
String wktLineString = "LINESTRING (42.0 27.1, 30.0 50.0)"; String wktLineString = "LINESTRING (42.0 27.1, 30.0 50.0)";
String wktPoint = "POINT (14.0 15.0)"; String wktPoint = "POINT (14.0 15.0)";
String wktMalformed = "POINT foo"; String wktMalformed = "POINT foo";

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.search.geo; package org.elasticsearch.legacygeo.search;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
@ -19,10 +19,11 @@ import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestLegacyGeoShapeFieldMapperPlugin; import org.elasticsearch.search.geo.GeoShapeQueryTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
@ -39,8 +40,7 @@ public class LegacyGeoShapeQueryTests extends GeoShapeQueryTestCase {
private static final String[] PREFIX_TREES = new String[] { private static final String[] PREFIX_TREES = new String[] {
LegacyGeoShapeFieldMapper.PrefixTrees.GEOHASH, LegacyGeoShapeFieldMapper.PrefixTrees.GEOHASH,
LegacyGeoShapeFieldMapper.PrefixTrees.QUADTREE LegacyGeoShapeFieldMapper.PrefixTrees.QUADTREE };
};
@Override @Override
protected Collection<Class<? extends Plugin>> getPlugins() { protected Collection<Class<? extends Plugin>> getPlugins() {
@ -49,8 +49,10 @@ public class LegacyGeoShapeQueryTests extends GeoShapeQueryTestCase {
@Override @Override
protected void createMapping(String indexName, String type, String fieldName, Settings settings) throws Exception { protected void createMapping(String indexName, String type, String fieldName, Settings settings) throws Exception {
final XContentBuilder xcb = XContentFactory.jsonBuilder().startObject() final XContentBuilder xcb = XContentFactory.jsonBuilder()
.startObject("properties").startObject(fieldName) .startObject()
.startObject("properties")
.startObject(fieldName)
.field("type", "geo_shape") .field("type", "geo_shape")
.field("tree", randomFrom(PREFIX_TREES)) .field("tree", randomFrom(PREFIX_TREES))
.endObject() .endObject()
@ -65,59 +67,72 @@ public class LegacyGeoShapeQueryTests extends GeoShapeQueryTestCase {
} }
public void testPointsOnlyExplicit() throws Exception { public void testPointsOnlyExplicit() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject() String mapping = Strings.toString(
.startObject("properties").startObject(defaultGeoFieldName) XContentFactory.jsonBuilder()
.field("type", "geo_shape") .startObject()
.field("tree", randomBoolean() ? "quadtree" : "geohash") .startObject("properties")
.field("tree_levels", "6") .startObject(defaultGeoFieldName)
.field("distance_error_pct", "0.01") .field("type", "geo_shape")
.field("points_only", true) .field("tree", randomBoolean() ? "quadtree" : "geohash")
.endObject() .field("tree_levels", "6")
.endObject().endObject()); .field("distance_error_pct", "0.01")
.field("points_only", true)
.endObject()
.endObject()
.endObject()
);
client().admin().indices().prepareCreate("geo_points_only").addMapping(defaultType, mapping, XContentType.JSON).get(); client().admin().indices().prepareCreate("geo_points_only").addMapping(defaultType, mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
// MULTIPOINT // MULTIPOINT
MultiPoint multiPoint = GeometryTestUtils.randomMultiPoint(false); MultiPoint multiPoint = GeometryTestUtils.randomMultiPoint(false);
client().prepareIndex("geo_points_only", defaultType).setId("1") client().prepareIndex("geo_points_only", defaultType)
.setId("1")
.setSource(GeoJson.toXContent(multiPoint, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject()) .setSource(GeoJson.toXContent(multiPoint, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject())
.setRefreshPolicy(IMMEDIATE).get(); .setRefreshPolicy(IMMEDIATE)
.get();
// POINT // POINT
Point point = GeometryTestUtils.randomPoint(false); Point point = GeometryTestUtils.randomPoint(false);
client().prepareIndex("geo_points_only", defaultType).setId("2") client().prepareIndex("geo_points_only", defaultType)
.setId("2")
.setSource(GeoJson.toXContent(point, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject()) .setSource(GeoJson.toXContent(point, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject())
.setRefreshPolicy(IMMEDIATE).get(); .setRefreshPolicy(IMMEDIATE)
.get();
// test that point was inserted // test that point was inserted
SearchResponse response = client().prepareSearch("geo_points_only") SearchResponse response = client().prepareSearch("geo_points_only").setQuery(matchAllQuery()).get();
.setQuery(matchAllQuery())
.get();
assertEquals(2, response.getHits().getTotalHits().value); assertEquals(2, response.getHits().getTotalHits().value);
} }
public void testPointsOnly() throws Exception { public void testPointsOnly() throws Exception {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject() String mapping = Strings.toString(
.startObject("properties").startObject(defaultGeoFieldName) XContentFactory.jsonBuilder()
.field("type", "geo_shape") .startObject()
.field("tree", randomBoolean() ? "quadtree" : "geohash") .startObject("properties")
.field("tree_levels", "6") .startObject(defaultGeoFieldName)
.field("distance_error_pct", "0.01") .field("type", "geo_shape")
.field("points_only", true) .field("tree", randomBoolean() ? "quadtree" : "geohash")
.endObject() .field("tree_levels", "6")
.endObject().endObject()); .field("distance_error_pct", "0.01")
.field("points_only", true)
.endObject()
.endObject()
.endObject()
);
client().admin().indices().prepareCreate("geo_points_only").addMapping(defaultType, mapping, XContentType.JSON).get(); client().admin().indices().prepareCreate("geo_points_only").addMapping(defaultType, mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
Geometry geometry = GeometryTestUtils.randomGeometry(false); Geometry geometry = GeometryTestUtils.randomGeometry(false);
try { try {
client().prepareIndex("geo_points_only", defaultType).setId("1") client().prepareIndex("geo_points_only", defaultType)
.setId("1")
.setSource(GeoJson.toXContent(geometry, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject()) .setSource(GeoJson.toXContent(geometry, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject())
.setRefreshPolicy(IMMEDIATE).get(); .setRefreshPolicy(IMMEDIATE)
.get();
} catch (MapperParsingException e) { } catch (MapperParsingException e) {
// Random geometry generator created something other than a POINT type, verify the correct exception is thrown // Random geometry generator created something other than a POINT type, verify the correct exception is thrown
assertThat(e.getMessage(), containsString("is configured for points only")); assertThat(e.getMessage(), containsString("is configured for points only"));
@ -125,37 +140,40 @@ public class LegacyGeoShapeQueryTests extends GeoShapeQueryTestCase {
} }
// test that point was inserted // test that point was inserted
SearchResponse response = SearchResponse response = client().prepareSearch("geo_points_only")
client().prepareSearch("geo_points_only").setQuery(geoIntersectionQuery(defaultGeoFieldName, geometry)).get(); .setQuery(geoIntersectionQuery(defaultGeoFieldName, geometry))
.get();
assertEquals(1, response.getHits().getTotalHits().value); assertEquals(1, response.getHits().getTotalHits().value);
} }
public void testFieldAlias() throws IOException { public void testFieldAlias() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder() String mapping = Strings.toString(
.startObject() XContentFactory.jsonBuilder()
.startObject("properties") .startObject()
.startObject(defaultGeoFieldName) .startObject("properties")
.field("type", "geo_shape") .startObject(defaultGeoFieldName)
.field("tree", randomBoolean() ? "quadtree" : "geohash") .field("type", "geo_shape")
.endObject() .field("tree", randomBoolean() ? "quadtree" : "geohash")
.startObject("alias") .endObject()
.field("type", "alias") .startObject("alias")
.field("path", defaultGeoFieldName) .field("type", "alias")
.endObject() .field("path", defaultGeoFieldName)
.endObject() .endObject()
.endObject()); .endObject()
.endObject()
);
client().admin().indices().prepareCreate(defaultIndexName).addMapping(defaultType, mapping, XContentType.JSON).get(); client().admin().indices().prepareCreate(defaultIndexName).addMapping(defaultType, mapping, XContentType.JSON).get();
ensureGreen(); ensureGreen();
MultiPoint multiPoint = GeometryTestUtils.randomMultiPoint(false); MultiPoint multiPoint = GeometryTestUtils.randomMultiPoint(false);
client().prepareIndex(defaultIndexName, defaultType).setId("1") client().prepareIndex(defaultIndexName, defaultType)
.setId("1")
.setSource(GeoJson.toXContent(multiPoint, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject()) .setSource(GeoJson.toXContent(multiPoint, jsonBuilder().startObject().field(defaultGeoFieldName), null).endObject())
.setRefreshPolicy(IMMEDIATE).get(); .setRefreshPolicy(IMMEDIATE)
SearchResponse response = client().prepareSearch(defaultIndexName)
.setQuery(geoShapeQuery("alias", multiPoint))
.get(); .get();
SearchResponse response = client().prepareSearch(defaultIndexName).setQuery(geoShapeQuery("alias", multiPoint)).get();
assertEquals(1, response.getHits().getTotalHits().value); assertEquals(1, response.getHits().getTotalHits().value);
} }
} }

View file

@ -0,0 +1,86 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo.search;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.test.ESTestCase;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
public class LegacyGeoUtilsTests extends ESTestCase {
public void testPrefixTreeCellSizes() {
assertThat(GeoUtils.EARTH_SEMI_MAJOR_AXIS, equalTo(DistanceUtils.EARTH_EQUATORIAL_RADIUS_KM * 1000));
assertThat(GeoUtils.quadTreeCellWidth(0), lessThanOrEqualTo(GeoUtils.EARTH_EQUATOR));
SpatialContext spatialContext = new SpatialContext(true);
GeohashPrefixTree geohashPrefixTree = new GeohashPrefixTree(spatialContext, GeohashPrefixTree.getMaxLevelsPossible() / 2);
Cell gNode = geohashPrefixTree.getWorldCell();
for (int i = 0; i < geohashPrefixTree.getMaxLevels(); i++) {
double width = GeoUtils.geoHashCellWidth(i);
double height = GeoUtils.geoHashCellHeight(i);
double size = GeoUtils.geoHashCellSize(i);
double degrees = 360.0 * width / GeoUtils.EARTH_EQUATOR;
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.geoHashLevelsForPrecision(size), equalTo(geohashPrefixTree.getLevelForDistance(degrees)));
assertThat(
"width at level " + i,
gNode.getShape().getBoundingBox().getWidth(),
equalTo(360.d * width / GeoUtils.EARTH_EQUATOR)
);
assertThat(
"height at level " + i,
gNode.getShape().getBoundingBox().getHeight(),
equalTo(180.d * height / GeoUtils.EARTH_POLAR_DISTANCE)
);
gNode = gNode.getNextLevelCells(null).next();
}
QuadPrefixTree quadPrefixTree = new QuadPrefixTree(spatialContext);
Cell qNode = quadPrefixTree.getWorldCell();
for (int i = 0; i < quadPrefixTree.getMaxLevels(); i++) {
double degrees = 360.0 / (1L << i);
double width = GeoUtils.quadTreeCellWidth(i);
double height = GeoUtils.quadTreeCellHeight(i);
double size = GeoUtils.quadTreeCellSize(i);
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.quadTreeLevelsForPrecision(size), equalTo(quadPrefixTree.getLevelForDistance(degrees)));
assertThat(
"width at level " + i,
qNode.getShape().getBoundingBox().getWidth(),
equalTo(360.d * width / GeoUtils.EARTH_EQUATOR)
);
assertThat(
"height at level " + i,
qNode.getShape().getBoundingBox().getHeight(),
equalTo(180.d * height / GeoUtils.EARTH_POLAR_DISTANCE)
);
qNode = qNode.getNextLevelCells(null).next();
}
}
}

View file

@ -6,15 +6,15 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.test.hamcrest; package org.elasticsearch.legacygeo.test;
import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geometry.Line; import org.elasticsearch.geometry.Line;
import org.elasticsearch.geometry.MultiLine; import org.elasticsearch.geometry.MultiLine;
import org.elasticsearch.legacygeo.parsers.ShapeParser;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.junit.Assert; import org.junit.Assert;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
@ -40,13 +40,13 @@ import static org.junit.Assert.assertTrue;
public class ElasticsearchGeoAssertions { public class ElasticsearchGeoAssertions {
private static int top(Coordinate...points) { private static int top(Coordinate... points) {
int top = 0; int top = 0;
for (int i = 1; i < points.length; i++) { for (int i = 1; i < points.length; i++) {
if(points[i].y < points[top].y) { if (points[i].y < points[top].y) {
top = i; top = i;
} else if(points[i].y == points[top].y) { } else if (points[i].y == points[top].y) {
if(points[i].x <= points[top].x) { if (points[i].x <= points[top].x) {
top = i; top = i;
} }
} }
@ -54,20 +54,20 @@ public class ElasticsearchGeoAssertions {
return top; return top;
} }
private static int prev(int top, Coordinate...points) { private static int prev(int top, Coordinate... points) {
for (int i = 1; i < points.length; i++) { for (int i = 1; i < points.length; i++) {
int p = (top + points.length - i) % points.length; int p = (top + points.length - i) % points.length;
if((points[p].x != points[top].x) || (points[p].y != points[top].y)) { if ((points[p].x != points[top].x) || (points[p].y != points[top].y)) {
return p; return p;
} }
} }
return -1; return -1;
} }
private static int next(int top, Coordinate...points) { private static int next(int top, Coordinate... points) {
for (int i = 1; i < points.length; i++) { for (int i = 1; i < points.length; i++) {
int n = (top + i) % points.length; int n = (top + i) % points.length;
if((points[n].x != points[top].x) || (points[n].y != points[top].y)) { if ((points[n].x != points[top].x) || (points[n].y != points[top].y)) {
return n; return n;
} }
} }
@ -85,16 +85,16 @@ public class ElasticsearchGeoAssertions {
final int prev = prev(top, points); final int prev = prev(top, points);
final boolean orientation = points[next].x < points[prev].x; final boolean orientation = points[next].x < points[prev].x;
if(orientation != direction) { if (orientation != direction) {
List<Coordinate> asList = Arrays.asList(points); List<Coordinate> asList = Arrays.asList(points);
Collections.reverse(asList); Collections.reverse(asList);
return fixedOrderedRing(asList, direction); return fixedOrderedRing(asList, direction);
} else { } else {
if(top>0) { if (top > 0) {
Coordinate[] aligned = new Coordinate[points.length]; Coordinate[] aligned = new Coordinate[points.length];
System.arraycopy(points, top, aligned, 0, points.length-top-1); System.arraycopy(points, top, aligned, 0, points.length - top - 1);
System.arraycopy(points, 0, aligned, points.length-top-1, top); System.arraycopy(points, 0, aligned, points.length - top - 1, top);
aligned[aligned.length-1] = aligned[0]; aligned[aligned.length - 1] = aligned[0];
return aligned; return aligned;
} else { } else {
return points; return points;
@ -108,13 +108,13 @@ public class ElasticsearchGeoAssertions {
} }
private static boolean isRing(Coordinate[] c) { private static boolean isRing(Coordinate[] c) {
return (c[0].x == c[c.length-1].x) && (c[0].y == c[c.length-1].y); return (c[0].x == c[c.length - 1].x) && (c[0].y == c[c.length - 1].y);
} }
public static void assertEquals(Coordinate[] c1, Coordinate[] c2) { public static void assertEquals(Coordinate[] c1, Coordinate[] c2) {
Assert.assertEquals(c1.length, c2.length); Assert.assertEquals(c1.length, c2.length);
if(isRing(c1) && isRing(c2)) { if (isRing(c1) && isRing(c2)) {
c1 = fixedOrderedRing(c1, true); c1 = fixedOrderedRing(c1, true);
c2 = fixedOrderedRing(c2, true); c2 = fixedOrderedRing(c2, true);
} }
@ -157,7 +157,7 @@ public class ElasticsearchGeoAssertions {
} }
public static void assertEquals(Geometry s1, Geometry s2) { public static void assertEquals(Geometry s1, Geometry s2) {
if(s1 instanceof LineString && s2 instanceof LineString) { if (s1 instanceof LineString && s2 instanceof LineString) {
assertEquals((LineString) s1, (LineString) s2); assertEquals((LineString) s1, (LineString) s2);
} else if (s1 instanceof Polygon && s2 instanceof Polygon) { } else if (s1 instanceof Polygon && s2 instanceof Polygon) {
@ -173,8 +173,9 @@ public class ElasticsearchGeoAssertions {
assertEquals((MultiLineString) s1, (MultiLineString) s2); assertEquals((MultiLineString) s1, (MultiLineString) s2);
} else { } else {
throw new RuntimeException("equality of shape types not supported [" + s1.getClass().getName() + " and " + throw new RuntimeException(
s2.getClass().getName() + "]"); "equality of shape types not supported [" + s1.getClass().getName() + " and " + s2.getClass().getName() + "]"
);
} }
} }
@ -190,9 +191,9 @@ public class ElasticsearchGeoAssertions {
} }
public static void assertEquals(Object s1, Object s2) { public static void assertEquals(Object s1, Object s2) {
if(s1 instanceof JtsGeometry && s2 instanceof JtsGeometry) { if (s1 instanceof JtsGeometry && s2 instanceof JtsGeometry) {
assertEquals((JtsGeometry) s1, (JtsGeometry) s2); assertEquals((JtsGeometry) s1, (JtsGeometry) s2);
} else if(s1 instanceof JtsPoint && s2 instanceof JtsPoint) { } else if (s1 instanceof JtsPoint && s2 instanceof JtsPoint) {
JtsPoint p1 = (JtsPoint) s1; JtsPoint p1 = (JtsPoint) s1;
JtsPoint p2 = (JtsPoint) s2; JtsPoint p2 = (JtsPoint) s2;
Assert.assertEquals(p1, p2); Assert.assertEquals(p1, p2);
@ -203,76 +204,78 @@ public class ElasticsearchGeoAssertions {
} else if (s1 instanceof RectangleImpl && s2 instanceof RectangleImpl) { } else if (s1 instanceof RectangleImpl && s2 instanceof RectangleImpl) {
Assert.assertEquals(s1, s2); Assert.assertEquals(s1, s2);
} else if (s1 instanceof org.apache.lucene.geo.Line[] && s2 instanceof org.apache.lucene.geo.Line[]) { } else if (s1 instanceof org.apache.lucene.geo.Line[] && s2 instanceof org.apache.lucene.geo.Line[]) {
Assert.assertArrayEquals((org.apache.lucene.geo.Line[])s1, (org.apache.lucene.geo.Line[])s2); Assert.assertArrayEquals((org.apache.lucene.geo.Line[]) s1, (org.apache.lucene.geo.Line[]) s2);
} else if (s1 instanceof org.apache.lucene.geo.Polygon[] && s2 instanceof org.apache.lucene.geo.Polygon[]) { } else if (s1 instanceof org.apache.lucene.geo.Polygon[] && s2 instanceof org.apache.lucene.geo.Polygon[]) {
Assert.assertArrayEquals((org.apache.lucene.geo.Polygon[]) s1, (org.apache.lucene.geo.Polygon[]) s2); Assert.assertArrayEquals((org.apache.lucene.geo.Polygon[]) s1, (org.apache.lucene.geo.Polygon[]) s2);
} else if ((s1 instanceof org.apache.lucene.geo.Line && s2 instanceof org.apache.lucene.geo.Line) } else if ((s1 instanceof org.apache.lucene.geo.Line && s2 instanceof org.apache.lucene.geo.Line)
|| (s1 instanceof org.apache.lucene.geo.Polygon && s2 instanceof org.apache.lucene.geo.Polygon) || (s1 instanceof org.apache.lucene.geo.Polygon && s2 instanceof org.apache.lucene.geo.Polygon)
|| (s1 instanceof org.apache.lucene.geo.Rectangle && s2 instanceof org.apache.lucene.geo.Rectangle) || (s1 instanceof org.apache.lucene.geo.Rectangle && s2 instanceof org.apache.lucene.geo.Rectangle)
|| (s1 instanceof GeoPoint && s2 instanceof GeoPoint)) { || (s1 instanceof GeoPoint && s2 instanceof GeoPoint)) {
Assert.assertEquals(s1, s2); Assert.assertEquals(s1, s2);
} else if (s1 instanceof Object[] && s2 instanceof Object[]) { } else if (s1 instanceof Object[] && s2 instanceof Object[]) {
Assert.assertArrayEquals((Object[]) s1, (Object[]) s2); Assert.assertArrayEquals((Object[]) s1, (Object[]) s2);
} else if (s1 instanceof org.elasticsearch.geometry.Geometry && s2 instanceof org.elasticsearch.geometry.Geometry) { } else if (s1 instanceof org.elasticsearch.geometry.Geometry && s2 instanceof org.elasticsearch.geometry.Geometry) {
Assert.assertEquals(s1, s2); Assert.assertEquals(s1, s2);
} else { } else {
//We want to know the type of the shape because we test shape equality in a special way... // We want to know the type of the shape because we test shape equality in a special way...
//... in particular we test that one ring is equivalent to another ring even if the points are rotated or reversed. // ... in particular we test that one ring is equivalent to another ring even if the points are rotated or reversed.
throw new RuntimeException( throw new RuntimeException(
"equality of shape types not supported [" + s1.getClass().getName() + " and " + s2.getClass().getName() + "]"); "equality of shape types not supported [" + s1.getClass().getName() + " and " + s2.getClass().getName() + "]"
} );
}
} }
@Deprecated @Deprecated
private static Geometry unwrapJTS(Object shape) { private static Geometry unwrapJTS(Object shape) {
assertThat(shape, instanceOf(JtsGeometry.class)); assertThat(shape, instanceOf(JtsGeometry.class));
return ((JtsGeometry)shape).getGeom(); return ((JtsGeometry) shape).getGeom();
} }
public static void assertMultiPolygon(Object shape, boolean useJTS) { public static void assertMultiPolygon(Object shape, boolean useJTS) {
if (useJTS) { if (useJTS) {
assertTrue("expected MultiPolygon but found " + unwrapJTS(shape).getClass().getName(), assertTrue(
unwrapJTS(shape) instanceof MultiPolygon); "expected MultiPolygon but found " + unwrapJTS(shape).getClass().getName(),
unwrapJTS(shape) instanceof MultiPolygon
);
} else { } else {
assertTrue("expected Polygon[] but found " + shape.getClass().getName(), assertTrue(
shape instanceof org.elasticsearch.geometry.MultiPolygon); "expected Polygon[] but found " + shape.getClass().getName(),
shape instanceof org.elasticsearch.geometry.MultiPolygon
);
} }
} }
public static void assertPolygon(Object shape, boolean useJTS) { public static void assertPolygon(Object shape, boolean useJTS) {
if (useJTS) { if (useJTS) {
assertTrue("expected Polygon but found " assertTrue("expected Polygon but found " + unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof Polygon);
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof Polygon);
} else { } else {
assertTrue("expected Polygon but found " + shape.getClass().getName(), assertTrue("expected Polygon but found " + shape.getClass().getName(), shape instanceof org.elasticsearch.geometry.Polygon);
shape instanceof org.elasticsearch.geometry.Polygon);
} }
} }
public static void assertLineString(Object shape, boolean useJTS) { public static void assertLineString(Object shape, boolean useJTS) {
if (useJTS) { if (useJTS) {
assertTrue("expected LineString but found " assertTrue("expected LineString but found " + unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof LineString);
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof LineString);
} else { } else {
assertTrue("expected Line but found " + shape.getClass().getName(), assertTrue("expected Line but found " + shape.getClass().getName(), shape instanceof Line);
shape instanceof Line);
} }
} }
public static void assertMultiLineString(Object shape, boolean useJTS) { public static void assertMultiLineString(Object shape, boolean useJTS) {
if (useJTS) { if (useJTS) {
assertTrue("expected MultiLineString but found " assertTrue(
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof MultiLineString); "expected MultiLineString but found " + unwrapJTS(shape).getClass().getName(),
unwrapJTS(shape) instanceof MultiLineString
);
} else { } else {
assertTrue("expected Line[] but found " + shape.getClass().getName(), assertTrue("expected Line[] but found " + shape.getClass().getName(), shape instanceof MultiLine);
shape instanceof MultiLine);
} }
} }
public static void assertDistance(String geohash1, String geohash2, Matcher<Double> match) { public static void assertDistance(String geohash1, String geohash2, Matcher<Double> match) {
GeoPoint p1 = new GeoPoint(geohash1); GeoPoint p1 = new GeoPoint(geohash1);
GeoPoint p2 = new GeoPoint(geohash2); GeoPoint p2 = new GeoPoint(geohash2);
assertDistance(p1.lat(), p1.lon(), p2.lat(),p2.lon(), match); assertDistance(p1.lat(), p1.lon(), p2.lat(), p2.lon(), match);
} }
public static void assertDistance(double lat1, double lon1, double lat2, double lon2, Matcher<Double> match) { public static void assertDistance(double lat1, double lon1, double lat2, double lon2, Matcher<Double> match) {
@ -288,8 +291,10 @@ public class ElasticsearchGeoAssertions {
ShapeParser.parse(parser).buildS4J(); ShapeParser.parse(parser).buildS4J();
Assert.fail("process completed successfully when " + expectedException.getName() + " expected"); Assert.fail("process completed successfully when " + expectedException.getName() + " expected");
} catch (Exception e) { } catch (Exception e) {
assertTrue("expected " + expectedException.getName() + " but found " + e.getClass().getName(), assertTrue(
e.getClass().equals(expectedException)); "expected " + expectedException.getName() + " but found " + e.getClass().getName(),
e.getClass().equals(expectedException)
);
} }
} }
} }

View file

@ -0,0 +1,85 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.legacygeo.test;
import org.elasticsearch.common.geo.GeoPoint;
import java.util.Random;
/**
* Random geo generation utilities for randomized {@code geo_point} type testing
* does not depend on jts or spatial4j. Use {@link org.elasticsearch.legacygeo.test.RandomShapeGenerator}
* to create random OGC compliant shapes.
*/
public class RandomGeoGenerator {
public static void randomPoint(Random r, double[] pt) {
final double[] min = { -180, -90 };
final double[] max = { 180, 90 };
randomPointIn(r, min[0], min[1], max[0], max[1], pt);
}
public static void randomPointIn(
Random r,
final double minLon,
final double minLat,
final double maxLon,
final double maxLat,
double[] pt
) {
assert pt != null && pt.length == 2;
// normalize min and max
double[] min = { normalizeLongitude(minLon), normalizeLatitude(minLat) };
double[] max = { normalizeLongitude(maxLon), normalizeLatitude(maxLat) };
final double[] tMin = new double[2];
final double[] tMax = new double[2];
tMin[0] = Math.min(min[0], max[0]);
tMax[0] = Math.max(min[0], max[0]);
tMin[1] = Math.min(min[1], max[1]);
tMax[1] = Math.max(min[1], max[1]);
pt[0] = tMin[0] + r.nextDouble() * (tMax[0] - tMin[0]);
pt[1] = tMin[1] + r.nextDouble() * (tMax[1] - tMin[1]);
}
public static GeoPoint randomPoint(Random r) {
return randomPointIn(r, -180, -90, 180, 90);
}
public static GeoPoint randomPointIn(Random r, final double minLon, final double minLat, final double maxLon, final double maxLat) {
double[] pt = new double[2];
randomPointIn(r, minLon, minLat, maxLon, maxLat, pt);
return new GeoPoint(pt[1], pt[0]);
}
/** Puts latitude in range of -90 to 90. */
private static double normalizeLatitude(double latitude) {
if (latitude >= -90 && latitude <= 90) {
return latitude; // common case, and avoids slight double precision shifting
}
double off = Math.abs((latitude + 90) % 360);
return (off <= 180 ? off : 360 - off) - 90;
}
/** Puts longitude in range of -180 to +180. */
private static double normalizeLongitude(double longitude) {
if (longitude >= -180 && longitude <= 180) {
return longitude; // common case, and avoids slight double precision shifting
}
double off = (longitude + 180) % 360;
if (off < 0) {
return 180 + off;
} else if (off == 0 && longitude > 0) {
return 180;
} else {
return -180 + off;
}
}
}

View file

@ -6,22 +6,23 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.test.geo; package org.elasticsearch.legacygeo.test;
import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.legacygeo.builders.CoordinatesBuilder;
import org.elasticsearch.legacygeo.builders.GeometryCollectionBuilder;
import org.elasticsearch.legacygeo.builders.LineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiLineStringBuilder;
import org.elasticsearch.legacygeo.builders.MultiPointBuilder;
import org.elasticsearch.legacygeo.builders.PointBuilder;
import org.elasticsearch.legacygeo.builders.PolygonBuilder;
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
import org.junit.Assert;
import org.locationtech.jts.algorithm.ConvexHull; import org.locationtech.jts.algorithm.ConvexHull;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.GeometryCollectionBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.junit.Assert;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils; import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.exception.InvalidShapeException;
@ -44,7 +45,12 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
protected static boolean ST_VALIDATE = true; protected static boolean ST_VALIDATE = true;
public enum ShapeType { public enum ShapeType {
POINT, MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON; POINT,
MULTIPOINT,
LINESTRING,
MULTILINESTRING,
POLYGON;
private static final ShapeType[] types = values(); private static final ShapeType[] types = values();
public static ShapeType randomType(Random r) { public static ShapeType randomType(Random r) {
@ -84,8 +90,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
return createGeometryCollection(r, nearPoint, null, 0); return createGeometryCollection(r, nearPoint, null, 0);
} }
public static GeometryCollectionBuilder createGeometryCollectionNear(Random r, Point nearPoint, int size) throws public static GeometryCollectionBuilder createGeometryCollectionNear(Random r, Point nearPoint, int size) throws InvalidShapeException {
InvalidShapeException {
return createGeometryCollection(r, nearPoint, null, size); return createGeometryCollection(r, nearPoint, null, size);
} }
@ -93,13 +98,13 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
return createGeometryCollection(r, null, within, 0); return createGeometryCollection(r, null, within, 0);
} }
public static GeometryCollectionBuilder createGeometryCollectionWithin(Random r, Rectangle within, int size) throws public static GeometryCollectionBuilder createGeometryCollectionWithin(Random r, Rectangle within, int size)
InvalidShapeException { throws InvalidShapeException {
return createGeometryCollection(r, null, within, size); return createGeometryCollection(r, null, within, size);
} }
protected static GeometryCollectionBuilder createGeometryCollection(Random r, Point nearPoint, Rectangle bounds, int numGeometries) protected static GeometryCollectionBuilder createGeometryCollection(Random r, Point nearPoint, Rectangle bounds, int numGeometries)
throws InvalidShapeException { throws InvalidShapeException {
if (numGeometries <= 0) { if (numGeometries <= 0) {
// cap geometry collection at 4 shapes (to save test time) // cap geometry collection at 4 shapes (to save test time)
numGeometries = RandomNumbers.randomIntBetween(r, 2, 4); numGeometries = RandomNumbers.randomIntBetween(r, 2, 4);
@ -114,7 +119,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
} }
GeometryCollectionBuilder gcb = new GeometryCollectionBuilder(); GeometryCollectionBuilder gcb = new GeometryCollectionBuilder();
for (int i=0; i<numGeometries;) { for (int i = 0; i < numGeometries;) {
ShapeBuilder<?, ?, ?> builder = createShapeWithin(r, bounds); ShapeBuilder<?, ?, ?> builder = createShapeWithin(r, bounds);
// due to world wrapping, and the possibility for ambiguous polygons, the random shape generation could bail with // due to world wrapping, and the possibility for ambiguous polygons, the random shape generation could bail with
// a null shape. We catch that situation here, and only increment the counter when a valid shape is returned. // a null shape. We catch that situation here, and only increment the counter when a valid shape is returned.
@ -130,7 +135,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
private static ShapeBuilder<?, ?, ?> createShape(Random r, Point nearPoint, Rectangle within, ShapeType st) private static ShapeBuilder<?, ?, ?> createShape(Random r, Point nearPoint, Rectangle within, ShapeType st)
throws InvalidShapeException { throws InvalidShapeException {
ShapeBuilder<?, ?, ?> shape; ShapeBuilder<?, ?, ?> shape;
short i=0; short i = 0;
do { do {
shape = createShape(r, nearPoint, within, st, ST_VALIDATE); shape = createShape(r, nearPoint, within, st, ST_VALIDATE);
if (shape != null) { if (shape != null) {
@ -150,8 +155,8 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
* @param st Create a random shape of the provided type * @param st Create a random shape of the provided type
* @return the ShapeBuilder for a random shape * @return the ShapeBuilder for a random shape
*/ */
private static ShapeBuilder<?, ?, ?> createShape(Random r, Point nearPoint, Rectangle within, ShapeType st, boolean validate) throws private static ShapeBuilder<?, ?, ?> createShape(Random r, Point nearPoint, Rectangle within, ShapeType st, boolean validate)
InvalidShapeException { throws InvalidShapeException {
if (st == null) { if (st == null) {
st = ShapeType.randomType(r); st = ShapeType.randomType(r);
@ -176,7 +181,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
// (n^2-n)/2 and computing the relation intersection matrix will become NP-Hard // (n^2-n)/2 and computing the relation intersection matrix will become NP-Hard
int numPoints = RandomNumbers.randomIntBetween(r, 3, 10); int numPoints = RandomNumbers.randomIntBetween(r, 3, 10);
CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder(); CoordinatesBuilder coordinatesBuilder = new CoordinatesBuilder();
for (int i=0; i<numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
p = xRandomPointIn(r, within); p = xRandomPointIn(r, within);
coordinatesBuilder.coordinate(p.getX(), p.getY()); coordinatesBuilder.coordinate(p.getX(), p.getY());
} }
@ -186,14 +191,14 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
return pcb; return pcb;
case MULTILINESTRING: case MULTILINESTRING:
MultiLineStringBuilder mlsb = new MultiLineStringBuilder(); MultiLineStringBuilder mlsb = new MultiLineStringBuilder();
for (int i=0; i<RandomNumbers.randomIntBetween(r, 1, 10); ++i) { for (int i = 0; i < RandomNumbers.randomIntBetween(r, 1, 10); ++i) {
mlsb.linestring((LineStringBuilder) createShape(r, nearPoint, within, ShapeType.LINESTRING, false)); mlsb.linestring((LineStringBuilder) createShape(r, nearPoint, within, ShapeType.LINESTRING, false));
} }
return mlsb; return mlsb;
case POLYGON: case POLYGON:
numPoints = RandomNumbers.randomIntBetween(r, 5, 25); numPoints = RandomNumbers.randomIntBetween(r, 5, 25);
Coordinate[] coordinates = new Coordinate[numPoints]; Coordinate[] coordinates = new Coordinate[numPoints];
for (int i=0; i<numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
p = (Point) createShape(r, nearPoint, within, ShapeType.POINT, false).buildS4J(); p = (Point) createShape(r, nearPoint, within, ShapeType.POINT, false).buildS4J();
coordinates[i] = new Coordinate(p.getX(), p.getY()); coordinates[i] = new Coordinate(p.getX(), p.getY());
} }
@ -236,15 +241,14 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
protected static Point xRandomPointIn(Random rand, Rectangle r) { protected static Point xRandomPointIn(Random rand, Rectangle r) {
double[] pt = new double[2]; double[] pt = new double[2];
randomPointIn(rand, r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY(), pt); RandomGeoGenerator.randomPointIn(rand, r.getMinX(), r.getMinY(), r.getMaxX(), r.getMaxY(), pt);
Point p = ctx.makePoint(pt[0], pt[1]); Point p = ctx.makePoint(pt[0], pt[1]);
Assert.assertEquals(CONTAINS, r.relate(p)); Assert.assertEquals(CONTAINS, r.relate(p));
return p; return p;
} }
private static Rectangle xRandomRectangle(Random r, Point nearP, Rectangle bounds, boolean small) { private static Rectangle xRandomRectangle(Random r, Point nearP, Rectangle bounds, boolean small) {
if (nearP == null) if (nearP == null) nearP = xRandomPointIn(r, bounds);
nearP = xRandomPointIn(r, bounds);
if (small) { if (small) {
// between 3 and 6 degrees // between 3 and 6 degrees
@ -271,10 +275,11 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
Range yRange = xRandomRange(r, rarely(r) ? 0 : nearP.getY(), Range.yRange(bounds, ctx)); Range yRange = xRandomRange(r, rarely(r) ? 0 : nearP.getY(), Range.yRange(bounds, ctx));
return xMakeNormRect( return xMakeNormRect(
xDivisible(xRange.getMin()*10e3)/10e3, xDivisible(xRange.getMin() * 10e3) / 10e3,
xDivisible(xRange.getMax()*10e3)/10e3, xDivisible(xRange.getMax() * 10e3) / 10e3,
xDivisible(yRange.getMin()*10e3)/10e3, xDivisible(yRange.getMin() * 10e3) / 10e3,
xDivisible(yRange.getMax()*10e3)/10e3); xDivisible(yRange.getMax() * 10e3) / 10e3
);
} }
/** creates a small random rectangle by default to keep shape test performance at bay */ /** creates a small random rectangle by default to keep shape test performance at bay */
@ -292,7 +297,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
private static Range xRandomRange(Random r, double near, Range bounds) { private static Range xRandomRange(Random r, double near, Range bounds) {
double mid = near + r.nextGaussian() * bounds.getWidth() / 6; double mid = near + r.nextGaussian() * bounds.getWidth() / 6;
double width = Math.abs(r.nextGaussian()) * bounds.getWidth() / 6;//1/3rd double width = Math.abs(r.nextGaussian()) * bounds.getWidth() / 6;// 1/3rd
return new Range(mid - width / 2, mid + width / 2); return new Range(mid - width / 2, mid + width / 2);
} }

View file

@ -5,25 +5,22 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server * in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1. * Side Public License, v 1.
*/ */
package org.elasticsearch.test; package org.elasticsearch.legacygeo.test;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.legacygeo.LegacyGeoPlugin;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.MapperPlugin;
import org.elasticsearch.plugins.Plugin;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
* Some tests depend on the {@link LegacyGeoShapeFieldMapper}. * Test plugin to test functionality of this mapper.
* This mapper is registered in the spatial-extras module, but used in some integration
* tests in server code. The goal is to migrate all of the spatial/geo pieces to the spatial-extras
* module such that no tests in server depend on this test plugin
*/ */
@Deprecated @Deprecated
public class TestLegacyGeoShapeFieldMapperPlugin extends Plugin implements MapperPlugin { public class TestLegacyGeoShapeFieldMapperPlugin extends LegacyGeoPlugin implements MapperPlugin {
@Override @Override
public Map<String, Mapper.TypeParser> getMappers() { public Map<String, Mapper.TypeParser> getMappers() {

View file

@ -84,7 +84,6 @@ dependencies {
api "org.apache.lucene:lucene-queries:${versions.lucene}" api "org.apache.lucene:lucene-queries:${versions.lucene}"
api "org.apache.lucene:lucene-queryparser:${versions.lucene}" api "org.apache.lucene:lucene-queryparser:${versions.lucene}"
api "org.apache.lucene:lucene-sandbox:${versions.lucene}" api "org.apache.lucene:lucene-sandbox:${versions.lucene}"
api "org.apache.lucene:lucene-spatial-extras:${versions.lucene}"
api "org.apache.lucene:lucene-spatial3d:${versions.lucene}" api "org.apache.lucene:lucene-spatial3d:${versions.lucene}"
api "org.apache.lucene:lucene-suggest:${versions.lucene}" api "org.apache.lucene:lucene-suggest:${versions.lucene}"
@ -103,10 +102,6 @@ dependencies {
// precentil ranks aggregation // precentil ranks aggregation
api 'org.hdrhistogram:HdrHistogram:2.1.9' api 'org.hdrhistogram:HdrHistogram:2.1.9'
// lucene spatial
api "org.locationtech.spatial4j:spatial4j:${versions.spatial4j}", optional
api "org.locationtech.jts:jts-core:${versions.jts}", optional
// logging // logging
api "org.apache.logging.log4j:log4j-api:${versions.log4j}" api "org.apache.logging.log4j:log4j-api:${versions.log4j}"
api "org.apache.logging.log4j:log4j-core:${versions.log4j}", optional api "org.apache.logging.log4j:log4j-core:${versions.log4j}", optional
@ -202,7 +197,6 @@ tasks.named("thirdPartyAudit").configure {
'com.fasterxml.jackson.dataformat.xml.JacksonXmlModule', 'com.fasterxml.jackson.dataformat.xml.JacksonXmlModule',
'com.fasterxml.jackson.dataformat.xml.XmlMapper', 'com.fasterxml.jackson.dataformat.xml.XmlMapper',
'com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter', 'com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter',
'com.fasterxml.jackson.databind.node.ObjectNode',
'org.fusesource.jansi.Ansi', 'org.fusesource.jansi.Ansi',
'org.fusesource.jansi.AnsiRenderer$Code', 'org.fusesource.jansi.AnsiRenderer$Code',
'com.lmax.disruptor.BlockingWaitStrategy', 'com.lmax.disruptor.BlockingWaitStrategy',
@ -270,20 +264,6 @@ tasks.named("thirdPartyAudit").configure {
'org.zeromq.ZMQ$Context', 'org.zeromq.ZMQ$Context',
'org.zeromq.ZMQ$Socket', 'org.zeromq.ZMQ$Socket',
'org.zeromq.ZMQ', 'org.zeromq.ZMQ',
// from org.locationtech.spatial4j.io.GeoJSONReader (spatial4j)
'org.noggit.JSONParser',
// from lucene-spatial
'com.fasterxml.jackson.databind.JsonSerializer',
'com.fasterxml.jackson.databind.JsonDeserializer',
'com.fasterxml.jackson.databind.node.ArrayNode',
'com.google.common.geometry.S2Cell',
'com.google.common.geometry.S2CellId',
'com.google.common.geometry.S2Projections',
'com.google.common.geometry.S2Point',
'com.google.common.geometry.S2$Metric',
'com.google.common.geometry.S2LatLng'
) )
if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_1_8) { if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_1_8) {

View file

@ -8,8 +8,6 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.util.SloppyMath; import org.apache.lucene.util.SloppyMath;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
@ -66,6 +64,9 @@ public class GeoUtils {
/** rounding error for quantized latitude and longitude values */ /** rounding error for quantized latitude and longitude values */
public static final double TOLERANCE = 1E-6; public static final double TOLERANCE = 1E-6;
private static final int QUAD_MAX_LEVELS_POSSIBLE = 50;
private static final int GEOHASH_MAX_LEVELS_POSSIBLE = 24;
/** Returns true if latitude is actually a valid latitude value.*/ /** Returns true if latitude is actually a valid latitude value.*/
public static boolean isValidLatitude(double latitude) { public static boolean isValidLatitude(double latitude) {
if (Double.isNaN(latitude) || Double.isInfinite(latitude) || latitude < GeoUtils.MIN_LAT || latitude > GeoUtils.MAX_LAT) { if (Double.isNaN(latitude) || Double.isInfinite(latitude) || latitude < GeoUtils.MIN_LAT || latitude > GeoUtils.MAX_LAT) {
@ -158,7 +159,7 @@ public class GeoUtils {
public static int quadTreeLevelsForPrecision(double meters) { public static int quadTreeLevelsForPrecision(double meters) {
assert meters >= 0; assert meters >= 0;
if(meters == 0) { if(meters == 0) {
return QuadPrefixTree.MAX_LEVELS_POSSIBLE; return QUAD_MAX_LEVELS_POSSIBLE;
} else { } else {
final double ratio = 1+(EARTH_POLAR_DISTANCE / EARTH_EQUATOR); // cell ratio final double ratio = 1+(EARTH_POLAR_DISTANCE / EARTH_EQUATOR); // cell ratio
final double width = Math.sqrt((meters*meters)/(ratio*ratio)); // convert to cell width final double width = Math.sqrt((meters*meters)/(ratio*ratio)); // convert to cell width
@ -188,7 +189,7 @@ public class GeoUtils {
assert meters >= 0; assert meters >= 0;
if(meters == 0) { if(meters == 0) {
return GeohashPrefixTree.getMaxLevelsPossible(); return GEOHASH_MAX_LEVELS_POSSIBLE;
} else { } else {
final double ratio = 1+(EARTH_POLAR_DISTANCE / EARTH_EQUATOR); // cell ratio final double ratio = 1+(EARTH_POLAR_DISTANCE / EARTH_EQUATOR); // cell ratio
final double width = Math.sqrt((meters*meters)/(ratio*ratio)); // convert to cell width final double width = Math.sqrt((meters*meters)/(ratio*ratio)); // convert to cell width

View file

@ -16,7 +16,7 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
/** /**
* Base class for {@link GeoShapeFieldMapper} and {@link LegacyGeoShapeFieldMapper} * Base class for {@link GeoShapeFieldMapper}
*/ */
public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeometryFieldMapper<T> { public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeometryFieldMapper<T> {

View file

@ -26,8 +26,10 @@ import org.elasticsearch.index.query.SearchExecutionContext;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
/** /**
@ -54,6 +56,10 @@ public class GeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geomet
private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(GeoShapeFieldMapper.class); private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(GeoShapeFieldMapper.class);
public static final Set<String> DEPRECATED_PARAMETERS = new HashSet<>(
Arrays.asList("strategy", "tree", "tree_levels", "precision", "distance_error_pct", "points_only")
);
public static final String CONTENT_TYPE = "geo_shape"; public static final String CONTENT_TYPE = "geo_shape";
private static Builder builder(FieldMapper in) { private static Builder builder(FieldMapper in) {

View file

@ -12,7 +12,6 @@ import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.index.query.DistanceFeatureQueryBuilder.Origin; import org.elasticsearch.index.query.DistanceFeatureQueryBuilder.Origin;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item; import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item;
@ -676,14 +675,6 @@ public final class QueryBuilders {
return new GeoShapeQueryBuilder(name, shape); return new GeoShapeQueryBuilder(name, shape);
} }
/**
* @deprecated use {@link #geoShapeQuery(String, Geometry)} instead
*/
@Deprecated
public static GeoShapeQueryBuilder geoShapeQuery(String name, ShapeBuilder<?, ?, ?> shape) throws IOException {
return new GeoShapeQueryBuilder(name, shape.buildGeometry());
}
public static GeoShapeQueryBuilder geoShapeQuery(String name, String indexedShapeId) { public static GeoShapeQueryBuilder geoShapeQuery(String name, String indexedShapeId) {
return new GeoShapeQueryBuilder(name, indexedShapeId); return new GeoShapeQueryBuilder(name, indexedShapeId);
} }
@ -708,16 +699,6 @@ public final class QueryBuilders {
return builder; return builder;
} }
/**
* @deprecated use {@link #geoIntersectionQuery(String, Geometry)} instead
*/
@Deprecated
public static GeoShapeQueryBuilder geoIntersectionQuery(String name, ShapeBuilder<?, ?, ?> shape) throws IOException {
GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.INTERSECTS);
return builder;
}
public static GeoShapeQueryBuilder geoIntersectionQuery(String name, String indexedShapeId) { public static GeoShapeQueryBuilder geoIntersectionQuery(String name, String indexedShapeId) {
GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId);
builder.relation(ShapeRelation.INTERSECTS); builder.relation(ShapeRelation.INTERSECTS);
@ -746,16 +727,6 @@ public final class QueryBuilders {
return builder; return builder;
} }
/**
* @deprecated use {@link #geoWithinQuery(String, Geometry)} instead
*/
@Deprecated
public static GeoShapeQueryBuilder geoWithinQuery(String name, ShapeBuilder<?, ?, ?> shape) throws IOException {
GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.WITHIN);
return builder;
}
public static GeoShapeQueryBuilder geoWithinQuery(String name, String indexedShapeId) { public static GeoShapeQueryBuilder geoWithinQuery(String name, String indexedShapeId) {
GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId);
builder.relation(ShapeRelation.WITHIN); builder.relation(ShapeRelation.WITHIN);
@ -784,16 +755,6 @@ public final class QueryBuilders {
return builder; return builder;
} }
/**
* @deprecated use {@link #geoDisjointQuery(String, Geometry)} instead
*/
@Deprecated
public static GeoShapeQueryBuilder geoDisjointQuery(String name, ShapeBuilder<?, ?, ?> shape) throws IOException {
GeoShapeQueryBuilder builder = geoShapeQuery(name, shape);
builder.relation(ShapeRelation.DISJOINT);
return builder;
}
public static GeoShapeQueryBuilder geoDisjointQuery(String name, String indexedShapeId) { public static GeoShapeQueryBuilder geoDisjointQuery(String name, String indexedShapeId) {
GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId); GeoShapeQueryBuilder builder = geoShapeQuery(name, indexedShapeId);
builder.relation(ShapeRelation.DISJOINT); builder.relation(ShapeRelation.DISJOINT);

View file

@ -12,8 +12,6 @@ import org.apache.lucene.search.BooleanQuery;
import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedBiConsumer;
import org.elasticsearch.common.NamedRegistry; import org.elasticsearch.common.NamedRegistry;
import org.elasticsearch.common.xcontent.ParseField; import org.elasticsearch.common.xcontent.ParseField;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.ShapesAvailability;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
@ -293,7 +291,7 @@ import static org.elasticsearch.index.query.CommonTermsQueryBuilder.COMMON_TERMS
*/ */
public class SearchModule { public class SearchModule {
public static final Setting<Integer> INDICES_MAX_CLAUSE_COUNT_SETTING = Setting.intSetting("indices.query.bool.max_clause_count", public static final Setting<Integer> INDICES_MAX_CLAUSE_COUNT_SETTING = Setting.intSetting("indices.query.bool.max_clause_count",
1024, 1, Integer.MAX_VALUE, Setting.Property.NodeScope); 1024, 1, Integer.MAX_VALUE, Setting.Property.NodeScope);
public static final Setting<Integer> INDICES_MAX_NESTED_DEPTH_SETTING = Setting.intSetting("indices.query.bool.max_nested_depth", public static final Setting<Integer> INDICES_MAX_NESTED_DEPTH_SETTING = Setting.intSetting("indices.query.bool.max_nested_depth",
20, 1, Integer.MAX_VALUE, Setting.Property.NodeScope); 20, 1, Integer.MAX_VALUE, Setting.Property.NodeScope);
@ -301,7 +299,7 @@ public class SearchModule {
private final boolean transportClient; private final boolean transportClient;
private final Map<String, Highlighter> highlighters; private final Map<String, Highlighter> highlighters;
private final ParseFieldRegistry<MovAvgModel.AbstractModelParser> movingAverageModelParserRegistry = new ParseFieldRegistry<>( private final ParseFieldRegistry<MovAvgModel.AbstractModelParser> movingAverageModelParserRegistry = new ParseFieldRegistry<>(
"moving_avg_model"); "moving_avg_model");
private final List<FetchSubPhase> fetchSubPhases = new ArrayList<>(); private final List<FetchSubPhase> fetchSubPhases = new ArrayList<>();
@ -336,7 +334,6 @@ public class SearchModule {
registerPipelineAggregations(plugins); registerPipelineAggregations(plugins);
registerFetchSubPhases(plugins); registerFetchSubPhases(plugins);
registerSearchExts(plugins); registerSearchExts(plugins);
registerShapes();
registerIntervalsSourceProviders(); registerIntervalsSourceProviders();
requestCacheKeyDifferentiator = registerRequestCacheKeyDifferentiator(plugins); requestCacheKeyDifferentiator = registerRequestCacheKeyDifferentiator(plugins);
namedWriteables.addAll(SortValue.namedWriteables()); namedWriteables.addAll(SortValue.namedWriteables());
@ -385,137 +382,137 @@ public class SearchModule {
.addResultReader(InternalSum::new) .addResultReader(InternalSum::new)
.setAggregatorRegistrar(SumAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(SumAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(MinAggregationBuilder.NAME, MinAggregationBuilder::new, MinAggregationBuilder.PARSER) registerAggregation(new AggregationSpec(MinAggregationBuilder.NAME, MinAggregationBuilder::new, MinAggregationBuilder.PARSER)
.addResultReader(InternalMin::new) .addResultReader(InternalMin::new)
.setAggregatorRegistrar(MinAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(MinAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(MaxAggregationBuilder.NAME, MaxAggregationBuilder::new, MaxAggregationBuilder.PARSER) registerAggregation(new AggregationSpec(MaxAggregationBuilder.NAME, MaxAggregationBuilder::new, MaxAggregationBuilder.PARSER)
.addResultReader(InternalMax::new) .addResultReader(InternalMax::new)
.setAggregatorRegistrar(MaxAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(MaxAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(StatsAggregationBuilder.NAME, StatsAggregationBuilder::new, StatsAggregationBuilder.PARSER) registerAggregation(new AggregationSpec(StatsAggregationBuilder.NAME, StatsAggregationBuilder::new, StatsAggregationBuilder.PARSER)
.addResultReader(InternalStats::new) .addResultReader(InternalStats::new)
.setAggregatorRegistrar(StatsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(StatsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(ExtendedStatsAggregationBuilder.NAME, ExtendedStatsAggregationBuilder::new, registerAggregation(new AggregationSpec(ExtendedStatsAggregationBuilder.NAME, ExtendedStatsAggregationBuilder::new,
ExtendedStatsAggregationBuilder.PARSER) ExtendedStatsAggregationBuilder.PARSER)
.addResultReader(InternalExtendedStats::new) .addResultReader(InternalExtendedStats::new)
.setAggregatorRegistrar(ExtendedStatsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(ExtendedStatsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(ValueCountAggregationBuilder.NAME, ValueCountAggregationBuilder::new, registerAggregation(new AggregationSpec(ValueCountAggregationBuilder.NAME, ValueCountAggregationBuilder::new,
ValueCountAggregationBuilder.PARSER) ValueCountAggregationBuilder.PARSER)
.addResultReader(InternalValueCount::new) .addResultReader(InternalValueCount::new)
.setAggregatorRegistrar(ValueCountAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(ValueCountAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(PercentilesAggregationBuilder.NAME, PercentilesAggregationBuilder::new, registerAggregation(new AggregationSpec(PercentilesAggregationBuilder.NAME, PercentilesAggregationBuilder::new,
PercentilesAggregationBuilder::parse) PercentilesAggregationBuilder::parse)
.addResultReader(InternalTDigestPercentiles.NAME, InternalTDigestPercentiles::new) .addResultReader(InternalTDigestPercentiles.NAME, InternalTDigestPercentiles::new)
.addResultReader(InternalHDRPercentiles.NAME, InternalHDRPercentiles::new) .addResultReader(InternalHDRPercentiles.NAME, InternalHDRPercentiles::new)
.setAggregatorRegistrar(PercentilesAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(PercentilesAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(PercentileRanksAggregationBuilder.NAME, PercentileRanksAggregationBuilder::new, registerAggregation(new AggregationSpec(PercentileRanksAggregationBuilder.NAME, PercentileRanksAggregationBuilder::new,
PercentileRanksAggregationBuilder::parse) PercentileRanksAggregationBuilder::parse)
.addResultReader(InternalTDigestPercentileRanks.NAME, InternalTDigestPercentileRanks::new) .addResultReader(InternalTDigestPercentileRanks.NAME, InternalTDigestPercentileRanks::new)
.addResultReader(InternalHDRPercentileRanks.NAME, InternalHDRPercentileRanks::new) .addResultReader(InternalHDRPercentileRanks.NAME, InternalHDRPercentileRanks::new)
.setAggregatorRegistrar(PercentileRanksAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(PercentileRanksAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(MedianAbsoluteDeviationAggregationBuilder.NAME, registerAggregation(new AggregationSpec(MedianAbsoluteDeviationAggregationBuilder.NAME,
MedianAbsoluteDeviationAggregationBuilder::new, MedianAbsoluteDeviationAggregationBuilder.PARSER) MedianAbsoluteDeviationAggregationBuilder::new, MedianAbsoluteDeviationAggregationBuilder.PARSER)
.addResultReader(InternalMedianAbsoluteDeviation::new) .addResultReader(InternalMedianAbsoluteDeviation::new)
.setAggregatorRegistrar(MedianAbsoluteDeviationAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(MedianAbsoluteDeviationAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(CardinalityAggregationBuilder.NAME, CardinalityAggregationBuilder::new, registerAggregation(new AggregationSpec(CardinalityAggregationBuilder.NAME, CardinalityAggregationBuilder::new,
CardinalityAggregationBuilder.PARSER).addResultReader(InternalCardinality::new) CardinalityAggregationBuilder.PARSER).addResultReader(InternalCardinality::new)
.setAggregatorRegistrar(CardinalityAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(CardinalityAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(GlobalAggregationBuilder.NAME, GlobalAggregationBuilder::new, registerAggregation(new AggregationSpec(GlobalAggregationBuilder.NAME, GlobalAggregationBuilder::new,
GlobalAggregationBuilder::parse).addResultReader(InternalGlobal::new), builder); GlobalAggregationBuilder::parse).addResultReader(InternalGlobal::new), builder);
registerAggregation(new AggregationSpec(MissingAggregationBuilder.NAME, MissingAggregationBuilder::new, registerAggregation(new AggregationSpec(MissingAggregationBuilder.NAME, MissingAggregationBuilder::new,
MissingAggregationBuilder.PARSER) MissingAggregationBuilder.PARSER)
.addResultReader(InternalMissing::new) .addResultReader(InternalMissing::new)
.setAggregatorRegistrar(MissingAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(MissingAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(FilterAggregationBuilder.NAME, FilterAggregationBuilder::new, registerAggregation(new AggregationSpec(FilterAggregationBuilder.NAME, FilterAggregationBuilder::new,
FilterAggregationBuilder::parse).addResultReader(InternalFilter::new), builder); FilterAggregationBuilder::parse).addResultReader(InternalFilter::new), builder);
registerAggregation(new AggregationSpec(FiltersAggregationBuilder.NAME, FiltersAggregationBuilder::new, registerAggregation(new AggregationSpec(FiltersAggregationBuilder.NAME, FiltersAggregationBuilder::new,
FiltersAggregationBuilder::parse).addResultReader(InternalFilters::new), builder); FiltersAggregationBuilder::parse).addResultReader(InternalFilters::new), builder);
registerAggregation(new AggregationSpec(AdjacencyMatrixAggregationBuilder.NAME, AdjacencyMatrixAggregationBuilder::new, registerAggregation(new AggregationSpec(AdjacencyMatrixAggregationBuilder.NAME, AdjacencyMatrixAggregationBuilder::new,
AdjacencyMatrixAggregationBuilder::parse).addResultReader(InternalAdjacencyMatrix::new), builder); AdjacencyMatrixAggregationBuilder::parse).addResultReader(InternalAdjacencyMatrix::new), builder);
registerAggregation(new AggregationSpec(SamplerAggregationBuilder.NAME, SamplerAggregationBuilder::new, registerAggregation(new AggregationSpec(SamplerAggregationBuilder.NAME, SamplerAggregationBuilder::new,
SamplerAggregationBuilder::parse) SamplerAggregationBuilder::parse)
.addResultReader(InternalSampler.NAME, InternalSampler::new) .addResultReader(InternalSampler.NAME, InternalSampler::new)
.addResultReader(UnmappedSampler.NAME, UnmappedSampler::new), .addResultReader(UnmappedSampler.NAME, UnmappedSampler::new),
builder); builder);
registerAggregation(new AggregationSpec(DiversifiedAggregationBuilder.NAME, DiversifiedAggregationBuilder::new, registerAggregation(new AggregationSpec(DiversifiedAggregationBuilder.NAME, DiversifiedAggregationBuilder::new,
DiversifiedAggregationBuilder.PARSER).setAggregatorRegistrar(DiversifiedAggregationBuilder::registerAggregators) DiversifiedAggregationBuilder.PARSER).setAggregatorRegistrar(DiversifiedAggregationBuilder::registerAggregators)
/* Reuses result readers from SamplerAggregator*/, builder); /* Reuses result readers from SamplerAggregator*/, builder);
registerAggregation(new AggregationSpec(TermsAggregationBuilder.NAME, TermsAggregationBuilder::new, registerAggregation(new AggregationSpec(TermsAggregationBuilder.NAME, TermsAggregationBuilder::new,
TermsAggregationBuilder.PARSER) TermsAggregationBuilder.PARSER)
.addResultReader(StringTerms.NAME, StringTerms::new) .addResultReader(StringTerms.NAME, StringTerms::new)
.addResultReader(UnmappedTerms.NAME, UnmappedTerms::new) .addResultReader(UnmappedTerms.NAME, UnmappedTerms::new)
.addResultReader(LongTerms.NAME, LongTerms::new) .addResultReader(LongTerms.NAME, LongTerms::new)
.addResultReader(DoubleTerms.NAME, DoubleTerms::new) .addResultReader(DoubleTerms.NAME, DoubleTerms::new)
.setAggregatorRegistrar(TermsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(TermsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(RareTermsAggregationBuilder.NAME, RareTermsAggregationBuilder::new, registerAggregation(new AggregationSpec(RareTermsAggregationBuilder.NAME, RareTermsAggregationBuilder::new,
RareTermsAggregationBuilder.PARSER) RareTermsAggregationBuilder.PARSER)
.addResultReader(StringRareTerms.NAME, StringRareTerms::new) .addResultReader(StringRareTerms.NAME, StringRareTerms::new)
.addResultReader(UnmappedRareTerms.NAME, UnmappedRareTerms::new) .addResultReader(UnmappedRareTerms.NAME, UnmappedRareTerms::new)
.addResultReader(LongRareTerms.NAME, LongRareTerms::new) .addResultReader(LongRareTerms.NAME, LongRareTerms::new)
.setAggregatorRegistrar(RareTermsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(RareTermsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(SignificantTermsAggregationBuilder.NAME, SignificantTermsAggregationBuilder::new, registerAggregation(new AggregationSpec(SignificantTermsAggregationBuilder.NAME, SignificantTermsAggregationBuilder::new,
SignificantTermsAggregationBuilder::parse) SignificantTermsAggregationBuilder::parse)
.addResultReader(SignificantStringTerms.NAME, SignificantStringTerms::new) .addResultReader(SignificantStringTerms.NAME, SignificantStringTerms::new)
.addResultReader(SignificantLongTerms.NAME, SignificantLongTerms::new) .addResultReader(SignificantLongTerms.NAME, SignificantLongTerms::new)
.addResultReader(UnmappedSignificantTerms.NAME, UnmappedSignificantTerms::new) .addResultReader(UnmappedSignificantTerms.NAME, UnmappedSignificantTerms::new)
.setAggregatorRegistrar(SignificantTermsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(SignificantTermsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(SignificantTextAggregationBuilder.NAME, SignificantTextAggregationBuilder::new, registerAggregation(new AggregationSpec(SignificantTextAggregationBuilder.NAME, SignificantTextAggregationBuilder::new,
SignificantTextAggregationBuilder::parse), builder); SignificantTextAggregationBuilder::parse), builder);
registerAggregation(new AggregationSpec(RangeAggregationBuilder.NAME, RangeAggregationBuilder::new, registerAggregation(new AggregationSpec(RangeAggregationBuilder.NAME, RangeAggregationBuilder::new,
RangeAggregationBuilder.PARSER) RangeAggregationBuilder.PARSER)
.addResultReader(InternalRange::new) .addResultReader(InternalRange::new)
.setAggregatorRegistrar(RangeAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(RangeAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(DateRangeAggregationBuilder.NAME, DateRangeAggregationBuilder::new, registerAggregation(new AggregationSpec(DateRangeAggregationBuilder.NAME, DateRangeAggregationBuilder::new,
DateRangeAggregationBuilder.PARSER) DateRangeAggregationBuilder.PARSER)
.addResultReader(InternalDateRange::new) .addResultReader(InternalDateRange::new)
.setAggregatorRegistrar(DateRangeAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(DateRangeAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(IpRangeAggregationBuilder.NAME, IpRangeAggregationBuilder::new, registerAggregation(new AggregationSpec(IpRangeAggregationBuilder.NAME, IpRangeAggregationBuilder::new,
IpRangeAggregationBuilder.PARSER) IpRangeAggregationBuilder.PARSER)
.addResultReader(InternalBinaryRange::new) .addResultReader(InternalBinaryRange::new)
.setAggregatorRegistrar(IpRangeAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(IpRangeAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(HistogramAggregationBuilder.NAME, HistogramAggregationBuilder::new, registerAggregation(new AggregationSpec(HistogramAggregationBuilder.NAME, HistogramAggregationBuilder::new,
HistogramAggregationBuilder.PARSER) HistogramAggregationBuilder.PARSER)
.addResultReader(InternalHistogram::new) .addResultReader(InternalHistogram::new)
.setAggregatorRegistrar(HistogramAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(HistogramAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(DateHistogramAggregationBuilder.NAME, DateHistogramAggregationBuilder::new, registerAggregation(new AggregationSpec(DateHistogramAggregationBuilder.NAME, DateHistogramAggregationBuilder::new,
DateHistogramAggregationBuilder.PARSER) DateHistogramAggregationBuilder.PARSER)
.addResultReader(InternalDateHistogram::new) .addResultReader(InternalDateHistogram::new)
.setAggregatorRegistrar(DateHistogramAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(DateHistogramAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(AutoDateHistogramAggregationBuilder.NAME, AutoDateHistogramAggregationBuilder::new, registerAggregation(new AggregationSpec(AutoDateHistogramAggregationBuilder.NAME, AutoDateHistogramAggregationBuilder::new,
AutoDateHistogramAggregationBuilder.PARSER) AutoDateHistogramAggregationBuilder.PARSER)
.addResultReader(InternalAutoDateHistogram::new) .addResultReader(InternalAutoDateHistogram::new)
.setAggregatorRegistrar(AutoDateHistogramAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(AutoDateHistogramAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(VariableWidthHistogramAggregationBuilder.NAME, registerAggregation(new AggregationSpec(VariableWidthHistogramAggregationBuilder.NAME,
VariableWidthHistogramAggregationBuilder::new, VariableWidthHistogramAggregationBuilder::new,
VariableWidthHistogramAggregationBuilder.PARSER) VariableWidthHistogramAggregationBuilder.PARSER)
.addResultReader(InternalVariableWidthHistogram::new) .addResultReader(InternalVariableWidthHistogram::new)
.setAggregatorRegistrar(VariableWidthHistogramAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(VariableWidthHistogramAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(GeoDistanceAggregationBuilder.NAME, GeoDistanceAggregationBuilder::new, registerAggregation(new AggregationSpec(GeoDistanceAggregationBuilder.NAME, GeoDistanceAggregationBuilder::new,
GeoDistanceAggregationBuilder::parse) GeoDistanceAggregationBuilder::parse)
.addResultReader(InternalGeoDistance::new) .addResultReader(InternalGeoDistance::new)
.setAggregatorRegistrar(GeoDistanceAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(GeoDistanceAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(GeoHashGridAggregationBuilder.NAME, GeoHashGridAggregationBuilder::new, registerAggregation(new AggregationSpec(GeoHashGridAggregationBuilder.NAME, GeoHashGridAggregationBuilder::new,
GeoHashGridAggregationBuilder.PARSER) GeoHashGridAggregationBuilder.PARSER)
.addResultReader(InternalGeoHashGrid::new) .addResultReader(InternalGeoHashGrid::new)
.setAggregatorRegistrar(GeoHashGridAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(GeoHashGridAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(GeoTileGridAggregationBuilder.NAME, GeoTileGridAggregationBuilder::new, registerAggregation(new AggregationSpec(GeoTileGridAggregationBuilder.NAME, GeoTileGridAggregationBuilder::new,
GeoTileGridAggregationBuilder.PARSER) GeoTileGridAggregationBuilder.PARSER)
.addResultReader(InternalGeoTileGrid::new) .addResultReader(InternalGeoTileGrid::new)
.setAggregatorRegistrar(GeoTileGridAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(GeoTileGridAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(NestedAggregationBuilder.NAME, NestedAggregationBuilder::new, registerAggregation(new AggregationSpec(NestedAggregationBuilder.NAME, NestedAggregationBuilder::new,
NestedAggregationBuilder::parse).addResultReader(InternalNested::new), builder); NestedAggregationBuilder::parse).addResultReader(InternalNested::new), builder);
registerAggregation(new AggregationSpec(ReverseNestedAggregationBuilder.NAME, ReverseNestedAggregationBuilder::new, registerAggregation(new AggregationSpec(ReverseNestedAggregationBuilder.NAME, ReverseNestedAggregationBuilder::new,
ReverseNestedAggregationBuilder::parse).addResultReader(InternalReverseNested::new), builder); ReverseNestedAggregationBuilder::parse).addResultReader(InternalReverseNested::new), builder);
registerAggregation(new AggregationSpec(TopHitsAggregationBuilder.NAME, TopHitsAggregationBuilder::new, registerAggregation(new AggregationSpec(TopHitsAggregationBuilder.NAME, TopHitsAggregationBuilder::new,
TopHitsAggregationBuilder::parse).addResultReader(InternalTopHits::new), builder); TopHitsAggregationBuilder::parse).addResultReader(InternalTopHits::new), builder);
registerAggregation(new AggregationSpec(GeoBoundsAggregationBuilder.NAME, GeoBoundsAggregationBuilder::new, registerAggregation(new AggregationSpec(GeoBoundsAggregationBuilder.NAME, GeoBoundsAggregationBuilder::new,
GeoBoundsAggregationBuilder.PARSER) GeoBoundsAggregationBuilder.PARSER)
.addResultReader(InternalGeoBounds::new) .addResultReader(InternalGeoBounds::new)
.setAggregatorRegistrar(GeoBoundsAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(GeoBoundsAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(GeoCentroidAggregationBuilder.NAME, GeoCentroidAggregationBuilder::new, registerAggregation(new AggregationSpec(GeoCentroidAggregationBuilder.NAME, GeoCentroidAggregationBuilder::new,
GeoCentroidAggregationBuilder.PARSER) GeoCentroidAggregationBuilder.PARSER)
.addResultReader(InternalGeoCentroid::new) .addResultReader(InternalGeoCentroid::new)
.setAggregatorRegistrar(GeoCentroidAggregationBuilder::registerAggregators), builder); .setAggregatorRegistrar(GeoCentroidAggregationBuilder::registerAggregators), builder);
registerAggregation(new AggregationSpec(ScriptedMetricAggregationBuilder.NAME, ScriptedMetricAggregationBuilder::new, registerAggregation(new AggregationSpec(ScriptedMetricAggregationBuilder.NAME, ScriptedMetricAggregationBuilder::new,
ScriptedMetricAggregationBuilder.PARSER).addResultReader(InternalScriptedMetric::new), builder); ScriptedMetricAggregationBuilder.PARSER).addResultReader(InternalScriptedMetric::new), builder);
registerAggregation( registerAggregation(
new AggregationSpec(CompositeAggregationBuilder.NAME, CompositeAggregationBuilder::new, CompositeAggregationBuilder.PARSER) new AggregationSpec(CompositeAggregationBuilder.NAME, CompositeAggregationBuilder::new, CompositeAggregationBuilder.PARSER)
.addResultReader(InternalComposite::new) .addResultReader(InternalComposite::new)
@ -539,7 +536,7 @@ public class SearchModule {
})); }));
} }
namedWriteables.add( namedWriteables.add(
new NamedWriteableRegistry.Entry(AggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader())); new NamedWriteableRegistry.Entry(AggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader()));
for (Map.Entry<String, Writeable.Reader<? extends InternalAggregation>> t : spec.getResultReaders().entrySet()) { for (Map.Entry<String, Writeable.Reader<? extends InternalAggregation>> t : spec.getResultReaders().entrySet()) {
String writeableName = t.getKey(); String writeableName = t.getKey();
Writeable.Reader<? extends InternalAggregation> internalReader = t.getValue(); Writeable.Reader<? extends InternalAggregation> internalReader = t.getValue();
@ -557,92 +554,92 @@ public class SearchModule {
private void registerPipelineAggregations(List<SearchPlugin> plugins) { private void registerPipelineAggregations(List<SearchPlugin> plugins) {
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
DerivativePipelineAggregationBuilder.NAME, DerivativePipelineAggregationBuilder.NAME,
DerivativePipelineAggregationBuilder::new, DerivativePipelineAggregationBuilder::new,
DerivativePipelineAggregator::new, DerivativePipelineAggregator::new,
DerivativePipelineAggregationBuilder::parse) DerivativePipelineAggregationBuilder::parse)
.addResultReader(InternalDerivative::new)); .addResultReader(InternalDerivative::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
MaxBucketPipelineAggregationBuilder.NAME, MaxBucketPipelineAggregationBuilder.NAME,
MaxBucketPipelineAggregationBuilder::new, MaxBucketPipelineAggregationBuilder::new,
MaxBucketPipelineAggregator::new, MaxBucketPipelineAggregator::new,
MaxBucketPipelineAggregationBuilder.PARSER) MaxBucketPipelineAggregationBuilder.PARSER)
// This bucket is used by many pipeline aggreations. // This bucket is used by many pipeline aggreations.
.addResultReader(InternalBucketMetricValue.NAME, InternalBucketMetricValue::new)); .addResultReader(InternalBucketMetricValue.NAME, InternalBucketMetricValue::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
MinBucketPipelineAggregationBuilder.NAME, MinBucketPipelineAggregationBuilder.NAME,
MinBucketPipelineAggregationBuilder::new, MinBucketPipelineAggregationBuilder::new,
MinBucketPipelineAggregator::new, MinBucketPipelineAggregator::new,
MinBucketPipelineAggregationBuilder.PARSER) MinBucketPipelineAggregationBuilder.PARSER)
/* Uses InternalBucketMetricValue */); /* Uses InternalBucketMetricValue */);
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
AvgBucketPipelineAggregationBuilder.NAME, AvgBucketPipelineAggregationBuilder.NAME,
AvgBucketPipelineAggregationBuilder::new, AvgBucketPipelineAggregationBuilder::new,
AvgBucketPipelineAggregator::new, AvgBucketPipelineAggregator::new,
AvgBucketPipelineAggregationBuilder.PARSER) AvgBucketPipelineAggregationBuilder.PARSER)
// This bucket is used by many pipeline aggreations. // This bucket is used by many pipeline aggreations.
.addResultReader(InternalSimpleValue.NAME, InternalSimpleValue::new)); .addResultReader(InternalSimpleValue.NAME, InternalSimpleValue::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
SumBucketPipelineAggregationBuilder.NAME, SumBucketPipelineAggregationBuilder.NAME,
SumBucketPipelineAggregationBuilder::new, SumBucketPipelineAggregationBuilder::new,
SumBucketPipelineAggregator::new, SumBucketPipelineAggregator::new,
SumBucketPipelineAggregationBuilder.PARSER) SumBucketPipelineAggregationBuilder.PARSER)
/* Uses InternalSimpleValue */); /* Uses InternalSimpleValue */);
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
StatsBucketPipelineAggregationBuilder.NAME, StatsBucketPipelineAggregationBuilder.NAME,
StatsBucketPipelineAggregationBuilder::new, StatsBucketPipelineAggregationBuilder::new,
StatsBucketPipelineAggregator::new, StatsBucketPipelineAggregator::new,
StatsBucketPipelineAggregationBuilder.PARSER) StatsBucketPipelineAggregationBuilder.PARSER)
.addResultReader(InternalStatsBucket::new)); .addResultReader(InternalStatsBucket::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
ExtendedStatsBucketPipelineAggregationBuilder.NAME, ExtendedStatsBucketPipelineAggregationBuilder.NAME,
ExtendedStatsBucketPipelineAggregationBuilder::new, ExtendedStatsBucketPipelineAggregationBuilder::new,
ExtendedStatsBucketPipelineAggregator::new, ExtendedStatsBucketPipelineAggregator::new,
new ExtendedStatsBucketParser()) new ExtendedStatsBucketParser())
.addResultReader(InternalExtendedStatsBucket::new)); .addResultReader(InternalExtendedStatsBucket::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
PercentilesBucketPipelineAggregationBuilder.NAME, PercentilesBucketPipelineAggregationBuilder.NAME,
PercentilesBucketPipelineAggregationBuilder::new, PercentilesBucketPipelineAggregationBuilder::new,
PercentilesBucketPipelineAggregator::new, PercentilesBucketPipelineAggregator::new,
PercentilesBucketPipelineAggregationBuilder.PARSER) PercentilesBucketPipelineAggregationBuilder.PARSER)
.addResultReader(InternalPercentilesBucket::new)); .addResultReader(InternalPercentilesBucket::new));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
MovAvgPipelineAggregationBuilder.NAME, MovAvgPipelineAggregationBuilder.NAME,
MovAvgPipelineAggregationBuilder::new, MovAvgPipelineAggregationBuilder::new,
MovAvgPipelineAggregator::new, MovAvgPipelineAggregator::new,
(XContentParser parser, String name) -> (XContentParser parser, String name) ->
MovAvgPipelineAggregationBuilder.parse(movingAverageModelParserRegistry, name, parser) MovAvgPipelineAggregationBuilder.parse(movingAverageModelParserRegistry, name, parser)
)/* Uses InternalHistogram for buckets */); )/* Uses InternalHistogram for buckets */);
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
CumulativeSumPipelineAggregationBuilder.NAME, CumulativeSumPipelineAggregationBuilder.NAME,
CumulativeSumPipelineAggregationBuilder::new, CumulativeSumPipelineAggregationBuilder::new,
CumulativeSumPipelineAggregator::new, CumulativeSumPipelineAggregator::new,
CumulativeSumPipelineAggregationBuilder.PARSER)); CumulativeSumPipelineAggregationBuilder.PARSER));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
BucketScriptPipelineAggregationBuilder.NAME, BucketScriptPipelineAggregationBuilder.NAME,
BucketScriptPipelineAggregationBuilder::new, BucketScriptPipelineAggregationBuilder::new,
BucketScriptPipelineAggregator::new, BucketScriptPipelineAggregator::new,
BucketScriptPipelineAggregationBuilder.PARSER)); BucketScriptPipelineAggregationBuilder.PARSER));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
BucketSelectorPipelineAggregationBuilder.NAME, BucketSelectorPipelineAggregationBuilder.NAME,
BucketSelectorPipelineAggregationBuilder::new, BucketSelectorPipelineAggregationBuilder::new,
BucketSelectorPipelineAggregator::new, BucketSelectorPipelineAggregator::new,
BucketSelectorPipelineAggregationBuilder::parse)); BucketSelectorPipelineAggregationBuilder::parse));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
BucketSortPipelineAggregationBuilder.NAME, BucketSortPipelineAggregationBuilder.NAME,
BucketSortPipelineAggregationBuilder::new, BucketSortPipelineAggregationBuilder::new,
BucketSortPipelineAggregator::new, BucketSortPipelineAggregator::new,
BucketSortPipelineAggregationBuilder::parse)); BucketSortPipelineAggregationBuilder::parse));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
SerialDiffPipelineAggregationBuilder.NAME, SerialDiffPipelineAggregationBuilder.NAME,
SerialDiffPipelineAggregationBuilder::new, SerialDiffPipelineAggregationBuilder::new,
SerialDiffPipelineAggregator::new, SerialDiffPipelineAggregator::new,
SerialDiffPipelineAggregationBuilder::parse)); SerialDiffPipelineAggregationBuilder::parse));
registerPipelineAggregation(new PipelineAggregationSpec( registerPipelineAggregation(new PipelineAggregationSpec(
MovFnPipelineAggregationBuilder.NAME, MovFnPipelineAggregationBuilder.NAME,
MovFnPipelineAggregationBuilder::new, MovFnPipelineAggregationBuilder::new,
MovFnPipelineAggregator::new, MovFnPipelineAggregator::new,
MovFnPipelineAggregationBuilder.PARSER)); MovFnPipelineAggregationBuilder.PARSER));
registerFromPlugin(plugins, SearchPlugin::getPipelineAggregations, this::registerPipelineAggregation); registerFromPlugin(plugins, SearchPlugin::getPipelineAggregations, this::registerPipelineAggregation);
} }
@ -650,23 +647,17 @@ public class SearchModule {
private void registerPipelineAggregation(PipelineAggregationSpec spec) { private void registerPipelineAggregation(PipelineAggregationSpec spec) {
if (false == transportClient) { if (false == transportClient) {
namedXContents.add(new NamedXContentRegistry.Entry(BaseAggregationBuilder.class, spec.getName(), namedXContents.add(new NamedXContentRegistry.Entry(BaseAggregationBuilder.class, spec.getName(),
(p, c) -> spec.getParser().parse(p, (String) c))); (p, c) -> spec.getParser().parse(p, (String) c)));
} }
namedWriteables.add( namedWriteables.add(
new NamedWriteableRegistry.Entry(PipelineAggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader())); new NamedWriteableRegistry.Entry(PipelineAggregationBuilder.class, spec.getName().getPreferredName(), spec.getReader()));
if (spec.getAggregatorReader() != null) { if (spec.getAggregatorReader() != null) {
namedWriteables.add(new NamedWriteableRegistry.Entry( namedWriteables.add(new NamedWriteableRegistry.Entry(
PipelineAggregator.class, spec.getName().getPreferredName(), spec.getAggregatorReader())); PipelineAggregator.class, spec.getName().getPreferredName(), spec.getAggregatorReader()));
} }
for (Map.Entry<String, Writeable.Reader<? extends InternalAggregation>> resultReader : spec.getResultReaders().entrySet()) { for (Map.Entry<String, Writeable.Reader<? extends InternalAggregation>> resultReader : spec.getResultReaders().entrySet()) {
namedWriteables namedWriteables
.add(new NamedWriteableRegistry.Entry(InternalAggregation.class, resultReader.getKey(), resultReader.getValue())); .add(new NamedWriteableRegistry.Entry(InternalAggregation.class, resultReader.getKey(), resultReader.getValue()));
}
}
private void registerShapes() {
if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) {
namedWriteables.addAll(GeoShapeType.getShapeWriteables());
} }
} }
@ -720,9 +711,9 @@ public class SearchModule {
private void registerSuggester(SuggesterSpec<?> suggester) { private void registerSuggester(SuggesterSpec<?> suggester) {
namedWriteables.add(new NamedWriteableRegistry.Entry( namedWriteables.add(new NamedWriteableRegistry.Entry(
SuggestionBuilder.class, suggester.getName().getPreferredName(), suggester.getReader())); SuggestionBuilder.class, suggester.getName().getPreferredName(), suggester.getReader()));
namedXContents.add(new NamedXContentRegistry.Entry(SuggestionBuilder.class, suggester.getName(), namedXContents.add(new NamedXContentRegistry.Entry(SuggestionBuilder.class, suggester.getName(),
suggester.getParser())); suggester.getParser()));
namedWriteables.add(new NamedWriteableRegistry.Entry( namedWriteables.add(new NamedWriteableRegistry.Entry(
Suggest.Suggestion.class, suggester.getName().getPreferredName(), suggester.getSuggestionReader() Suggest.Suggestion.class, suggester.getName().getPreferredName(), suggester.getSuggestionReader()
@ -744,18 +735,18 @@ public class SearchModule {
namedWriteables.add(new NamedWriteableRegistry.Entry( namedWriteables.add(new NamedWriteableRegistry.Entry(
ScriptScoreFunctionBuilder.class, ScriptScoreFunctionBuilder.NAME, ScriptScoreFunctionBuilder::new)); ScriptScoreFunctionBuilder.class, ScriptScoreFunctionBuilder.NAME, ScriptScoreFunctionBuilder::new));
registerScoreFunction(new ScoreFunctionSpec<>(ScriptScoreFunctionBuilder.NAME, ScriptScoreFunctionBuilder::new, registerScoreFunction(new ScoreFunctionSpec<>(ScriptScoreFunctionBuilder.NAME, ScriptScoreFunctionBuilder::new,
ScriptScoreFunctionBuilder::fromXContent)); ScriptScoreFunctionBuilder::fromXContent));
registerScoreFunction( registerScoreFunction(
new ScoreFunctionSpec<>(GaussDecayFunctionBuilder.NAME, GaussDecayFunctionBuilder::new, GaussDecayFunctionBuilder.PARSER)); new ScoreFunctionSpec<>(GaussDecayFunctionBuilder.NAME, GaussDecayFunctionBuilder::new, GaussDecayFunctionBuilder.PARSER));
registerScoreFunction(new ScoreFunctionSpec<>(LinearDecayFunctionBuilder.NAME, LinearDecayFunctionBuilder::new, registerScoreFunction(new ScoreFunctionSpec<>(LinearDecayFunctionBuilder.NAME, LinearDecayFunctionBuilder::new,
LinearDecayFunctionBuilder.PARSER)); LinearDecayFunctionBuilder.PARSER));
registerScoreFunction(new ScoreFunctionSpec<>(ExponentialDecayFunctionBuilder.NAME, ExponentialDecayFunctionBuilder::new, registerScoreFunction(new ScoreFunctionSpec<>(ExponentialDecayFunctionBuilder.NAME, ExponentialDecayFunctionBuilder::new,
ExponentialDecayFunctionBuilder.PARSER)); ExponentialDecayFunctionBuilder.PARSER));
registerScoreFunction(new ScoreFunctionSpec<>(RandomScoreFunctionBuilder.NAME, RandomScoreFunctionBuilder::new, registerScoreFunction(new ScoreFunctionSpec<>(RandomScoreFunctionBuilder.NAME, RandomScoreFunctionBuilder::new,
RandomScoreFunctionBuilder::fromXContent)); RandomScoreFunctionBuilder::fromXContent));
registerScoreFunction(new ScoreFunctionSpec<>(FieldValueFactorFunctionBuilder.NAME, FieldValueFactorFunctionBuilder::new, registerScoreFunction(new ScoreFunctionSpec<>(FieldValueFactorFunctionBuilder.NAME, FieldValueFactorFunctionBuilder::new,
FieldValueFactorFunctionBuilder::fromXContent)); FieldValueFactorFunctionBuilder::fromXContent));
//weight doesn't have its own parser, so every function supports it out of the box. //weight doesn't have its own parser, so every function supports it out of the box.
//Can be a single function too when not associated to any other function, which is why it needs to be registered manually here. //Can be a single function too when not associated to any other function, which is why it needs to be registered manually here.
@ -766,11 +757,11 @@ public class SearchModule {
private void registerScoreFunction(ScoreFunctionSpec<?> scoreFunction) { private void registerScoreFunction(ScoreFunctionSpec<?> scoreFunction) {
namedWriteables.add(new NamedWriteableRegistry.Entry( namedWriteables.add(new NamedWriteableRegistry.Entry(
ScoreFunctionBuilder.class, scoreFunction.getName().getPreferredName(), scoreFunction.getReader())); ScoreFunctionBuilder.class, scoreFunction.getName().getPreferredName(), scoreFunction.getReader()));
// TODO remove funky contexts // TODO remove funky contexts
namedXContents.add(new NamedXContentRegistry.Entry( namedXContents.add(new NamedXContentRegistry.Entry(
ScoreFunctionBuilder.class, scoreFunction.getName(), ScoreFunctionBuilder.class, scoreFunction.getName(),
(XContentParser p, Object c) -> scoreFunction.getParser().fromXContent(p))); (XContentParser p, Object c) -> scoreFunction.getParser().fromXContent(p)));
} }
private void registerValueFormats() { private void registerValueFormats() {
@ -824,7 +815,7 @@ public class SearchModule {
private void registerMovingAverageModel(SearchExtensionSpec<MovAvgModel, MovAvgModel.AbstractModelParser> movAvgModel) { private void registerMovingAverageModel(SearchExtensionSpec<MovAvgModel, MovAvgModel.AbstractModelParser> movAvgModel) {
movingAverageModelParserRegistry.register(movAvgModel.getParser(), movAvgModel.getName()); movingAverageModelParserRegistry.register(movAvgModel.getParser(), movAvgModel.getName());
namedWriteables.add( namedWriteables.add(
new NamedWriteableRegistry.Entry(MovAvgModel.class, movAvgModel.getName().getPreferredName(), movAvgModel.getReader())); new NamedWriteableRegistry.Entry(MovAvgModel.class, movAvgModel.getName().getPreferredName(), movAvgModel.getReader()));
} }
private void registerFetchSubPhases(List<SearchPlugin> plugins) { private void registerFetchSubPhases(List<SearchPlugin> plugins) {
@ -864,7 +855,7 @@ public class SearchModule {
registerQuery(new QuerySpec<>(MatchQueryBuilder.NAME, MatchQueryBuilder::new, MatchQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MatchQueryBuilder.NAME, MatchQueryBuilder::new, MatchQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(MatchPhraseQueryBuilder.NAME, MatchPhraseQueryBuilder::new, MatchPhraseQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MatchPhraseQueryBuilder.NAME, MatchPhraseQueryBuilder::new, MatchPhraseQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(MatchPhrasePrefixQueryBuilder.NAME, MatchPhrasePrefixQueryBuilder::new, registerQuery(new QuerySpec<>(MatchPhrasePrefixQueryBuilder.NAME, MatchPhrasePrefixQueryBuilder::new,
MatchPhrasePrefixQueryBuilder::fromXContent)); MatchPhrasePrefixQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(MultiMatchQueryBuilder.NAME, MultiMatchQueryBuilder::new, MultiMatchQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(MultiMatchQueryBuilder.NAME, MultiMatchQueryBuilder::new, MultiMatchQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(CombinedFieldsQueryBuilder.NAME, CombinedFieldsQueryBuilder::new, registerQuery(new QuerySpec<>(CombinedFieldsQueryBuilder.NAME, CombinedFieldsQueryBuilder::new,
CombinedFieldsQueryBuilder::fromXContent)); CombinedFieldsQueryBuilder::fromXContent));
@ -885,35 +876,35 @@ public class SearchModule {
registerQuery(new QuerySpec<>(PrefixQueryBuilder.NAME, PrefixQueryBuilder::new, PrefixQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(PrefixQueryBuilder.NAME, PrefixQueryBuilder::new, PrefixQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(WildcardQueryBuilder.NAME, WildcardQueryBuilder::new, WildcardQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(WildcardQueryBuilder.NAME, WildcardQueryBuilder::new, WildcardQueryBuilder::fromXContent));
registerQuery( registerQuery(
new QuerySpec<>(ConstantScoreQueryBuilder.NAME, ConstantScoreQueryBuilder::new, ConstantScoreQueryBuilder::fromXContent)); new QuerySpec<>(ConstantScoreQueryBuilder.NAME, ConstantScoreQueryBuilder::new, ConstantScoreQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanTermQueryBuilder.NAME, SpanTermQueryBuilder::new, SpanTermQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanTermQueryBuilder.NAME, SpanTermQueryBuilder::new, SpanTermQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanNotQueryBuilder.NAME, SpanNotQueryBuilder::new, SpanNotQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanNotQueryBuilder.NAME, SpanNotQueryBuilder::new, SpanNotQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanWithinQueryBuilder.NAME, SpanWithinQueryBuilder::new, SpanWithinQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanWithinQueryBuilder.NAME, SpanWithinQueryBuilder::new, SpanWithinQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanContainingQueryBuilder.NAME, SpanContainingQueryBuilder::new, registerQuery(new QuerySpec<>(SpanContainingQueryBuilder.NAME, SpanContainingQueryBuilder::new,
SpanContainingQueryBuilder::fromXContent)); SpanContainingQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(FieldMaskingSpanQueryBuilder.NAME, FieldMaskingSpanQueryBuilder::new, registerQuery(new QuerySpec<>(FieldMaskingSpanQueryBuilder.NAME, FieldMaskingSpanQueryBuilder::new,
FieldMaskingSpanQueryBuilder::fromXContent)); FieldMaskingSpanQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanFirstQueryBuilder.NAME, SpanFirstQueryBuilder::new, SpanFirstQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanFirstQueryBuilder.NAME, SpanFirstQueryBuilder::new, SpanFirstQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanNearQueryBuilder.NAME, SpanNearQueryBuilder::new, SpanNearQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanNearQueryBuilder.NAME, SpanNearQueryBuilder::new, SpanNearQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanGapQueryBuilder.NAME, SpanGapQueryBuilder::new, SpanGapQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanGapQueryBuilder.NAME, SpanGapQueryBuilder::new, SpanGapQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(SpanOrQueryBuilder.NAME, SpanOrQueryBuilder::new, SpanOrQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(SpanOrQueryBuilder.NAME, SpanOrQueryBuilder::new, SpanOrQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(MoreLikeThisQueryBuilder.NAME, MoreLikeThisQueryBuilder::new, registerQuery(new QuerySpec<>(MoreLikeThisQueryBuilder.NAME, MoreLikeThisQueryBuilder::new,
MoreLikeThisQueryBuilder::fromXContent)); MoreLikeThisQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(WrapperQueryBuilder.NAME, WrapperQueryBuilder::new, WrapperQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(WrapperQueryBuilder.NAME, WrapperQueryBuilder::new, WrapperQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(new ParseField(CommonTermsQueryBuilder.NAME).withAllDeprecated(COMMON_TERMS_QUERY_DEPRECATION_MSG), registerQuery(new QuerySpec<>(new ParseField(CommonTermsQueryBuilder.NAME).withAllDeprecated(COMMON_TERMS_QUERY_DEPRECATION_MSG),
CommonTermsQueryBuilder::new, CommonTermsQueryBuilder::fromXContent)); CommonTermsQueryBuilder::new, CommonTermsQueryBuilder::fromXContent));
registerQuery( registerQuery(
new QuerySpec<>(SpanMultiTermQueryBuilder.NAME, SpanMultiTermQueryBuilder::new, SpanMultiTermQueryBuilder::fromXContent)); new QuerySpec<>(SpanMultiTermQueryBuilder.NAME, SpanMultiTermQueryBuilder::new, SpanMultiTermQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(FunctionScoreQueryBuilder.NAME, FunctionScoreQueryBuilder::new, registerQuery(new QuerySpec<>(FunctionScoreQueryBuilder.NAME, FunctionScoreQueryBuilder::new,
FunctionScoreQueryBuilder::fromXContent)); FunctionScoreQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(ScriptScoreQueryBuilder.NAME, ScriptScoreQueryBuilder::new, ScriptScoreQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(ScriptScoreQueryBuilder.NAME, ScriptScoreQueryBuilder::new, ScriptScoreQueryBuilder::fromXContent));
registerQuery( registerQuery(
new QuerySpec<>(SimpleQueryStringBuilder.NAME, SimpleQueryStringBuilder::new, SimpleQueryStringBuilder::fromXContent)); new QuerySpec<>(SimpleQueryStringBuilder.NAME, SimpleQueryStringBuilder::new, SimpleQueryStringBuilder::fromXContent));
registerQuery(new QuerySpec<>(TypeQueryBuilder.NAME, TypeQueryBuilder::new, TypeQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(TypeQueryBuilder.NAME, TypeQueryBuilder::new, TypeQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(ScriptQueryBuilder.NAME, ScriptQueryBuilder::new, ScriptQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(ScriptQueryBuilder.NAME, ScriptQueryBuilder::new, ScriptQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(GeoDistanceQueryBuilder.NAME, GeoDistanceQueryBuilder::new, GeoDistanceQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(GeoDistanceQueryBuilder.NAME, GeoDistanceQueryBuilder::new, GeoDistanceQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(GeoBoundingBoxQueryBuilder.NAME, GeoBoundingBoxQueryBuilder::new, registerQuery(new QuerySpec<>(GeoBoundingBoxQueryBuilder.NAME, GeoBoundingBoxQueryBuilder::new,
GeoBoundingBoxQueryBuilder::fromXContent)); GeoBoundingBoxQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>( registerQuery(new QuerySpec<>(
(new ParseField(GeoPolygonQueryBuilder.NAME).withAllDeprecated(GeoPolygonQueryBuilder.GEO_POLYGON_DEPRECATION_MSG)), (new ParseField(GeoPolygonQueryBuilder.NAME).withAllDeprecated(GeoPolygonQueryBuilder.GEO_POLYGON_DEPRECATION_MSG)),
GeoPolygonQueryBuilder::new, GeoPolygonQueryBuilder::fromXContent)); GeoPolygonQueryBuilder::new, GeoPolygonQueryBuilder::fromXContent));
@ -925,10 +916,8 @@ public class SearchModule {
DistanceFeatureQueryBuilder::fromXContent)); DistanceFeatureQueryBuilder::fromXContent));
registerQuery( registerQuery(
new QuerySpec<>(MatchBoolPrefixQueryBuilder.NAME, MatchBoolPrefixQueryBuilder::new, MatchBoolPrefixQueryBuilder::fromXContent)); new QuerySpec<>(MatchBoolPrefixQueryBuilder.NAME, MatchBoolPrefixQueryBuilder::new, MatchBoolPrefixQueryBuilder::fromXContent));
registerQuery(new QuerySpec<>(GeoShapeQueryBuilder.NAME, GeoShapeQueryBuilder::new, GeoShapeQueryBuilder::fromXContent));
if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) {
registerQuery(new QuerySpec<>(GeoShapeQueryBuilder.NAME, GeoShapeQueryBuilder::new, GeoShapeQueryBuilder::fromXContent));
}
registerFromPlugin(plugins, SearchPlugin::getQueries, this::registerQuery); registerFromPlugin(plugins, SearchPlugin::getQueries, this::registerQuery);
} }
@ -955,24 +944,24 @@ public class SearchModule {
public static List<NamedWriteableRegistry.Entry> getIntervalsSourceProviderNamedWritables() { public static List<NamedWriteableRegistry.Entry> getIntervalsSourceProviderNamedWritables() {
return unmodifiableList(Arrays.asList( return unmodifiableList(Arrays.asList(
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Match.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Match.NAME,
IntervalsSourceProvider.Match::new), IntervalsSourceProvider.Match::new),
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Combine.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Combine.NAME,
IntervalsSourceProvider.Combine::new), IntervalsSourceProvider.Combine::new),
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Disjunction.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Disjunction.NAME,
IntervalsSourceProvider.Disjunction::new), IntervalsSourceProvider.Disjunction::new),
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Prefix.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Prefix.NAME,
IntervalsSourceProvider.Prefix::new), IntervalsSourceProvider.Prefix::new),
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Wildcard.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Wildcard.NAME,
IntervalsSourceProvider.Wildcard::new), IntervalsSourceProvider.Wildcard::new),
new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Fuzzy.NAME, new NamedWriteableRegistry.Entry(IntervalsSourceProvider.class, IntervalsSourceProvider.Fuzzy.NAME,
IntervalsSourceProvider.Fuzzy::new))); IntervalsSourceProvider.Fuzzy::new)));
} }
private void registerQuery(QuerySpec<?> spec) { private void registerQuery(QuerySpec<?> spec) {
namedWriteables.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, spec.getName().getPreferredName(), spec.getReader())); namedWriteables.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, spec.getName().getPreferredName(), spec.getReader()));
namedXContents.add(new NamedXContentRegistry.Entry(QueryBuilder.class, spec.getName(), namedXContents.add(new NamedXContentRegistry.Entry(QueryBuilder.class, spec.getName(),
(p, c) -> spec.getParser().fromXContent(p))); (p, c) -> spec.getParser().fromXContent(p)));
} }
private void registerBoolQuery(ParseField name, Writeable.Reader<QueryBuilder> reader) { private void registerBoolQuery(ParseField name, Writeable.Reader<QueryBuilder> reader) {

View file

@ -26,6 +26,7 @@ import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon; import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle; import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.geometry.utils.GeographyValidator; import org.elasticsearch.geometry.utils.GeographyValidator;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
@ -36,9 +37,8 @@ import java.util.Collections;
/** /**
* Tests for {@code GeoJSONShapeParser} * Tests for {@code GeoJSONShapeParser}
*/ */
public class GeoJsonParserTests extends BaseGeoParsingTestCase { public class GeoJsonParserTests extends ESTestCase {
@Override
public void testParsePoint() throws IOException { public void testParsePoint() throws IOException {
XContentBuilder pointGeoJson = XContentFactory.jsonBuilder() XContentBuilder pointGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -49,7 +49,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
assertGeometryEquals(expected, pointGeoJson); assertGeometryEquals(expected, pointGeoJson);
} }
@Override
public void testParseLineString() throws IOException { public void testParseLineString() throws IOException {
XContentBuilder lineGeoJson = XContentFactory.jsonBuilder() XContentBuilder lineGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -67,7 +66,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
} }
} }
@Override
public void testParseMultiLineString() throws IOException { public void testParseMultiLineString() throws IOException {
XContentBuilder multilinesGeoJson = XContentFactory.jsonBuilder() XContentBuilder multilinesGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -138,7 +136,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
} }
} }
@Override
public void testParseEnvelope() throws IOException { public void testParseEnvelope() throws IOException {
// test #1: envelope with expected coordinate order (TopLeft, BottomRight) // test #1: envelope with expected coordinate order (TopLeft, BottomRight)
XContentBuilder multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", randomBoolean() ? "envelope" : "bbox") XContentBuilder multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", randomBoolean() ? "envelope" : "bbox")
@ -189,7 +186,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
} }
} }
@Override
public void testParsePolygon() throws IOException { public void testParsePolygon() throws IOException {
XContentBuilder polygonGeoJson = XContentFactory.jsonBuilder() XContentBuilder polygonGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -516,7 +512,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
assertGeometryEquals(p, polygonGeoJson); assertGeometryEquals(p, polygonGeoJson);
} }
@Override
public void testParseMultiPoint() throws IOException { public void testParseMultiPoint() throws IOException {
XContentBuilder multiPointGeoJson = XContentFactory.jsonBuilder() XContentBuilder multiPointGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -532,7 +527,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
new Point(101, 1))), multiPointGeoJson); new Point(101, 1))), multiPointGeoJson);
} }
@Override
public void testParseMultiPolygon() throws IOException { public void testParseMultiPolygon() throws IOException {
// two polygons; one without hole, one with hole // two polygons; one without hole, one with hole
XContentBuilder multiPolygonGeoJson = XContentFactory.jsonBuilder() XContentBuilder multiPolygonGeoJson = XContentFactory.jsonBuilder()
@ -580,7 +574,6 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
assertGeometryEquals(polygons, multiPolygonGeoJson); assertGeometryEquals(polygons, multiPolygonGeoJson);
} }
@Override
public void testParseGeometryCollection() throws IOException { public void testParseGeometryCollection() throws IOException {
XContentBuilder geometryCollectionGeoJson = XContentFactory.jsonBuilder() XContentBuilder geometryCollectionGeoJson = XContentFactory.jsonBuilder()
.startObject() .startObject()
@ -641,7 +634,7 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
.endObject(); .endObject();
Point expectedPt = new Point(100, 0); Point expectedPt = new Point(100, 0);
assertGeometryEquals(expectedPt, pointGeoJson, false); assertGeometryEquals(expectedPt, pointGeoJson);
} }
public void testParseOrientationOption() throws IOException { public void testParseOrientationOption() throws IOException {
@ -763,4 +756,11 @@ public class GeoJsonParserTests extends BaseGeoParsingTestCase {
assertNull(parser.nextToken()); // no more elements afterwards assertNull(parser.nextToken()); // no more elements afterwards
} }
} }
private void assertGeometryEquals(org.elasticsearch.geometry.Geometry expected, XContentBuilder geoJson) throws IOException {
try (XContentParser parser = createParser(geoJson)) {
parser.nextToken();
assertEquals(expected, GeoJson.fromXContent(GeographyValidator.instance(false), false, true, parser));
}
}
} }

View file

@ -8,18 +8,12 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.GeometryCollection;
import org.elasticsearch.geometry.ShapeType;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import static org.elasticsearch.geo.GeometryTestUtils.randomGeometry; import static org.elasticsearch.geo.GeometryTestUtils.randomGeometry;
import static org.elasticsearch.index.query.LegacyGeoShapeQueryProcessor.geometryToShapeBuilder;
public class GeometryIOTests extends ESTestCase { public class GeometryIOTests extends ESTestCase {
@ -27,69 +21,16 @@ public class GeometryIOTests extends ESTestCase {
for (int i = 0; i < randomIntBetween(1, 20); i++) { for (int i = 0; i < randomIntBetween(1, 20); i++) {
boolean hasAlt = randomBoolean(); boolean hasAlt = randomBoolean();
Geometry geometry = randomGeometry(hasAlt); Geometry geometry = randomGeometry(hasAlt);
if (shapeSupported(geometry) && randomBoolean()) { // Test Geometry -> Geometry
// Shape builder conversion doesn't support altitude try (BytesStreamOutput out = new BytesStreamOutput()) {
ShapeBuilder<?, ?, ?> shapeBuilder = geometryToShapeBuilder(geometry); GeometryIO.writeGeometry(out, geometry);
if (randomBoolean()) { ;
Geometry actual = shapeBuilder.buildGeometry(); try (StreamInput in = out.bytes().streamInput()) {
Geometry actual = GeometryIO.readGeometry(in);
assertEquals(geometry, actual); assertEquals(geometry, actual);
} assertEquals(0, in.available());
if (randomBoolean()) {
// Test ShapeBuilder -> Geometry Serialization
try (BytesStreamOutput out = new BytesStreamOutput()) {
out.writeNamedWriteable(shapeBuilder);
try (StreamInput in = out.bytes().streamInput()) {
Geometry actual = GeometryIO.readGeometry(in);
assertEquals(geometry, actual);
assertEquals(0, in.available());
}
}
} else {
// Test Geometry -> ShapeBuilder Serialization
try (BytesStreamOutput out = new BytesStreamOutput()) {
GeometryIO.writeGeometry(out, geometry);
try (StreamInput in = out.bytes().streamInput()) {
try (StreamInput nin = new NamedWriteableAwareStreamInput(in, this.writableRegistry())) {
ShapeBuilder<?, ?, ?> actual = nin.readNamedWriteable(ShapeBuilder.class);
assertEquals(shapeBuilder, actual);
assertEquals(0, in.available());
}
}
}
}
// Test Geometry -> Geometry
try (BytesStreamOutput out = new BytesStreamOutput()) {
GeometryIO.writeGeometry(out, geometry);
;
try (StreamInput in = out.bytes().streamInput()) {
Geometry actual = GeometryIO.readGeometry(in);
assertEquals(geometry, actual);
assertEquals(0, in.available());
}
}
}
}
}
private boolean shapeSupported(Geometry geometry) {
if (geometry.hasZ()) {
return false;
}
if (geometry.type() == ShapeType.GEOMETRYCOLLECTION) {
GeometryCollection<?> collection = (GeometryCollection<?>) geometry;
for (Geometry g : collection) {
if (shapeSupported(g) == false) {
return false;
} }
} }
} }
return true;
}
@Override
protected NamedWriteableRegistry writableRegistry() {
return new NamedWriteableRegistry(GeoShapeType.getShapeWriteables());
} }
} }

View file

@ -1,842 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.common.geo;
import org.elasticsearch.common.geo.builders.CircleBuilder;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
import org.elasticsearch.common.geo.builders.LineStringBuilder;
import org.elasticsearch.common.geo.builders.MultiLineStringBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.test.ESTestCase;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.impl.PointImpl;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiLineString;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiPolygon;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertPolygon;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
/**
* Tests for {@link ShapeBuilder}
*/
public class ShapeBuilderTests extends ESTestCase {
public void testNewPoint() {
PointBuilder pb = new PointBuilder().coordinate(-100, 45);
Point point = pb.buildS4J();
assertEquals(-100D, point.getX(), 0.0d);
assertEquals(45D, point.getY(), 0.0d);
org.elasticsearch.geometry.Point geoPoint = pb.buildGeometry();
assertEquals(-100D, geoPoint.getX(), 0.0d);
assertEquals(45D, geoPoint.getY(), 0.0d);
}
public void testNewRectangle() {
EnvelopeBuilder eb = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30));
Rectangle rectangle = eb.buildS4J();
assertEquals(-45D, rectangle.getMinX(), 0.0d);
assertEquals(-30D, rectangle.getMinY(), 0.0d);
assertEquals(45D, rectangle.getMaxX(), 0.0d);
assertEquals(30D, rectangle.getMaxY(), 0.0d);
org.elasticsearch.geometry.Rectangle luceneRectangle = eb.buildGeometry();
assertEquals(-45D, luceneRectangle.getMinX(), 0.0d);
assertEquals(-30D, luceneRectangle.getMinY(), 0.0d);
assertEquals(45D, luceneRectangle.getMaxX(), 0.0d);
assertEquals(30D, luceneRectangle.getMaxY(), 0.0d);
}
public void testNewPolygon() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-45, 30)
.coordinate(45, 30)
.coordinate(45, -30)
.coordinate(-45, -30)
.coordinate(-45, 30));
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testNewPolygon_coordinate() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(new Coordinate(-45, 30))
.coordinate(new Coordinate(45, 30))
.coordinate(new Coordinate(45, -30))
.coordinate(new Coordinate(-45, -30))
.coordinate(new Coordinate(-45, 30)));
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testNewPolygon_coordinates() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinates(new Coordinate(-45, 30), new Coordinate(45, 30),
new Coordinate(45, -30), new Coordinate(-45, -30), new Coordinate(-45, 30))
);
Polygon poly = pb.toPolygonS4J();
LineString exterior = poly.getExteriorRing();
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
LinearRing polygon = pb.toPolygonGeometry().getPolygon();
assertEquals(polygon.getY(0), 30, 0d);
assertEquals(polygon.getX(0), -45, 0d);
assertEquals(polygon.getY(1), 30, 0d);
assertEquals(polygon.getX(1), 45, 0d);
assertEquals(polygon.getY(2), -30, 0d);
assertEquals(polygon.getX(2), 45, 0d);
assertEquals(polygon.getY(3), -30, 0d);
assertEquals(polygon.getX(3), -45, 0d);
}
public void testLineStringBuilder() {
// Building a simple LineString
LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-130.0, 55.0)
.coordinate(-130.0, -40.0)
.coordinate(-15.0, -40.0)
.coordinate(-20.0, 50.0)
.coordinate(-45.0, 50.0)
.coordinate(-45.0, -15.0)
.coordinate(-110.0, -15.0)
.coordinate(-110.0, 55.0));
lsb.buildS4J();
buildGeometry(lsb);
// Building a linestring that needs to be wrapped
lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(100.0, 50.0)
.coordinate(110.0, -40.0)
.coordinate(240.0, -40.0)
.coordinate(230.0, 60.0)
.coordinate(200.0, 60.0)
.coordinate(200.0, -30.0)
.coordinate(130.0, -30.0)
.coordinate(130.0, 60.0));
lsb.buildS4J();
buildGeometry(lsb);
// Building a lineString on the dateline
lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-180.0, 80.0)
.coordinate(-180.0, 40.0)
.coordinate(-180.0, -40.0)
.coordinate(-180.0, -80.0));
lsb.buildS4J();
buildGeometry(lsb);
// Building a lineString on the dateline
lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(180.0, 80.0)
.coordinate(180.0, 40.0)
.coordinate(180.0, -40.0)
.coordinate(180.0, -80.0));
lsb.buildS4J();
buildGeometry(lsb);
}
public void testMultiLineString() {
MultiLineStringBuilder mlsb = new MultiLineStringBuilder()
.linestring(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-100.0, 50.0)
.coordinate(50.0, 50.0)
.coordinate(50.0, 20.0)
.coordinate(-100.0, 20.0)
)
)
.linestring(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-100.0, 20.0)
.coordinate(50.0, 20.0)
.coordinate(50.0, 0.0)
.coordinate(-100.0, 0.0)
)
);
mlsb.buildS4J();
buildGeometry(mlsb);
// LineString that needs to be wrapped
new MultiLineStringBuilder()
.linestring(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(150.0, 60.0)
.coordinate(200.0, 60.0)
.coordinate(200.0, 40.0)
.coordinate(150.0, 40.0)
)
)
.linestring(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(150.0, 20.0)
.coordinate(200.0, 20.0)
.coordinate(200.0, 0.0)
.coordinate(150.0, 0.0)
)
);
mlsb.buildS4J();
buildGeometry(mlsb);
}
public void testPolygonSelfIntersection() {
PolygonBuilder newPolygon = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-40.0, 50.0)
.coordinate(40.0, 50.0)
.coordinate(-40.0, -50.0)
.coordinate(40.0, -50.0).close());
Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.buildS4J());
assertThat(e.getMessage(), containsString("Cannot determine orientation: signed area equal to 0"));
}
/** note: only supported by S4J at the moment */
public void testGeoCircle() {
double earthCircumference = 40075016.69;
Circle circle = new CircleBuilder().center(0, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(+180, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(-180, 0).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(-180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(0, 90).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
circle = new CircleBuilder().center(0, -90).radius("100m").buildS4J();
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
double randomLat = (randomDouble() * 180) - 90;
double randomLon = (randomDouble() * 360) - 180;
double randomRadius = randomIntBetween(1, (int) earthCircumference / 4);
circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").buildS4J();
assertEquals((360 * randomRadius) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
}
public void testPolygonWrapping() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0)
.close());
assertMultiPolygon(pb.buildS4J(), true);
assertMultiPolygon(buildGeometry(pb), false);
}
public void testLineStringWrapping() {
LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0)
.close());
assertMultiLineString(lsb.buildS4J(), true);
assertMultiLineString(buildGeometry(lsb), false);
}
public void testDatelineOGC() {
// tests that the following shape (defined in counterclockwise OGC order)
// https://gist.github.com/anonymous/7f1bb6d7e9cd72f5977c crosses the dateline
// expected results: 3 polygons, 1 with a hole
// a giant c shape
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(174,0)
.coordinate(-176,0)
.coordinate(-176,3)
.coordinate(177,3)
.coordinate(177,5)
.coordinate(-176,5)
.coordinate(-176,8)
.coordinate(174,8)
.coordinate(174,0)
);
// 3/4 of an embedded 'c', crossing dateline once
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(175, 1)
.coordinate(175, 7)
.coordinate(-178, 7)
.coordinate(-178, 6)
.coordinate(176, 6)
.coordinate(176, 2)
.coordinate(179, 2)
.coordinate(179,1)
.coordinate(175, 1)
));
// embedded hole right of the dateline
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-179, 1)
.coordinate(-179, 2)
.coordinate(-177, 2)
.coordinate(-177,1)
.coordinate(-179,1)
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testDateline() {
// tests that the following shape (defined in clockwise non-OGC order)
// https://gist.github.com/anonymous/7f1bb6d7e9cd72f5977c crosses the dateline
// expected results: 3 polygons, 1 with a hole
// a giant c shape
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-186,0)
.coordinate(-176,0)
.coordinate(-176,3)
.coordinate(-183,3)
.coordinate(-183,5)
.coordinate(-176,5)
.coordinate(-176,8)
.coordinate(-186,8)
.coordinate(-186,0)
);
// 3/4 of an embedded 'c', crossing dateline once
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-185,1)
.coordinate(-181,1)
.coordinate(-181,2)
.coordinate(-184,2)
.coordinate(-184,6)
.coordinate(-178,6)
.coordinate(-178,7)
.coordinate(-185,7)
.coordinate(-185,1)
));
// embedded hole right of the dateline
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-179,1)
.coordinate(-177,1)
.coordinate(-177,2)
.coordinate(-179,2)
.coordinate(-179,1)
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testComplexShapeWithHole() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-85.0018514,37.1311314)
.coordinate(-85.0016645,37.1315293)
.coordinate(-85.0016246,37.1317069)
.coordinate(-85.0016526,37.1318183)
.coordinate(-85.0017119,37.1319196)
.coordinate(-85.0019371,37.1321182)
.coordinate(-85.0019972,37.1322115)
.coordinate(-85.0019942,37.1323234)
.coordinate(-85.0019543,37.1324336)
.coordinate(-85.001906,37.1324985)
.coordinate(-85.001834,37.1325497)
.coordinate(-85.0016965,37.1325907)
.coordinate(-85.0016011,37.1325873)
.coordinate(-85.0014816,37.1325353)
.coordinate(-85.0011755,37.1323509)
.coordinate(-85.000955,37.1322802)
.coordinate(-85.0006241,37.1322529)
.coordinate(-85.0000002,37.1322307)
.coordinate(-84.9994,37.1323001)
.coordinate(-84.999109,37.1322864)
.coordinate(-84.998934,37.1322415)
.coordinate(-84.9988639,37.1321888)
.coordinate(-84.9987841,37.1320944)
.coordinate(-84.9987208,37.131954)
.coordinate(-84.998736,37.1316611)
.coordinate(-84.9988091,37.131334)
.coordinate(-84.9989283,37.1311337)
.coordinate(-84.9991943,37.1309198)
.coordinate(-84.9993573,37.1308459)
.coordinate(-84.9995888,37.1307924)
.coordinate(-84.9998746,37.130806)
.coordinate(-85.0000002,37.1308358)
.coordinate(-85.0004984,37.1310658)
.coordinate(-85.0008008,37.1311625)
.coordinate(-85.0009461,37.1311684)
.coordinate(-85.0011373,37.1311515)
.coordinate(-85.0016455,37.1310491)
.coordinate(-85.0018514,37.1311314)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-85.0000002,37.1317672)
.coordinate(-85.0001983,37.1317538)
.coordinate(-85.0003378,37.1317582)
.coordinate(-85.0004697,37.131792)
.coordinate(-85.0008048,37.1319439)
.coordinate(-85.0009342,37.1319838)
.coordinate(-85.0010184,37.1319463)
.coordinate(-85.0010618,37.13184)
.coordinate(-85.0010057,37.1315102)
.coordinate(-85.000977,37.1314403)
.coordinate(-85.0009182,37.1313793)
.coordinate(-85.0005366,37.1312209)
.coordinate(-85.000224,37.1311466)
.coordinate(-85.000087,37.1311356)
.coordinate(-85.0000002,37.1311433)
.coordinate(-84.9995021,37.1312336)
.coordinate(-84.9993308,37.1312859)
.coordinate(-84.9992567,37.1313252)
.coordinate(-84.9991868,37.1314277)
.coordinate(-84.9991593,37.1315381)
.coordinate(-84.9991841,37.1316527)
.coordinate(-84.9992329,37.1317117)
.coordinate(-84.9993527,37.1317788)
.coordinate(-84.9994931,37.1318061)
.coordinate(-84.9996815,37.1317979)
.coordinate(-85.0000002,37.1317672)
)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithHoleAtEdgeEndPoints() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-4, 2)
.coordinate(4, 2)
.coordinate(6, 0)
.coordinate(4, -2)
.coordinate(-4, -2)
.coordinate(-6, 0)
.coordinate(-4, 2)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(4, 1)
.coordinate(4, -1)
.coordinate(-4, -1)
.coordinate(-4, 1)
.coordinate(4, 1)
));
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithPointOnDateline() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(176, 4)
.coordinate(176, -4)
.coordinate(180, 0)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithEdgeAlongDateline() {
// test case 1: test the positive side of the dateline
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(176, 4)
.coordinate(180, -4)
.coordinate(180, 0)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
// test case 2: test the negative side of the dateline
builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-176, 4)
.coordinate(-180, 0)
.coordinate(-180, -4)
.coordinate(-176, 4)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithBoundaryHoles() {
// test case 1: test the positive side of the dateline
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(176, 10)
.coordinate(180, 5)
.coordinate(180, -5)
.coordinate(176, -10)
.coordinate(176, 10)
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
// test case 2: test the negative side of the dateline
builder = new PolygonBuilder(
new CoordinatesBuilder()
.coordinate(-176, 15)
.coordinate(179, 10)
.coordinate(179, -10)
.coordinate(-176, -15)
.coordinate(-172, 0)
.close()
);
builder.hole(new LineStringBuilder(
new CoordinatesBuilder()
.coordinate(-176, 10)
.coordinate(-176, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-176, 10)
.close()
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithHoleTouchingAtDateline() throws Exception {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-180, 90)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, 90)
.coordinate(-180, 90)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(180.0, -16.14)
.coordinate(178.53, -16.64)
.coordinate(178.49, -16.82)
.coordinate(178.73, -17.02)
.coordinate(178.86, -16.86)
.coordinate(180.0, -16.14)
));
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithTangentialHole() {
// test a shape with one tangential (shared) vertex (should pass)
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(179, 10)
.coordinate(168, 15)
.coordinate(164, 0)
.coordinate(166, -15)
.coordinate(179, -10)
.coordinate(179, 10)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(-178, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-177, 10)
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithInvalidTangentialHole() {
// test a shape with one invalid tangential (shared) vertex (should throw exception)
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(179, 10)
.coordinate(168, 15)
.coordinate(164, 0)
.coordinate(166, -15)
.coordinate(179, -10)
.coordinate(179, 10)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(164, 0)
.coordinate(175, 10)
.coordinate(175, 5)
.coordinate(179, -10)
.coordinate(164, 0)
));
Exception e;
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
e = expectThrows(IllegalArgumentException.class, () -> buildGeometry(builder.close()));
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
}
public void testBoundaryShapeWithTangentialHole() {
// test a shape with one tangential (shared) vertex for each hole (should pass)
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(-178, -10)
.coordinate(-180, -5)
.coordinate(-180, 5)
.coordinate(-177, 10)
));
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(172, 0)
.coordinate(176, 10)
.coordinate(176, -5)
.coordinate(172, 0)
));
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testBoundaryShapeWithInvalidTangentialHole() {
// test shape with two tangential (shared) vertices (should throw exception)
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(176, 15)
.coordinate(172, 0)
.coordinate(176, -15)
.coordinate(-177, -10)
.coordinate(-177, 10)
);
builder.hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-177, 10)
.coordinate(172, 0)
.coordinate(180, -5)
.coordinate(176, -10)
.coordinate(-177, 10)
));
Exception e;
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
e = expectThrows(IllegalArgumentException.class, () -> buildGeometry(builder.close()));
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
}
/**
* Test an enveloping polygon around the max mercator bounds
*/
public void testBoundaryShape() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-180, 90)
.coordinate(180, 90)
.coordinate(180, -90)
.coordinate(-180, 90)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithAlternateOrientation() {
// cw: should produce a multi polygon spanning hemispheres
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(176, 4)
.coordinate(-176, 4)
.coordinate(180, 0)
);
assertPolygon(builder.close().buildS4J(), true);
assertPolygon(buildGeometry(builder.close()), false);
// cw: geo core will convert to ccw across the dateline
builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(-176, 4)
.coordinate(176, 4)
.coordinate(180, 0)
);
assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(buildGeometry(builder.close()), false);
}
public void testShapeWithConsecutiveDuplicatePoints() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(176, 4)
.coordinate(176, 4)
.coordinate(-176, 4)
.coordinate(180, 0)
);
// duplicated points are removed
PolygonBuilder expected = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, 0)
.coordinate(176, 4)
.coordinate(-176, 4)
.coordinate(180, 0)
);
assertEquals(buildGeometry(expected.close()), buildGeometry(builder.close()));
assertEquals(expected.close().buildS4J(), builder.close().buildS4J());
}
public void testShapeWithCoplanarVerticalPoints() throws Exception {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, -36)
.coordinate(180, 90)
.coordinate(-180, 90)
.coordinate(-180, 79)
.coordinate(16, 58)
.coordinate(8, 13)
.coordinate(-180, 74)
.coordinate(-180, -85)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, -85)
.coordinate(26, 6)
.coordinate(33, 62)
.coordinate(180, -36)
);
//coplanar points on vertical edge are removed.
PolygonBuilder expected = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(180, -36)
.coordinate(180, 90)
.coordinate(-180, 90)
.coordinate(-180, 79)
.coordinate(16, 58)
.coordinate(8, 13)
.coordinate(-180, 74)
.coordinate(-180, -90)
.coordinate(180, -90)
.coordinate(180, -85)
.coordinate(26, 6)
.coordinate(33, 62)
.coordinate(180, -36)
);
assertEquals(buildGeometry(expected.close()), buildGeometry(builder.close()));
assertEquals(expected.close().buildS4J(), builder.close().buildS4J());
}
public void testPolygon3D() {
String expected = "{\n" +
" \"type\" : \"polygon\",\n" +
" \"orientation\" : \"right\",\n" +
" \"coordinates\" : [\n" +
" [\n" +
" [\n" +
" -45.0,\n" +
" 30.0,\n" +
" 100.0\n" +
" ],\n" +
" [\n" +
" 45.0,\n" +
" 30.0,\n" +
" 75.0\n" +
" ],\n" +
" [\n" +
" 45.0,\n" +
" -30.0,\n" +
" 77.0\n" +
" ],\n" +
" [\n" +
" -45.0,\n" +
" -30.0,\n" +
" 101.0\n" +
" ],\n" +
" [\n" +
" -45.0,\n" +
" 30.0,\n" +
" 110.0\n" +
" ]\n" +
" ]\n" +
" ]\n" +
"}";
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(new Coordinate(-45, 30, 100))
.coordinate(new Coordinate(45, 30, 75))
.coordinate(new Coordinate(45, -30, 77))
.coordinate(new Coordinate(-45, -30, 101))
.coordinate(new Coordinate(-45, 30, 110)));
assertEquals(expected, pb.toString());
}
public void testInvalidSelfCrossingPolygon() {
PolygonBuilder builder = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(0, 0)
.coordinate(0, 2)
.coordinate(1, 1.9)
.coordinate(0.5, 1.8)
.coordinate(1.5, 1.8)
.coordinate(1, 1.9)
.coordinate(2, 2)
.coordinate(2, 0)
.coordinate(0, 0)
);
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
assertThat(e.getMessage(), containsString("Self-intersection at or near point ["));
assertThat(e.getMessage(), not(containsString("NaN")));
}
public Object buildGeometry(ShapeBuilder<?, ?, ?> builder) {
return new GeoShapeIndexer(true, "name").prepareForIndexing(builder.buildGeometry());
}
}

View file

@ -1,96 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.common.geo.builders;
import org.elasticsearch.test.geo.RandomShapeGenerator;
import java.io.IOException;
public class GeometryCollectionBuilderTests extends AbstractShapeBuilderTestCase<GeometryCollectionBuilder> {
@Override
protected GeometryCollectionBuilder createTestShapeBuilder() {
GeometryCollectionBuilder geometryCollection = new GeometryCollectionBuilder();
int shapes = randomIntBetween(0, 8);
for (int i = 0; i < shapes; i++) {
switch (randomIntBetween(0, 7)) {
case 0:
geometryCollection.shape(PointBuilderTests.createRandomShape());
break;
case 1:
geometryCollection.shape(CircleBuilderTests.createRandomShape());
break;
case 2:
geometryCollection.shape(EnvelopeBuilderTests.createRandomShape());
break;
case 3:
geometryCollection.shape(LineStringBuilderTests.createRandomShape());
break;
case 4:
geometryCollection.shape(MultiLineStringBuilderTests.createRandomShape());
break;
case 5:
geometryCollection.shape(MultiPolygonBuilderTests.createRandomShape());
break;
case 6:
geometryCollection.shape(MultiPointBuilderTests.createRandomShape());
break;
case 7:
geometryCollection.shape(PolygonBuilderTests.createRandomShape());
break;
}
}
return geometryCollection;
}
@Override
protected GeometryCollectionBuilder createMutation(GeometryCollectionBuilder original) throws IOException {
return mutate(original);
}
static GeometryCollectionBuilder mutate(GeometryCollectionBuilder original) throws IOException {
GeometryCollectionBuilder mutation = copyShape(original);
if (mutation.shapes.size() > 0) {
int shapePosition = randomIntBetween(0, mutation.shapes.size() - 1);
ShapeBuilder<?, ?, ?> shapeToChange = mutation.shapes.get(shapePosition);
switch (shapeToChange.type()) {
case POINT:
shapeToChange = PointBuilderTests.mutate((PointBuilder) shapeToChange);
break;
case CIRCLE:
shapeToChange = CircleBuilderTests.mutate((CircleBuilder) shapeToChange);
break;
case ENVELOPE:
shapeToChange = EnvelopeBuilderTests.mutate((EnvelopeBuilder) shapeToChange);
break;
case LINESTRING:
shapeToChange = LineStringBuilderTests.mutate((LineStringBuilder) shapeToChange);
break;
case MULTILINESTRING:
shapeToChange = MultiLineStringBuilderTests.mutate((MultiLineStringBuilder) shapeToChange);
break;
case MULTIPOLYGON:
shapeToChange = MultiPolygonBuilderTests.mutate((MultiPolygonBuilder) shapeToChange);
break;
case MULTIPOINT:
shapeToChange = MultiPointBuilderTests.mutate((MultiPointBuilder) shapeToChange);
break;
case POLYGON:
shapeToChange = PolygonBuilderTests.mutate((PolygonBuilder) shapeToChange);
break;
case GEOMETRYCOLLECTION:
throw new UnsupportedOperationException("GeometryCollection should not be nested inside each other");
}
mutation.shapes.set(shapePosition, shapeToChange);
} else {
mutation.shape(RandomShapeGenerator.createShape(random()));
}
return mutation;
}
}

View file

@ -1,105 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.index.query;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.SpatialStrategy;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.ShapeType;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestLegacyGeoShapeFieldMapperPlugin;
import org.elasticsearch.test.VersionUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
public class LegacyGeoShapeFieldQueryTests extends GeoShapeQueryBuilderTests {
@SuppressWarnings("deprecation")
@Override
protected Collection<Class<? extends Plugin>> getPlugins() {
return Collections.singletonList(TestLegacyGeoShapeFieldMapperPlugin.class);
}
@Override
protected String fieldName() {
return GEO_SHAPE_FIELD_NAME;
}
@Override
protected Settings createTestIndexSettings() {
// force the legacy shape impl
Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.V_6_5_0);
return Settings.builder()
.put(super.createTestIndexSettings())
.put(IndexMetadata.SETTING_VERSION_CREATED, version)
.build();
}
@Override
protected GeoShapeQueryBuilder doCreateTestQueryBuilder(boolean indexedShape) {
Geometry geometry = GeometryTestUtils.randomGeometry(false);
GeoShapeQueryBuilder builder;
clearShapeFields();
if (indexedShape == false) {
builder = new GeoShapeQueryBuilder(fieldName(), geometry);
} else {
indexedShapeToReturn = geometry;
indexedShapeId = randomAlphaOfLengthBetween(3, 20);
builder = new GeoShapeQueryBuilder(fieldName(), indexedShapeId);
if (randomBoolean()) {
indexedShapeIndex = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeIndex(indexedShapeIndex);
}
if (randomBoolean()) {
indexedShapePath = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapePath(indexedShapePath);
}
if (randomBoolean()) {
indexedShapeRouting = randomAlphaOfLengthBetween(3, 20);
builder.indexedShapeRouting(indexedShapeRouting);
}
}
if (randomBoolean()) {
SpatialStrategy strategy = randomFrom(SpatialStrategy.values());
// ShapeType.MULTILINESTRING + SpatialStrategy.TERM can lead to large queries and will slow down tests, so
// we try to avoid that combination
while (geometry.type() == ShapeType.MULTILINESTRING && strategy == SpatialStrategy.TERM) {
strategy = randomFrom(SpatialStrategy.values());
}
builder.strategy(strategy);
if (strategy != SpatialStrategy.TERM) {
builder.relation(randomFrom(ShapeRelation.values()));
}
}
if (randomBoolean()) {
builder.ignoreUnmapped(randomBoolean());
}
return builder;
}
public void testInvalidRelation() throws IOException {
Geometry shape = GeometryTestUtils.randomGeometry(false);
GeoShapeQueryBuilder builder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
builder.strategy(SpatialStrategy.TERM);
expectThrows(IllegalArgumentException.class, () -> builder.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
GeoShapeQueryBuilder builder2 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
builder2.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN));
expectThrows(IllegalArgumentException.class, () -> builder2.strategy(SpatialStrategy.TERM));
GeoShapeQueryBuilder builder3 = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, shape);
builder3.strategy(SpatialStrategy.TERM);
expectThrows(IllegalArgumentException.class, () -> builder3.relation(randomFrom(ShapeRelation.DISJOINT, ShapeRelation.WITHIN)));
}
}

View file

@ -8,9 +8,6 @@
package org.elasticsearch.index.search.geo; package org.elasticsearch.index.search.geo;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.geo.GeoUtils;
@ -19,8 +16,6 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.geometry.utils.Geohash; import org.elasticsearch.geometry.utils.Geohash;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import java.io.IOException; import java.io.IOException;
@ -617,56 +612,7 @@ public class GeoUtilsTests extends ESTestCase {
} }
} }
public void testPrefixTreeCellSizes() {
assertThat(GeoUtils.EARTH_SEMI_MAJOR_AXIS, equalTo(DistanceUtils.EARTH_EQUATORIAL_RADIUS_KM * 1000));
assertThat(GeoUtils.quadTreeCellWidth(0), lessThanOrEqualTo(GeoUtils.EARTH_EQUATOR));
SpatialContext spatialContext = new SpatialContext(true);
GeohashPrefixTree geohashPrefixTree = new GeohashPrefixTree(spatialContext, GeohashPrefixTree.getMaxLevelsPossible() / 2);
Cell gNode = geohashPrefixTree.getWorldCell();
for (int i = 0; i < geohashPrefixTree.getMaxLevels(); i++) {
double width = GeoUtils.geoHashCellWidth(i);
double height = GeoUtils.geoHashCellHeight(i);
double size = GeoUtils.geoHashCellSize(i);
double degrees = 360.0 * width / GeoUtils.EARTH_EQUATOR;
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.geoHashLevelsForPrecision(size), equalTo(geohashPrefixTree.getLevelForDistance(degrees)));
assertThat("width at level " + i,
gNode.getShape().getBoundingBox().getWidth(), equalTo(360.d * width / GeoUtils.EARTH_EQUATOR));
assertThat("height at level " + i, gNode.getShape().getBoundingBox().getHeight(), equalTo(180.d * height
/ GeoUtils.EARTH_POLAR_DISTANCE));
gNode = gNode.getNextLevelCells(null).next();
}
QuadPrefixTree quadPrefixTree = new QuadPrefixTree(spatialContext);
Cell qNode = quadPrefixTree.getWorldCell();
for (int i = 0; i < quadPrefixTree.getMaxLevels(); i++) {
double degrees = 360.0 / (1L << i);
double width = GeoUtils.quadTreeCellWidth(i);
double height = GeoUtils.quadTreeCellHeight(i);
double size = GeoUtils.quadTreeCellSize(i);
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.quadTreeLevelsForPrecision(size), equalTo(quadPrefixTree.getLevelForDistance(degrees)));
assertThat("width at level " + i,
qNode.getShape().getBoundingBox().getWidth(), equalTo(360.d * width / GeoUtils.EARTH_EQUATOR));
assertThat("height at level " + i, qNode.getShape().getBoundingBox().getHeight(), equalTo(180.d * height
/ GeoUtils.EARTH_POLAR_DISTANCE));
qNode = qNode.getNextLevelCells(null).next();
}
}
public void testParseGeoPointGeohashPositions() throws IOException { public void testParseGeoPointGeohashPositions() throws IOException {
assertNormalizedPoint(parseGeohash("drt5", assertNormalizedPoint(parseGeohash("drt5",

View file

@ -14,8 +14,7 @@ import java.util.Random;
/** /**
* Random geo generation utilities for randomized {@code geo_point} type testing * Random geo generation utilities for randomized {@code geo_point} type testing
* does not depend on jts or spatial4j. Use {@link org.elasticsearch.test.geo.RandomShapeGenerator} * does not depend on jts or spatial4j.
* to create random OGC compliant shapes.
*/ */
public class RandomGeoGenerator { public class RandomGeoGenerator {

View file

@ -22,7 +22,7 @@ import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple; import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper; import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.ingest.IngestService; import org.elasticsearch.ingest.IngestService;
import org.elasticsearch.ingest.PipelineConfiguration; import org.elasticsearch.ingest.PipelineConfiguration;
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
@ -224,7 +224,7 @@ public class ClusterDeprecationChecks {
XContentType.JSON); XContentType.JSON);
Map<String, Object> mappingAsMap = tuple.v2(); Map<String, Object> mappingAsMap = tuple.v2();
List<String> messages = mappingAsMap == null ? Collections.emptyList() : List<String> messages = mappingAsMap == null ? Collections.emptyList() :
IndexDeprecationChecks.findInPropertiesRecursively(LegacyGeoShapeFieldMapper.CONTENT_TYPE, IndexDeprecationChecks.findInPropertiesRecursively(GeoShapeFieldMapper.CONTENT_TYPE,
mappingAsMap, mappingAsMap,
IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam, IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam,
IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage); IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage);
@ -253,7 +253,7 @@ public class ClusterDeprecationChecks {
XContentType.JSON); XContentType.JSON);
Map<String, Object> mappingAsMap = (Map<String, Object>) tuple.v2().get("_doc"); Map<String, Object> mappingAsMap = (Map<String, Object>) tuple.v2().get("_doc");
List<String> messages = mappingAsMap == null ? Collections.emptyList() : List<String> messages = mappingAsMap == null ? Collections.emptyList() :
IndexDeprecationChecks.findInPropertiesRecursively(LegacyGeoShapeFieldMapper.CONTENT_TYPE, IndexDeprecationChecks.findInPropertiesRecursively(GeoShapeFieldMapper.CONTENT_TYPE,
mappingAsMap, mappingAsMap,
IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam, IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam,
IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage); IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage);

View file

@ -16,10 +16,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexModule;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.IndexingSlowLog; import org.elasticsearch.index.IndexingSlowLog;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.xpack.core.deprecation.DeprecationIssue; import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
import org.elasticsearch.index.SearchSlowLog; import org.elasticsearch.index.SearchSlowLog;
import org.elasticsearch.index.SlowLogLevel; import org.elasticsearch.index.SlowLogLevel;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.SearchModule;
import java.util.ArrayList; import java.util.ArrayList;
@ -384,8 +384,8 @@ public class IndexDeprecationChecks {
} }
protected static boolean isGeoShapeFieldWithDeprecatedParam(Map<?, ?> property) { protected static boolean isGeoShapeFieldWithDeprecatedParam(Map<?, ?> property) {
return LegacyGeoShapeFieldMapper.CONTENT_TYPE.equals(property.get("type")) && return GeoShapeFieldMapper.CONTENT_TYPE.equals(property.get("type")) &&
LegacyGeoShapeFieldMapper.DEPRECATED_PARAMETERS.stream().anyMatch(deprecatedParameter -> GeoShapeFieldMapper.DEPRECATED_PARAMETERS.stream().anyMatch(deprecatedParameter ->
property.containsKey(deprecatedParameter) property.containsKey(deprecatedParameter)
); );
} }
@ -393,7 +393,7 @@ public class IndexDeprecationChecks {
protected static String formatDeprecatedGeoShapeParamMessage(String type, Map.Entry<?, ?> entry) { protected static String formatDeprecatedGeoShapeParamMessage(String type, Map.Entry<?, ?> entry) {
String fieldName = entry.getKey().toString(); String fieldName = entry.getKey().toString();
Map<?, ?> value = (Map<?, ?>) entry.getValue(); Map<?, ?> value = (Map<?, ?>) entry.getValue();
return LegacyGeoShapeFieldMapper.DEPRECATED_PARAMETERS.stream() return GeoShapeFieldMapper.DEPRECATED_PARAMETERS.stream()
.filter(deprecatedParameter -> value.containsKey(deprecatedParameter)) .filter(deprecatedParameter -> value.containsKey(deprecatedParameter))
.map(deprecatedParameter -> String.format(Locale.ROOT, "parameter [%s] in field [%s]", deprecatedParameter, fieldName)) .map(deprecatedParameter -> String.format(Locale.ROOT, "parameter [%s] in field [%s]", deprecatedParameter, fieldName))
.collect(Collectors.joining("; ")); .collect(Collectors.joining("; "));
@ -405,7 +405,7 @@ public class IndexDeprecationChecks {
return null; return null;
} }
Map<String, Object> sourceAsMap = indexMetadata.mapping().getSourceAsMap(); Map<String, Object> sourceAsMap = indexMetadata.mapping().getSourceAsMap();
List<String> messages = findInPropertiesRecursively(LegacyGeoShapeFieldMapper.CONTENT_TYPE, sourceAsMap, List<String> messages = findInPropertiesRecursively(GeoShapeFieldMapper.CONTENT_TYPE, sourceAsMap,
IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam, IndexDeprecationChecks::isGeoShapeFieldWithDeprecatedParam,
IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage); IndexDeprecationChecks::formatDeprecatedGeoShapeParamMessage);
if (messages.isEmpty()) { if (messages.isEmpty()) {

View file

@ -22,6 +22,7 @@ dependencies {
testImplementation project(path: xpackModule('monitoring')) testImplementation project(path: xpackModule('monitoring'))
testImplementation project(path: xpackModule('spatial')) testImplementation project(path: xpackModule('spatial'))
testImplementation project(path: ':modules:legacy-geo')
testImplementation project(path: ':modules:percolator') testImplementation project(path: ':modules:percolator')
testImplementation project(path: xpackModule('sql:sql-action')) testImplementation project(path: xpackModule('sql:sql-action'))
testImplementation project(path: ':modules:analysis-common') testImplementation project(path: ':modules:analysis-common')

View file

@ -6,13 +6,15 @@ esplugin {
name 'spatial' name 'spatial'
description 'A plugin for Basic Spatial features' description 'A plugin for Basic Spatial features'
classname 'org.elasticsearch.xpack.spatial.SpatialPlugin' classname 'org.elasticsearch.xpack.spatial.SpatialPlugin'
extendedPlugins = ['x-pack-core'] extendedPlugins = ['x-pack-core', 'legacy-geo']
} }
dependencies { dependencies {
compileOnly project(path: ':modules:legacy-geo')
compileOnly project(path: xpackModule('core')) compileOnly project(path: xpackModule('core'))
testImplementation(testArtifact(project(xpackModule('core')))) testImplementation(testArtifact(project(xpackModule('core'))))
testImplementation project(path: xpackModule('vector-tile')) testImplementation project(path: xpackModule('vector-tile'))
testImplementation project(path: ':modules:legacy-geo')
yamlRestTestImplementation(testArtifact(project(xpackModule('core')))) yamlRestTestImplementation(testArtifact(project(xpackModule('core'))))
} }

View file

@ -31,7 +31,6 @@ import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.GeoShapeIndexer; import org.elasticsearch.index.mapper.GeoShapeIndexer;
import org.elasticsearch.index.mapper.GeoShapeParser; import org.elasticsearch.index.mapper.GeoShapeParser;
import org.elasticsearch.index.mapper.GeoShapeQueryable; import org.elasticsearch.index.mapper.GeoShapeQueryable;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperBuilderContext; import org.elasticsearch.index.mapper.MapperBuilderContext;
@ -39,6 +38,7 @@ import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.mapper.MappingParserContext; import org.elasticsearch.index.mapper.MappingParserContext;
import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.QueryShardException;
import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.search.lookup.SearchLookup; import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.xpack.spatial.index.fielddata.plain.AbstractLatLonShapeIndexFieldData; import org.elasticsearch.xpack.spatial.index.fielddata.plain.AbstractLatLonShapeIndexFieldData;
import org.elasticsearch.xpack.spatial.search.aggregations.support.GeoShapeValuesSourceType; import org.elasticsearch.xpack.spatial.search.aggregations.support.GeoShapeValuesSourceType;
@ -280,7 +280,7 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
@Override @Override
protected void checkIncomingMergeType(FieldMapper mergeWith) { protected void checkIncomingMergeType(FieldMapper mergeWith) {
if (mergeWith instanceof LegacyGeoShapeFieldMapper) { if (mergeWith instanceof GeoShapeWithDocValuesFieldMapper == false && CONTENT_TYPE.equals(mergeWith.typeName())) {
throw new IllegalArgumentException("mapper [" + name() throw new IllegalArgumentException("mapper [" + name()
+ "] of type [geo_shape] cannot change strategy from [BKD] to [recursive]"); + "] of type [geo_shape] cannot change strategy from [BKD] to [recursive]");
} }

View file

@ -24,7 +24,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperParsingException;
@ -32,6 +31,7 @@ import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.MapperTestCase; import org.elasticsearch.index.mapper.MapperTestCase;
import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.SourceToParse;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.xpack.spatial.LocalStateSpatialPlugin; import org.elasticsearch.xpack.spatial.LocalStateSpatialPlugin;

View file

@ -18,8 +18,8 @@ import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Geometry; import org.elasticsearch.geometry.Geometry;
import org.elasticsearch.geometry.MultiPoint; import org.elasticsearch.geometry.MultiPoint;
import org.elasticsearch.geometry.Point; import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.search.geo.GeoShapeQueryTestCase; import org.elasticsearch.search.geo.GeoShapeQueryTestCase;
import org.elasticsearch.xpack.spatial.LocalStateSpatialPlugin; import org.elasticsearch.xpack.spatial.LocalStateSpatialPlugin;

View file

@ -100,6 +100,7 @@ subprojects {
// spatial dependency // spatial dependency
testRuntimeOnly project(path: xpackModule('spatial')) testRuntimeOnly project(path: xpackModule('spatial'))
testRuntimeOnly project(path: ':modules:legacy-geo')
testRuntimeOnly "org.slf4j:slf4j-api:1.7.25" testRuntimeOnly "org.slf4j:slf4j-api:1.7.25"
} }

View file

@ -30,6 +30,7 @@ dependencies {
compileOnly project(path: xpackModule('core')) compileOnly project(path: xpackModule('core'))
compileOnly project(path: xpackModule('spatial')) compileOnly project(path: xpackModule('spatial'))
testImplementation(testArtifact(project(xpackModule('core')))) testImplementation(testArtifact(project(xpackModule('core'))))
compileOnly "org.locationtech.jts:jts-core:${versions.jts}"
api "com.wdtinc:mapbox-vector-tile:3.1.0" api "com.wdtinc:mapbox-vector-tile:3.1.0"
api "com.google.protobuf:protobuf-java:3.14.0" api "com.google.protobuf:protobuf-java:3.14.0"
runtimeOnly("org.slf4j:slf4j-api:${versions.slf4j}") runtimeOnly("org.slf4j:slf4j-api:${versions.slf4j}")