Merge branch 'master' into feature/autoscaling

# Conflicts:
#	solr/CHANGES.txt
#	solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
#	solr/core/src/test/org/apache/solr/cloud/SharedFSAutoReplicaFailoverTest.java
#	solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
#	solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
This commit is contained in:
Shalin Shekhar Mangar 2017-08-08 17:15:46 +05:30
commit 1f6a94551f
273 changed files with 10281 additions and 2450 deletions

4
build.xml Normal file → Executable file
View File

@ -210,14 +210,14 @@
++lineNumber;
if (underSourceHeader) { // This line is either a single source line, or the boundary of a code block
inCodeBlock = blockBoundaryPattern.matcher(it).matches();
if ( ! blockTitlePattern.matcher(it).matches()) { // Keep underSourceHeader=true
if ( ! blockTitlePattern.matcher(it).matches()) {
underSourceHeader = false;
}
} else {
if (inCodeBlock) {
inCodeBlock = ! blockBoundaryPattern.matcher(it).matches();
} else {
underSourceHeader = sourceHeaderPattern.matcher(it).matches();
underSourceHeader = sourceHeaderPattern.matcher(it).lookingAt();
if ( ! underSourceHeader) {
def unescapedSymbolMatcher = unescapedSymbolPattern.matcher(it);
if (unescapedSymbolMatcher.find()) {

View File

@ -14,7 +14,6 @@ Changes in Runtime Behavior
======================= Lucene 7.1.0 =======================
(No Changes)
Optimizations
@ -22,6 +21,20 @@ Optimizations
SortedSetDocValuesFacetCounts and others) builds its map (Robert
Muir, Adrien Grand, Mike McCandless)
* LUCENE-7655: Speed up geo-distance queries in case of dense single-valued
fields when most documents match. (Maciej Zasada via Adrien Grand)
Bug Fixes
* LUCENE-7914: Add a maximum recursion level in automaton recursive
functions (Operations.isFinite and Operations.topsortState) to prevent
large automaton to overflow the stack (Robert Muir, Adrien Grand, Jim Ferenczi)
* LUCENE-7916: Prevent ArrayIndexOutOfBoundsException if ICUTokenizer is used
with a different ICU JAR version than it is compiled against. Note, this is
not recommended, lucene-analyzers-icu contains binary data structures
specific to ICU/Unicode versions it is built against. (Chris Koenig, Robert Muir)
======================= Lucene 7.0.0 =======================
New Features

View File

@ -61,6 +61,8 @@ import com.ibm.icu.text.Normalizer2;
* </p>
*/
public final class ICUFoldingFilter extends ICUNormalizer2Filter {
// TODO: if the wrong version of the ICU jar is used, loading these data files may give a strange error.
// maybe add an explicit check? http://icu-project.org/apiref/icu4j/com/ibm/icu/util/VersionInfo.html
private static final Normalizer2 normalizer = Normalizer2.getInstance(
ICUFoldingFilter.class.getResourceAsStream("utr30.nrm"),
"utr30", Normalizer2.Mode.COMPOSE);

View File

@ -17,6 +17,8 @@
package org.apache.lucene.analysis.icu.segmentation;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.lang.UScript;
import com.ibm.icu.text.BreakIterator;
@ -38,7 +40,7 @@ import com.ibm.icu.text.BreakIterator;
*/
final class CompositeBreakIterator {
private final ICUTokenizerConfig config;
private final BreakIteratorWrapper wordBreakers[] = new BreakIteratorWrapper[UScript.CODE_LIMIT];
private final BreakIteratorWrapper wordBreakers[] = new BreakIteratorWrapper[1 + UCharacter.getIntPropertyMaxValue(UProperty.SCRIPT)];
private BreakIteratorWrapper rbbi;
private final ScriptIterator scriptIterator;

View File

@ -60,6 +60,10 @@ public class DefaultICUTokenizerConfig extends ICUTokenizerConfig {
// we keep the cjk breaking separate, thats because it cannot be customized (because dictionary
// is only triggered when kind = WORD, but kind = LINE by default and we have no non-evil way to change it)
private static final BreakIterator cjkBreakIterator = BreakIterator.getWordInstance(ULocale.ROOT);
// TODO: if the wrong version of the ICU jar is used, loading these data files may give a strange error.
// maybe add an explicit check? http://icu-project.org/apiref/icu4j/com/ibm/icu/util/VersionInfo.html
// the same as ROOT, except no dictionary segmentation for cjk
private static final BreakIterator defaultBreakIterator =
readBreakIterator("Default.brk");

View File

@ -33,7 +33,6 @@ import org.apache.lucene.util.IOUtils;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.lang.UProperty;
import com.ibm.icu.lang.UScript;
import com.ibm.icu.text.BreakIterator;
import com.ibm.icu.text.RuleBasedBreakIterator;
@ -108,7 +107,7 @@ public class ICUTokenizerFactory extends TokenizerFactory implements ResourceLoa
if (tailored.isEmpty()) {
config = new DefaultICUTokenizerConfig(cjkAsWords, myanmarAsWords);
} else {
final BreakIterator breakers[] = new BreakIterator[UScript.CODE_LIMIT];
final BreakIterator breakers[] = new BreakIterator[1 + UCharacter.getIntPropertyMaxValue(UProperty.SCRIPT)];
for (Map.Entry<Integer,String> entry : tailored.entrySet()) {
int code = entry.getKey();
String resourcePath = entry.getValue();

View File

@ -212,8 +212,6 @@ final class DocumentsWriterPerThreadPool {
state.unlock();
synchronized (this) {
freeList.add(state);
// In case any thread is waiting, wake one of them up since we just released a thread state:
notify();
}
}

View File

@ -20,7 +20,6 @@ package org.apache.lucene.index;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@ -298,7 +297,7 @@ public class TieredMergePolicy extends MergePolicy {
// call size() once per segment and sort by that:
Map<SegmentCommitInfo,Long> sizeInBytes = getSegmentSizes(writer, infos.asList());
Collections.sort(infosSorted, new SegmentByteSizeDescending(sizeInBytes));
infosSorted.sort(new SegmentByteSizeDescending(sizeInBytes));
// Compute total index bytes & print details about the index
long totIndexBytes = 0;
@ -439,9 +438,7 @@ public class TieredMergePolicy extends MergePolicy {
}
final OneMerge merge = new OneMerge(best);
spec.add(merge);
for(SegmentCommitInfo info : merge.segments) {
toBeMerged.add(info);
}
toBeMerged.addAll(merge.segments);
if (verbose(writer)) {
message(" add merge=" + writer.segString(merge.segments) + " size=" + String.format(Locale.ROOT, "%.3f MB", bestMergeBytes/1024./1024.) + " score=" + String.format(Locale.ROOT, "%.3f", bestScore.getScore()) + " " + bestScore.getExplanation() + (bestTooLarge ? " [max merge]" : ""), writer);
@ -553,7 +550,7 @@ public class TieredMergePolicy extends MergePolicy {
return null;
}
Collections.sort(eligible, new SegmentByteSizeDescending(sizeInBytes));
eligible.sort(new SegmentByteSizeDescending(sizeInBytes));
if (verbose(writer)) {
message("eligible=" + eligible, writer);
@ -614,7 +611,7 @@ public class TieredMergePolicy extends MergePolicy {
// call size() once per segment and sort by that:
Map<SegmentCommitInfo,Long> sizeInBytes = getSegmentSizes(writer, infos.asList());
Collections.sort(eligible, new SegmentByteSizeDescending(sizeInBytes));
eligible.sort(new SegmentByteSizeDescending(sizeInBytes));
if (verbose(writer)) {
message("eligible=" + eligible, writer);

View File

@ -58,6 +58,11 @@ final public class Operations {
*/
public static final int DEFAULT_MAX_DETERMINIZED_STATES = 10000;
/**
* Maximum level of recursion allowed in recursive operations.
*/
public static final int MAX_RECURSION_LEVEL = 1000;
private Operations() {}
/**
@ -1018,7 +1023,7 @@ final public class Operations {
if (a.getNumStates() == 0) {
return true;
}
return isFinite(new Transition(), a, 0, new BitSet(a.getNumStates()), new BitSet(a.getNumStates()));
return isFinite(new Transition(), a, 0, new BitSet(a.getNumStates()), new BitSet(a.getNumStates()), 0);
}
/**
@ -1026,13 +1031,16 @@ final public class Operations {
* there are never transitions to dead states.)
*/
// TODO: not great that this is recursive... in theory a
// large automata could exceed java's stack
private static boolean isFinite(Transition scratch, Automaton a, int state, BitSet path, BitSet visited) {
// large automata could exceed java's stack so the maximum level of recursion is bounded to 1000
private static boolean isFinite(Transition scratch, Automaton a, int state, BitSet path, BitSet visited, int level) {
if (level > MAX_RECURSION_LEVEL) {
throw new IllegalArgumentException("input automaton is too large: " + level);
}
path.set(state);
int numTransitions = a.initTransition(state, scratch);
for(int t=0;t<numTransitions;t++) {
a.getTransition(state, t, scratch);
if (path.get(scratch.dest) || (!visited.get(scratch.dest) && !isFinite(scratch, a, scratch.dest, path, visited))) {
if (path.get(scratch.dest) || (!visited.get(scratch.dest) && !isFinite(scratch, a, scratch.dest, path, visited, level+1))) {
return false;
}
}
@ -1264,7 +1272,7 @@ final public class Operations {
int numStates = a.getNumStates();
int[] states = new int[numStates];
final BitSet visited = new BitSet(numStates);
int upto = topoSortStatesRecurse(a, visited, states, 0, 0);
int upto = topoSortStatesRecurse(a, visited, states, 0, 0, 0);
if (upto < states.length) {
// There were dead states
@ -1283,14 +1291,19 @@ final public class Operations {
return states;
}
private static int topoSortStatesRecurse(Automaton a, BitSet visited, int[] states, int upto, int state) {
// TODO: not great that this is recursive... in theory a
// large automata could exceed java's stack so the maximum level of recursion is bounded to 1000
private static int topoSortStatesRecurse(Automaton a, BitSet visited, int[] states, int upto, int state, int level) {
if (level > MAX_RECURSION_LEVEL) {
throw new IllegalArgumentException("input automaton is too large: " + level);
}
Transition t = new Transition();
int count = a.initTransition(state, t);
for (int i=0;i<count;i++) {
a.getNextTransition(t);
if (!visited.get(t.dest)) {
visited.set(t.dest);
upto = topoSortStatesRecurse(a, visited, states, upto, t.dest);
upto = topoSortStatesRecurse(a, visited, states, upto, t.dest, level+1);
}
}
states[upto] = state;

View File

@ -542,19 +542,21 @@ public class RegExp {
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
break;
case REGEXP_REPEAT_MIN:
a = Operations.repeat(
exp1.toAutomatonInternal(automata, automaton_provider,
maxDeterminizedStates),
min);
a = exp1.toAutomatonInternal(automata, automaton_provider, maxDeterminizedStates);
int minNumStates = (a.getNumStates() - 1) * min;
if (minNumStates > maxDeterminizedStates) {
throw new TooComplexToDeterminizeException(a, minNumStates);
}
a = Operations.repeat(a, min);
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
break;
case REGEXP_REPEAT_MINMAX:
a = Operations.repeat(
exp1.toAutomatonInternal(automata, automaton_provider,
maxDeterminizedStates),
min,
max);
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
a = exp1.toAutomatonInternal(automata, automaton_provider, maxDeterminizedStates);
int minMaxNumStates = (a.getNumStates() - 1) * max;
if (minMaxNumStates > maxDeterminizedStates) {
throw new TooComplexToDeterminizeException(a, minMaxNumStates);
}
a = Operations.repeat(a, min, max);
break;
case REGEXP_COMPLEMENT:
a = Operations.complement(

View File

@ -54,7 +54,7 @@ import org.apache.lucene.util.RamUsageEstimator;
/** Represents an finite state machine (FST), using a
* compact byte[] format.
* <p> The format is similar to what's used by Morfologik
* (http://sourceforge.net/projects/morfologik).
* (https://github.com/morfologik/morfologik-stemming).
*
* <p> See the {@link org.apache.lucene.util.fst package
* documentation} for some simple examples.

View File

@ -52,8 +52,7 @@ public class TestOperations extends LuceneTestCase {
for (BytesRef bref : strings) {
eachIndividual[i++] = Automata.makeString(bref.utf8ToString());
}
return Operations.determinize(Operations.union(Arrays.asList(eachIndividual)),
DEFAULT_MAX_DETERMINIZED_STATES);
return Operations.determinize(Operations.union(Arrays.asList(eachIndividual)), DEFAULT_MAX_DETERMINIZED_STATES);
}
/** Test concatenation with empty language returns empty */
@ -61,6 +60,7 @@ public class TestOperations extends LuceneTestCase {
Automaton a = Automata.makeString("a");
Automaton concat = Operations.concatenate(a, Automata.makeEmpty());
assertTrue(Operations.isEmpty(concat));
}
/** Test optimization to concatenate() with empty String to an NFA */
@ -124,6 +124,28 @@ public class TestOperations extends LuceneTestCase {
}
}
public void testIsFiniteEatsStack() {
char[] chars = new char[50000];
TestUtil.randomFixedLengthUnicodeString(random(), chars, 0, chars.length);
String bigString1 = new String(chars);
TestUtil.randomFixedLengthUnicodeString(random(), chars, 0, chars.length);
String bigString2 = new String(chars);
Automaton a = Operations.union(Automata.makeString(bigString1), Automata.makeString(bigString2));
IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> Operations.isFinite(a));
assertTrue(exc.getMessage().contains("input automaton is too large"));
}
public void testTopoSortEatsStack() {
char[] chars = new char[50000];
TestUtil.randomFixedLengthUnicodeString(random(), chars, 0, chars.length);
String bigString1 = new String(chars);
TestUtil.randomFixedLengthUnicodeString(random(), chars, 0, chars.length);
String bigString2 = new String(chars);
Automaton a = Operations.union(Automata.makeString(bigString1), Automata.makeString(bigString2));
IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> Operations.topoSortStates(a));
assertTrue(exc.getMessage().contains("input automaton is too large"));
}
/**
* Returns the set of all accepted strings.
*

View File

@ -19,13 +19,6 @@ package org.apache.lucene.util.automaton;
import org.apache.lucene.util.LuceneTestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
public class TestRegExp extends LuceneTestCase {
/**
@ -54,6 +47,14 @@ public class TestRegExp extends LuceneTestCase {
assertTrue(expected.getMessage().contains(source));
}
public void testSerializeTooManyStatesToRepeat() throws Exception {
String source = "a{50001}";
TooComplexToDeterminizeException expected = expectThrows(TooComplexToDeterminizeException.class, () -> {
new RegExp(source).toAutomaton(50000);
});
assertTrue(expected.getMessage().contains(source));
}
// LUCENE-6713
public void testSerializeTooManyStatesToDeterminizeExc() throws Exception {
// LUCENE-6046
@ -62,16 +63,6 @@ public class TestRegExp extends LuceneTestCase {
new RegExp(source).toAutomaton();
});
assertTrue(expected.getMessage().contains(source));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(expected);
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInput in = new ObjectInputStream(bis);
TooComplexToDeterminizeException e2 = (TooComplexToDeterminizeException) in.readObject();
assertNotNull(e2.getMessage());
}
// LUCENE-6046

View File

@ -84,15 +84,16 @@ io.netty.netty-all.version = 4.0.36.Final
/org.apache.ant/ant = 1.8.2
org.apache.calcite.avatica.version = 1.9.0
org.apache.calcite.avatica.version = 1.10.0
/org.apache.calcite.avatica/avatica-core = ${org.apache.calcite.avatica.version}
org.apache.calcite.version = 1.11.0
org.apache.calcite.version = 1.13.0
/org.apache.calcite/calcite-core = ${org.apache.calcite.version}
/org.apache.calcite/calcite-linq4j = ${org.apache.calcite.version}
/org.apache.commons/commons-compress = 1.11
/org.apache.commons/commons-exec = 1.3
/org.apache.commons/commons-lang3 = 3.6
/org.apache.commons/commons-math3 = 3.4.1
org.apache.curator.version = 2.8.0

View File

@ -29,12 +29,15 @@ import org.apache.lucene.index.PointValues.IntersectVisitor;
import org.apache.lucene.index.PointValues.Relation;
import org.apache.lucene.search.ConstantScoreScorer;
import org.apache.lucene.search.ConstantScoreWeight;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.ScorerSupplier;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.BitSetIterator;
import org.apache.lucene.util.DocIdSetBuilder;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.util.StringHelper;
@ -128,75 +131,11 @@ final class LatLonPointDistanceQuery extends Query {
return null;
}
LatLonPoint.checkCompatible(fieldInfo);
// matching docids
DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
final IntersectVisitor visitor =
new IntersectVisitor() {
final IntersectVisitor visitor = getIntersectVisitor(result);
DocIdSetBuilder.BulkAdder adder;
@Override
public void grow(int count) {
adder = result.grow(count);
}
@Override
public void visit(int docID) {
adder.add(docID);
}
@Override
public void visit(int docID, byte[] packedValue) {
// bounding box check
if (StringHelper.compare(Integer.BYTES, packedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
return;
}
if ((StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon2, 0) < 0) {
// longitude out of bounding box range
return;
}
int docLatitude = NumericUtils.sortableBytesToInt(packedValue, 0);
int docLongitude = NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES);
if (distancePredicate.test(docLatitude, docLongitude)) {
adder.add(docID);
}
}
// algorithm: we create a bounding box (two bounding boxes if we cross the dateline).
// 1. check our bounding box(es) first. if the subtree is entirely outside of those, bail.
// 2. check if the subtree is disjoint. it may cross the bounding box but not intersect with circle
// 3. see if the subtree is fully contained. if the subtree is enormous along the x axis, wrapping half way around the world, etc: then this can't work, just go to step 4.
// 4. recurse naively (subtrees crossing over circle edge)
@Override
public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
if (StringHelper.compare(Integer.BYTES, minPackedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
return Relation.CELL_OUTSIDE_QUERY;
}
if ((StringHelper.compare(Integer.BYTES, minPackedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon2, 0) < 0) {
// longitude out of bounding box range
return Relation.CELL_OUTSIDE_QUERY;
}
double latMin = decodeLatitude(minPackedValue, 0);
double lonMin = decodeLongitude(minPackedValue, Integer.BYTES);
double latMax = decodeLatitude(maxPackedValue, 0);
double lonMax = decodeLongitude(maxPackedValue, Integer.BYTES);
return GeoUtils.relate(latMin, latMax, lonMin, lonMax, latitude, longitude, sortKey, axisLat);
}
};
final Weight weight = this;
return new ScorerSupplier() {
@ -204,6 +143,19 @@ final class LatLonPointDistanceQuery extends Query {
@Override
public Scorer get(boolean randomAccess) throws IOException {
if (values.getDocCount() == reader.maxDoc()
&& values.getDocCount() == values.size()
&& cost() > reader.maxDoc() / 2) {
// If all docs have exactly one value and the cost is greater
// than half the leaf size then maybe we can make things faster
// by computing the set of documents that do NOT match the range
final FixedBitSet result = new FixedBitSet(reader.maxDoc());
result.set(0, reader.maxDoc());
int[] cost = new int[]{reader.maxDoc()};
values.intersect(getInverseIntersectVisitor(result, cost));
final DocIdSetIterator iterator = new BitSetIterator(result, cost[0]);
return new ConstantScoreScorer(weight, score(), iterator);
}
values.intersect(visitor);
return new ConstantScoreScorer(weight, score(), result.build().iterator());
}
@ -219,6 +171,154 @@ final class LatLonPointDistanceQuery extends Query {
};
}
/**
* Create a visitor that collects documents matching the range.
*/
private IntersectVisitor getIntersectVisitor(DocIdSetBuilder result) {
return new IntersectVisitor() {
DocIdSetBuilder.BulkAdder adder;
@Override
public void grow(int count) {
adder = result.grow(count);
}
@Override
public void visit(int docID) {
adder.add(docID);
}
@Override
public void visit(int docID, byte[] packedValue) {
// bounding box check
if (StringHelper.compare(Integer.BYTES, packedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
return;
}
if ((StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon2, 0) < 0) {
// longitude out of bounding box range
return;
}
int docLatitude = NumericUtils.sortableBytesToInt(packedValue, 0);
int docLongitude = NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES);
if (distancePredicate.test(docLatitude, docLongitude)) {
adder.add(docID);
}
}
// algorithm: we create a bounding box (two bounding boxes if we cross the dateline).
// 1. check our bounding box(es) first. if the subtree is entirely outside of those, bail.
// 2. check if the subtree is disjoint. it may cross the bounding box but not intersect with circle
// 3. see if the subtree is fully contained. if the subtree is enormous along the x axis, wrapping half way around the world, etc: then this can't work, just go to step 4.
// 4. recurse naively (subtrees crossing over circle edge)
@Override
public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
if (StringHelper.compare(Integer.BYTES, minPackedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
return Relation.CELL_OUTSIDE_QUERY;
}
if ((StringHelper.compare(Integer.BYTES, minPackedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon2, 0) < 0) {
// longitude out of bounding box range
return Relation.CELL_OUTSIDE_QUERY;
}
double latMin = decodeLatitude(minPackedValue, 0);
double lonMin = decodeLongitude(minPackedValue, Integer.BYTES);
double latMax = decodeLatitude(maxPackedValue, 0);
double lonMax = decodeLongitude(maxPackedValue, Integer.BYTES);
return GeoUtils.relate(latMin, latMax, lonMin, lonMax, latitude, longitude, sortKey, axisLat);
}
};
}
/**
* Create a visitor that clears documents that do NOT match the range.
*/
private IntersectVisitor getInverseIntersectVisitor(FixedBitSet result, int[] cost) {
return new IntersectVisitor() {
@Override
public void visit(int docID) {
result.clear(docID);
cost[0]--;
}
@Override
public void visit(int docID, byte[] packedValue) {
// bounding box check
if (StringHelper.compare(Integer.BYTES, packedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
result.clear(docID);
cost[0]--;
return;
}
if ((StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon2, 0) < 0) {
// longitude out of bounding box range
result.clear(docID);
cost[0]--;
return;
}
int docLatitude = NumericUtils.sortableBytesToInt(packedValue, 0);
int docLongitude = NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES);
if (!distancePredicate.test(docLatitude, docLongitude)) {
result.clear(docID);
cost[0]--;
}
}
@Override
public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
if (StringHelper.compare(Integer.BYTES, minPackedValue, 0, maxLat, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, 0, minLat, 0) < 0) {
// latitude out of bounding box range
return Relation.CELL_INSIDE_QUERY;
}
if ((StringHelper.compare(Integer.BYTES, minPackedValue, Integer.BYTES, maxLon, 0) > 0 ||
StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon, 0) < 0)
&& StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon2, 0) < 0) {
// latitude out of bounding box range
return Relation.CELL_INSIDE_QUERY;
}
double latMin = decodeLatitude(minPackedValue, 0);
double lonMin = decodeLongitude(minPackedValue, Integer.BYTES);
double latMax = decodeLatitude(maxPackedValue, 0);
double lonMax = decodeLongitude(maxPackedValue, Integer.BYTES);
Relation relation = GeoUtils.relate(latMin, latMax, lonMin, lonMax, latitude, longitude, sortKey, axisLat);
switch (relation) {
case CELL_INSIDE_QUERY:
// all points match, skip this subtree
return Relation.CELL_OUTSIDE_QUERY;
case CELL_OUTSIDE_QUERY:
// none of the points match, clear all documents
return Relation.CELL_INSIDE_QUERY;
default:
return relation;
}
}
};
}
};
}

View File

@ -16,11 +16,18 @@
*/
package org.apache.lucene.search;
import java.io.IOException;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.LatLonPoint;
import org.apache.lucene.geo.BaseGeoPointTestCase;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.geo.GeoEncodingUtils;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.bkd.BKDWriter;
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
@ -53,4 +60,32 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
protected double quantizeLon(double lonRaw) {
return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lonRaw));
}
public void testDistanceQueryWithInvertedIntersection() throws IOException {
final int numMatchingDocs = atLeast(10 * BKDWriter.DEFAULT_MAX_POINTS_IN_LEAF_NODE);
try (Directory dir = newDirectory()) {
try (IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
for (int i = 0; i < numMatchingDocs; ++i) {
Document doc = new Document();
addPointToDoc("field", doc, 18.313694, -65.227444);
w.addDocument(doc);
}
// Add a handful of docs that don't match
for (int i = 0; i < 11; ++i) {
Document doc = new Document();
addPointToDoc("field", doc, 10, -65.227444);
w.addDocument(doc);
}
w.forceMerge(1);
}
try (IndexReader r = DirectoryReader.open(dir)) {
IndexSearcher searcher = newSearcher(r);
assertEquals(numMatchingDocs, searcher.count(newDistanceQuery("field", 18, -65, 50_000)));
}
}
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
/**
* Shape that implements GeoArea. This type of shapes are able to resolve the
* spatial relationship of other shapes with itself.
*
* @lucene.experimental
*/
public interface GeoAreaShape extends GeoMembershipShape, GeoArea{
/**
* Assess whether a shape intersects with any of the edges this shape.
* Note well that this method return false if the shape contains, is within
* or is disjoint with the given shape.
*
* @param geoShape is the shape to assess for intersection with this shape's edges.
*
* @return true if there's such an intersection, false if not.
*/
boolean intersects(GeoShape geoShape);
}

View File

@ -23,7 +23,7 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.experimental
*/
public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea {
public interface GeoBBox extends GeoAreaShape, GeoSizeable {
/**
* Expand box by specified angle.

View File

@ -0,0 +1,125 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
/**
* Base extended areaShape object.
*
* @lucene.internal
*/
abstract class GeoBaseAreaShape extends GeoBaseMembershipShape implements GeoAreaShape {
/** Constructor.
*@param planetModel is the planet model to use.
*/
public GeoBaseAreaShape(final PlanetModel planetModel) {
super(planetModel);
}
/** All edgepoints inside shape */
protected final static int ALL_INSIDE = 0;
/** Some edgepoints inside shape */
protected final static int SOME_INSIDE = 1;
/** No edgepoints inside shape */
protected final static int NONE_INSIDE = 2;
/** Determine the relationship between the GeoAreShape and the
* shape's edgepoints.
*@param geoShape is the shape.
*@return the relationship.
*/
protected int isShapeInsideGeoAreaShape(final GeoShape geoShape) {
boolean foundOutside = false;
boolean foundInside = false;
for (GeoPoint p : geoShape.getEdgePoints()) {
if (isWithin(p)) {
foundInside = true;
} else {
foundOutside = true;
}
if (foundInside && foundOutside) {
return SOME_INSIDE;
}
}
if (!foundInside && !foundOutside)
return NONE_INSIDE;
if (foundInside && !foundOutside)
return ALL_INSIDE;
if (foundOutside && !foundInside)
return NONE_INSIDE;
return SOME_INSIDE;
}
/** Determine the relationship between the GeoAreaShape's edgepoints and the
* provided shape.
*@param geoshape is the shape.
*@return the relationship.
*/
protected int isGeoAreaShapeInsideShape(final GeoShape geoshape) {
boolean foundOutside = false;
boolean foundInside = false;
for (GeoPoint p : getEdgePoints()) {
if (geoshape.isWithin(p)) {
foundInside = true;
} else {
foundOutside = true;
}
if (foundInside && foundOutside) {
return SOME_INSIDE;
}
}
if (!foundInside && !foundOutside)
return NONE_INSIDE;
if (foundInside && !foundOutside)
return ALL_INSIDE;
if (foundOutside && !foundInside)
return NONE_INSIDE;
return SOME_INSIDE;
}
@Override
public int getRelationship(GeoShape geoShape) {
final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape);
if (insideGeoAreaShape == SOME_INSIDE) {
return GeoArea.OVERLAPS;
}
final int insideShape = isGeoAreaShapeInsideShape(geoShape);
if (insideShape == SOME_INSIDE) {
return GeoArea.OVERLAPS;
}
if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) {
return GeoArea.OVERLAPS;
}
if (intersects(geoShape)){
return GeoArea.OVERLAPS;
}
if (insideGeoAreaShape == ALL_INSIDE) {
return GeoArea.WITHIN;
}
if (insideShape==ALL_INSIDE) {
return GeoArea.CONTAINS;
}
return GeoArea.DISJOINT;
}
}

View File

@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.internal
*/
abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
abstract class GeoBaseBBox extends GeoBaseAreaShape implements GeoBBox {
/** Construct, given planet model.
*@param planetModel is the planet model.
@ -31,42 +31,6 @@ abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
super(planetModel);
}
// Signals for relationship of edge points to shape
/** All edgepoints inside shape */
protected final static int ALL_INSIDE = 0;
/** Some edgepoints inside shape */
protected final static int SOME_INSIDE = 1;
/** No edgepoints inside shape */
protected final static int NONE_INSIDE = 2;
/** Determine the relationship between this BBox and the provided
* shape's edgepoints.
*@param path is the shape.
*@return the relationship.
*/
protected int isShapeInsideBBox(final GeoShape path) {
final GeoPoint[] pathPoints = path.getEdgePoints();
boolean foundOutside = false;
boolean foundInside = false;
for (GeoPoint p : pathPoints) {
if (isWithin(p)) {
foundInside = true;
} else {
foundOutside = true;
}
if (foundInside && foundOutside) {
return SOME_INSIDE;
}
}
if (!foundInside && !foundOutside)
return NONE_INSIDE;
if (foundInside && !foundOutside)
return ALL_INSIDE;
if (foundOutside && !foundInside)
return NONE_INSIDE;
return SOME_INSIDE;
}
}

View File

@ -0,0 +1,135 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
/**
* Base class to create a composite of GeoAreaShapes
*
* @param <T> is the type of GeoAreaShapes of the composite.
* @lucene.internal
*/
abstract class GeoBaseCompositeAreaShape<T extends GeoAreaShape> extends GeoBaseCompositeMembershipShape<T> implements GeoAreaShape {
/** All edgepoints inside shape */
protected final static int ALL_INSIDE = 0;
/** Some edgepoints inside shape */
protected final static int SOME_INSIDE = 1;
/** No edgepoints inside shape */
protected final static int NONE_INSIDE = 2;
/**
* Constructor.
*/
public GeoBaseCompositeAreaShape() {
}
@Override
public boolean intersects(GeoShape geoShape){
for(GeoAreaShape geoAreaShape : shapes){
if (geoAreaShape.intersects(geoShape)){
return true;
}
}
return false;
}
@Override
public int getRelationship(GeoShape geoShape) {
final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape);
if (insideGeoAreaShape == SOME_INSIDE) {
return GeoArea.OVERLAPS;
}
final int insideShape = isGeoAreaShapeInsideShape(geoShape);
if (insideShape == SOME_INSIDE) {
return GeoArea.OVERLAPS;
}
if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) {
return GeoArea.OVERLAPS;
}
if (intersects(geoShape)){
return GeoArea.OVERLAPS;
}
if (insideGeoAreaShape == ALL_INSIDE) {
return GeoArea.WITHIN;
}
if (insideShape==ALL_INSIDE) {
return GeoArea.CONTAINS;
}
return GeoArea.DISJOINT;
}
/** Determine the relationship between the GeoAreShape and the
* shape's edgepoints.
*@param geoShape is the shape.
*@return the relationship.
*/
protected int isShapeInsideGeoAreaShape(final GeoShape geoShape) {
boolean foundOutside = false;
boolean foundInside = false;
for (GeoPoint p : geoShape.getEdgePoints()) {
if (isWithin(p)) {
foundInside = true;
} else {
foundOutside = true;
}
if (foundInside && foundOutside) {
return SOME_INSIDE;
}
}
if (!foundInside && !foundOutside)
return NONE_INSIDE;
if (foundInside && !foundOutside)
return ALL_INSIDE;
if (foundOutside && !foundInside)
return NONE_INSIDE;
return SOME_INSIDE;
}
/** Determine the relationship between the GeoAreShape's edgepoints and the
* provided shape.
*@param geoshape is the shape.
*@return the relationship.
*/
protected int isGeoAreaShapeInsideShape(final GeoShape geoshape) {
boolean foundOutside = false;
boolean foundInside = false;
for (GeoPoint p : getEdgePoints()) {
if (geoshape.isWithin(p)) {
foundInside = true;
} else {
foundOutside = true;
}
if (foundInside && foundOutside) {
return SOME_INSIDE;
}
}
if (!foundInside && !foundOutside)
return NONE_INSIDE;
if (foundInside && !foundOutside)
return ALL_INSIDE;
if (foundOutside && !foundInside)
return NONE_INSIDE;
return SOME_INSIDE;
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
/**
* Base class to create a composite of GeoMembershipShapes
*
* @param <T> is the type of GeoMembershipShapes of the composite.
* @lucene.internal
*/
abstract class GeoBaseCompositeMembershipShape<T extends GeoMembershipShape>
extends GeoBaseCompositeShape<T> implements GeoMembershipShape{
/**
* Constructor.
*/
GeoBaseCompositeMembershipShape() {
}
@Override
public double computeOutsideDistance(final DistanceStyle distanceStyle, final GeoPoint point) {
return computeOutsideDistance(distanceStyle, point.x, point.y, point.z);
}
@Override
public double computeOutsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
if (isWithin(x,y,z))
return 0.0;
double distance = Double.POSITIVE_INFINITY;
for (GeoMembershipShape shape : shapes) {
final double normalDistance = shape.computeOutsideDistance(distanceStyle, x, y, z);
if (normalDistance < distance) {
distance = normalDistance;
}
}
return distance;
}
}

View File

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Base class to create a composite of GeoShapes.
*
* @param <T> is the type of GeoShapes of the composite.
* @lucene.experimental
*/
public abstract class GeoBaseCompositeShape<T extends GeoShape> implements GeoShape {
/**
* Shape's container
*/
protected final List<T> shapes = new ArrayList<>();
/**
* Constructor.
*/
public GeoBaseCompositeShape() {
}
/**
* Add a shape to the composite.
*
* @param shape is the shape to add.
*/
public void addShape(final T shape) {
shapes.add(shape);
}
/**
* Get the number of shapes in the composite
*
* @return the number of shapes
*/
public int size() {
return shapes.size();
}
/**
* Get shape at index
*
* @return the shape at given index
*/
public T getShape(int index) {
return shapes.get(index);
}
@Override
public boolean isWithin(final Vector point) {
return isWithin(point.x, point.y, point.z);
}
@Override
public boolean isWithin(final double x, final double y, final double z) {
for (GeoShape shape : shapes) {
if (shape.isWithin(x, y, z))
return true;
}
return false;
}
@Override
public GeoPoint[] getEdgePoints() {
List<GeoPoint> edgePoints = new ArrayList<>();
for (GeoShape shape : shapes) {
edgePoints.addAll(Arrays.asList(shape.getEdgePoints()));
}
return edgePoints.toArray(new GeoPoint[edgePoints.size()]);
}
@Override
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
for (GeoShape shape : shapes) {
if (shape.intersects(p, notablePoints, bounds))
return true;
}
return false;
}
@Override
public void getBounds(Bounds bounds) {
for (GeoShape shape : shapes) {
shape.getBounds(bounds);
}
}
@Override
public int hashCode() {
return super.hashCode() * 31 + shapes.hashCode();//TODO cache
}
@Override
public boolean equals(Object o) {
if (!(o instanceof GeoBaseCompositeShape<?>))
return false;
GeoBaseCompositeShape<?> other = (GeoBaseCompositeShape<?>) o;
return super.equals(o) && shapes.equals(other.shapes);
}
}

View File

@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.experimental
*/
public abstract class GeoBaseDistanceShape extends GeoBaseMembershipShape implements GeoDistanceShape {
public abstract class GeoBaseDistanceShape extends GeoBaseAreaShape implements GeoDistanceShape {
/** Constructor.
*@param planetModel is the planet model to use.

View File

@ -21,7 +21,7 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.internal
*/
abstract class GeoBasePolygon extends GeoBaseMembershipShape implements GeoPolygon {
abstract class GeoBasePolygon extends GeoBaseAreaShape implements GeoPolygon {
/** Constructor.
*@param planetModel is the planet model to use.

View File

@ -369,6 +369,32 @@ class GeoComplexPolygon extends GeoBasePolygon {
return true;
}
@Override
public boolean intersects(GeoShape geoShape) {
// Create the intersector
final EdgeIterator intersector = new IntersectorShapeIterator(geoShape);
// First, compute the bounds for the the plane
final XYZBounds xyzBounds = new XYZBounds();
geoShape.getBounds(xyzBounds);
// Figure out which tree likely works best
final double xDelta = xyzBounds.getMaximumX() - xyzBounds.getMinimumX();
final double yDelta = xyzBounds.getMaximumY() - xyzBounds.getMinimumY();
final double zDelta = xyzBounds.getMaximumZ() - xyzBounds.getMinimumZ();
// Select the smallest range
if (xDelta <= yDelta && xDelta <= zDelta) {
// Drill down in x
return !xTree.traverse(intersector, xyzBounds.getMinimumX(), xyzBounds.getMaximumX());
} else if (yDelta <= xDelta && yDelta <= zDelta) {
// Drill down in y
return !yTree.traverse(intersector, xyzBounds.getMinimumY(), xyzBounds.getMaximumY());
} else if (zDelta <= xDelta && zDelta <= yDelta) {
// Drill down in z
return !zTree.traverse(intersector, xyzBounds.getMinimumZ(), xyzBounds.getMaximumZ());
}
return true;
}
@Override
public void getBounds(Bounds bounds) {
@ -492,7 +518,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
if (left != null && left.traverse(edgeIterator, minValue, maxValue) == false) {
return false;
}
if (right != null && minValue >= low && right.traverse(edgeIterator, minValue, maxValue) == false) {
if (right != null && maxValue >= low && right.traverse(edgeIterator, minValue, maxValue) == false) {
return false;
}
}
@ -691,7 +717,24 @@ class GeoComplexPolygon extends GeoBasePolygon {
}
}
/** Assess whether edge intersects the provided shape.
*/
private class IntersectorShapeIterator implements EdgeIterator {
private final GeoShape shape;
public IntersectorShapeIterator(final GeoShape shape) {
this.shape = shape;
}
@Override
public boolean matches(final Edge edge) {
return !shape.intersects(edge.plane, edge.notablePoints, edge.startPlane, edge.endPlane);
}
}
/** Count the number of verifiable edge crossings.
*/
private class LinearCrossingEdgeIterator implements EdgeIterator {

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
/**
* GeoCompositeAreaShape is a set of GeoAreaShape's, treated as a unit.
*
* @lucene.experimental
*/
public class GeoCompositeAreaShape extends GeoBaseCompositeAreaShape<GeoAreaShape> {
/**
* Constructor.
*/
public GeoCompositeAreaShape() {
}
@Override
public String toString() {
return "GeoCompositeAreaShape: {" + shapes + '}';
}
}

View File

@ -16,97 +16,17 @@
*/
package org.apache.lucene.spatial3d.geom;
import java.util.ArrayList;
import java.util.List;
/**
* GeoComposite is a set of GeoMembershipShape's, treated as a unit.
* GeoCompositeMembershipShape is a set of GeoMembershipShape's, treated as a unit.
*
* @lucene.experimental
*/
public class GeoCompositeMembershipShape implements GeoMembershipShape {
/** The list of shapes. */
protected final List<GeoMembershipShape> shapes = new ArrayList<GeoMembershipShape>();
/** Constructor.
*/
public GeoCompositeMembershipShape() {
}
public class GeoCompositeMembershipShape extends GeoBaseCompositeMembershipShape<GeoMembershipShape> implements GeoMembershipShape {
/**
* Add a shape to the composite.
*@param shape is the shape to add.
* Constructor.
*/
public void addShape(final GeoMembershipShape shape) {
shapes.add(shape);
}
@Override
public boolean isWithin(final Vector point) {
return isWithin(point.x, point.y, point.z);
}
@Override
public boolean isWithin(final double x, final double y, final double z) {
for (GeoMembershipShape shape : shapes) {
if (shape.isWithin(x, y, z))
return true;
}
return false;
}
@Override
public GeoPoint[] getEdgePoints() {
return shapes.get(0).getEdgePoints();
}
@Override
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
for (GeoMembershipShape shape : shapes) {
if (shape.intersects(p, notablePoints, bounds))
return true;
}
return false;
}
@Override
public void getBounds(Bounds bounds) {
for (GeoMembershipShape shape : shapes) {
shape.getBounds(bounds);
}
}
@Override
public double computeOutsideDistance(final DistanceStyle distanceStyle, final GeoPoint point) {
return computeOutsideDistance(distanceStyle, point.x, point.y, point.z);
}
@Override
public double computeOutsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
if (isWithin(x,y,z))
return 0.0;
double distance = Double.POSITIVE_INFINITY;
for (GeoMembershipShape shape : shapes) {
final double normalDistance = shape.computeOutsideDistance(distanceStyle, x, y, z);
if (normalDistance < distance) {
distance = normalDistance;
}
}
return distance;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof GeoCompositeMembershipShape))
return false;
GeoCompositeMembershipShape other = (GeoCompositeMembershipShape) o;
return super.equals(o) && shapes.equals(other.shapes);
}
@Override
public int hashCode() {
return super.hashCode() * 31 + shapes.hashCode();//TODO cache
public GeoCompositeMembershipShape() {
}
@Override

View File

@ -17,15 +17,20 @@
package org.apache.lucene.spatial3d.geom;
/**
* GeoCompositePolygon is a specific implementation of GeoMembershipShape, which implements GeoPolygon explicitly.
* GeoCompositePolygon is a specific implementation of GeoCompositeAreaShape, which implements GeoPolygon explicitly.
*
* @lucene.experimental
*/
public class GeoCompositePolygon extends GeoCompositeMembershipShape implements GeoPolygon {
/** Constructor.
public class GeoCompositePolygon extends GeoBaseCompositeAreaShape<GeoPolygon> implements GeoPolygon {
/**
* Constructor.
*/
public GeoCompositePolygon() {
}
@Override
public String toString() {
return "GeoCompositePolygon: {" + shapes + '}';
}
}

View File

@ -366,6 +366,27 @@ class GeoConcavePolygon extends GeoBasePolygon {
return false;
}
@Override
public boolean intersects(GeoShape geoShape) {
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
final SidedPlane edge = edges[edgeIndex];
final GeoPoint[] points = this.notableEdgePoints[edgeIndex];
if (!isInternalEdges.get(edgeIndex)) {
if (geoShape.intersects(edge, points, eitherBounds.get(edge))) {
return true;
}
}
}
if (holes != null) {
for (final GeoPolygon hole : holes) {
if (hole.intersects(geoShape)) {
return true;
}
}
}
return false;
}
/** A membership implementation representing polygon edges that must apply.
*/
protected static class EitherBound implements Membership {

View File

@ -356,6 +356,27 @@ class GeoConvexPolygon extends GeoBasePolygon {
return false;
}
@Override
public boolean intersects(GeoShape shape) {
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
final SidedPlane edge = edges[edgeIndex];
final GeoPoint[] points = this.notableEdgePoints[edgeIndex];
if (!isInternalEdges.get(edgeIndex)) {
if (shape.intersects(edge, points, eitherBounds.get(edge))) {
return true;
}
}
}
if (holes != null) {
for (final GeoPolygon hole : holes) {
if (hole.intersects(shape)) {
return true;
}
}
}
return false;
}
/** A membership implementation representing polygon edges that must apply.
*/
protected static class EitherBound implements Membership {

View File

@ -154,6 +154,11 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, leftPlane, rightPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(plane, planePoints, leftPlane, rightPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -164,7 +169,7 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
@Override
public int getRelationship(final GeoShape path) {
//System.err.println("getting relationship between "+this+" and "+path);
if (path.intersects(plane, planePoints, leftPlane, rightPlane)) {
if (intersects(path)) {
//System.err.println(" overlaps");
return OVERLAPS;
}

View File

@ -85,6 +85,11 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(plane, planePoints);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -98,7 +103,7 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
// work with no area endpoints. So we rely entirely on intersections.
//System.out.println("Got here! latitude="+latitude+" path="+path);
if (path.intersects(plane, planePoints)) {
if (intersects(path)) {
return OVERLAPS;
}

View File

@ -96,6 +96,11 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(plane, planePoints, boundingPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -107,7 +112,7 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
@Override
public int getRelationship(final GeoShape path) {
// Look for intersections.
if (path.intersects(plane, planePoints, boundingPlane))
if (intersects(path))
return OVERLAPS;
if (path.isWithin(interiorPoint))

View File

@ -66,6 +66,11 @@ class GeoDegeneratePoint extends GeoPoint implements GeoBBox, GeoCircle {
return true;
}
@Override
public boolean intersects(GeoShape geoShape) {
return false;
}
@Override
public void getBounds(Bounds bounds) {
bounds.addPoint(this);

View File

@ -144,6 +144,11 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane, topPlane, bottomPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -154,7 +159,7 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" relationship to "+path);
if (path.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane)) {
if (intersects(path)) {
//System.err.println(" overlaps");
return OVERLAPS;
}

View File

@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.experimental
*/
public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance {
public interface GeoDistanceShape extends GeoAreaShape, GeoDistance {
/**
* Compute a bound based on a provided distance measure.

View File

@ -119,6 +119,12 @@ class GeoLatitudeZone extends GeoBaseBBox {
p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds, topPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(topPlane, planePoints, bottomPlane) ||
geoShape.intersects(bottomPlane, planePoints, topPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -127,46 +133,6 @@ class GeoLatitudeZone extends GeoBaseBBox {
.addHorizontalPlane(planetModel, bottomLat, bottomPlane);
}
@Override
public int getRelationship(final GeoShape path) {
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE)
return OVERLAPS;
final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint);
final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint);
if (topBoundaryInsideShape && !bottomBoundaryInsideShape ||
!topBoundaryInsideShape && bottomBoundaryInsideShape)
return OVERLAPS;
final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape;
if (insideRectangle == ALL_INSIDE && insideShape)
return OVERLAPS;
// Second, the shortcut of seeing whether endpoints are in/out is not going to
// work with no area endpoints. So we rely entirely on intersections.
if (path.intersects(topPlane, planePoints, bottomPlane) ||
path.intersects(bottomPlane, planePoints, topPlane))
return OVERLAPS;
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
// within the zone, but the shape includes areas outside the zone crossing a pole.
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
// one such point is within, then OVERLAPS is the right answer.
if (insideShape)
return CONTAINS;
if (insideRectangle == ALL_INSIDE)
return WITHIN;
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane);

View File

@ -127,6 +127,12 @@ class GeoLongitudeSlice extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds, leftPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(leftPlane, planePoints, rightPlane) ||
geoShape.intersects(rightPlane, planePoints, leftPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -138,33 +144,6 @@ class GeoLongitudeSlice extends GeoBaseBBox {
.addPoint(planetModel.SOUTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE)
return OVERLAPS;
final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape)
return OVERLAPS;
if (path.intersects(leftPlane, planePoints, rightPlane) ||
path.intersects(rightPlane, planePoints, leftPlane)) {
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
return WITHIN;
}
if (insideShape) {
return CONTAINS;
}
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane);

View File

@ -98,6 +98,12 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return
geoShape.intersects(bottomPlane, planePoints);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -105,38 +111,6 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
.addHorizontalPlane(planetModel, bottomLat, bottomPlane);
}
@Override
public int getRelationship(final GeoShape path) {
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE)
return OVERLAPS;
final boolean insideShape = path.isWithin(bottomBoundaryPoint);
if (insideRectangle == ALL_INSIDE && insideShape)
return OVERLAPS;
// Second, the shortcut of seeing whether endpoints are in/out is not going to
// work with no area endpoints. So we rely entirely on intersections.
if (path.intersects(bottomPlane, planePoints))
return OVERLAPS;
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
// within the zone, but the shape includes areas outside the zone crossing a pole.
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
// one such point is within, then OVERLAPS is the right answer.
if (insideShape)
return CONTAINS;
if (insideRectangle == ALL_INSIDE)
return WITHIN;
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
return distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z);

View File

@ -174,6 +174,14 @@ class GeoNorthRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, bottomPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return
geoShape.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) ||
geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, bottomPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -185,43 +193,6 @@ class GeoNorthRectangle extends GeoBaseBBox {
.addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" getrelationship with "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" inside of each other");
return OVERLAPS;
}
if (
path.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) ||
path.intersects(leftPlane, leftPlanePoints, bottomPlane, rightPlane) ||
path.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" shape contains rectangle");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, leftPlane, rightPlane);

View File

@ -21,6 +21,6 @@ package org.apache.lucene.spatial3d.geom;
*
* @lucene.experimental
*/
public interface GeoPolygon extends GeoMembershipShape {
public interface GeoPolygon extends GeoAreaShape {
}

View File

@ -196,6 +196,14 @@ class GeoRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane);
}
@Override
public boolean intersects(GeoShape geoShape) {
return geoShape.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
geoShape.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, topPlane, bottomPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -207,43 +215,6 @@ class GeoRectangle extends GeoBaseBBox {
.addPoint(ULHC).addPoint(URHC).addPoint(LLHC).addPoint(LRHC);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" getrelationship with "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(ULHC);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" inside of each other");
return OVERLAPS;
}
if (path.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
path.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane, rightPlane) ||
path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" shape contains rectangle");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, leftPlane, rightPlane);

View File

@ -101,6 +101,11 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(topPlane, planePoints);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -108,38 +113,6 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
.addHorizontalPlane(planetModel, topLat, topPlane);
}
@Override
public int getRelationship(final GeoShape path) {
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE)
return OVERLAPS;
final boolean insideShape = path.isWithin(topBoundaryPoint);
if (insideRectangle == ALL_INSIDE && insideShape)
return OVERLAPS;
// Second, the shortcut of seeing whether endpoints are in/out is not going to
// work with no area endpoints. So we rely entirely on intersections.
if (path.intersects(topPlane, planePoints))
return OVERLAPS;
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
// within the zone, but the shape includes areas outside the zone crossing a pole.
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
// one such point is within, then OVERLAPS is the right answer.
if (insideShape)
return CONTAINS;
if (insideRectangle == ALL_INSIDE)
return WITHIN;
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);

View File

@ -172,6 +172,13 @@ class GeoSouthRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, topPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -183,42 +190,6 @@ class GeoSouthRectangle extends GeoBaseBBox {
.addPoint(URHC).addPoint(ULHC).addPoint(planetModel.SOUTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" getrelationship with "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" inside of each other");
return OVERLAPS;
}
if (path.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
path.intersects(leftPlane, leftPlanePoints, topPlane, rightPlane) ||
path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" shape contains rectangle");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, leftPlane, rightPlane);

View File

@ -139,6 +139,14 @@ class GeoStandardCircle extends GeoBaseCircle {
return circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds);
}
@Override
public boolean intersects(GeoShape geoShape) {
if (circlePlane == null) {
return false;
}
return geoShape.intersects(circlePlane, circlePoints);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);

View File

@ -288,6 +288,23 @@ class GeoStandardPath extends GeoBasePath {
return false;
}
@Override
public boolean intersects(GeoShape geoShape) {
for (final SegmentEndpoint pathPoint : endPoints) {
if (pathPoint.intersects(geoShape)) {
return true;
}
}
for (final PathSegment pathSegment : segments) {
if (pathSegment.intersects(geoShape)) {
return true;
}
}
return false;
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -549,6 +566,17 @@ class GeoStandardPath extends GeoBasePath {
return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
}
/** Determine if this endpoint intersects a GeoShape.
*@param geoShape is the GeoShape.
*@return true if there is shape intersect this endpoint.
*/
public boolean intersects(final GeoShape geoShape) {
//System.err.println(" looking for intersection between plane "+p+" and circle "+circlePlane+" on proper side of "+cutoffPlanes+" within "+bounds);
if (circlePlane == null)
return false;
return geoShape.intersects(circlePlane, this.notablePoints, this.cutoffPlanes);
}
/** Get the bounds for a segment endpoint.
*@param planetModel is the planet model.
*@param bounds are the bounds to be modified.
@ -799,6 +827,15 @@ class GeoStandardPath extends GeoBasePath {
lowerConnectingPlane.intersects(planetModel, p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
}
/** Determine if this endpoint intersects a specified GeoShape.
*@param geoShape is the GeoShape.
*@return true if there GeoShape intersects this endpoint.
*/
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(upperConnectingPlane, upperConnectingPlanePoints, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
geoShape.intersects(lowerConnectingPlane, lowerConnectingPlanePoints, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
}
/** Get the bounds for a segment endpoint.
*@param planetModel is the planet model.
*@param bounds are the bounds to be modified.

View File

@ -165,6 +165,13 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, eitherBound);
}
@Override
public boolean intersects(final GeoShape geoShape) {
// Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
// requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
return geoShape.intersects(plane, planePoints, eitherBound);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -176,7 +183,7 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
@Override
public int getRelationship(final GeoShape path) {
if (path.intersects(plane, planePoints, eitherBound)) {
if (intersects(path)) {
return OVERLAPS;
}

View File

@ -139,6 +139,14 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds);
}
@Override
public boolean intersects(final GeoShape geoShape) {
// Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
// requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
return geoShape.intersects(leftPlane, planePoints) ||
geoShape.intersects(rightPlane, planePoints);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -150,30 +158,6 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
.addPoint(planetModel.SOUTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE)
return OVERLAPS;
final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape)
return OVERLAPS;
if (path.intersects(leftPlane, planePoints) ||
path.intersects(rightPlane, planePoints))
return OVERLAPS;
if (insideRectangle == ALL_INSIDE)
return WITHIN;
if (insideShape)
return CONTAINS;
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
// Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally

View File

@ -176,6 +176,16 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
// Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
// requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
return
geoShape.intersects(bottomPlane, bottomPlanePoints, eitherBound) ||
geoShape.intersects(leftPlane, leftPlanePoints, bottomPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, bottomPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -187,44 +197,6 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
.addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" comparing to "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" both inside each other");
return OVERLAPS;
}
if (
path.intersects(bottomPlane, bottomPlanePoints, eitherBound) ||
path.intersects(leftPlane, leftPlanePoints, bottomPlane) ||
path.intersects(rightPlane, rightPlanePoints, bottomPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" rectangle inside shape");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, eitherBound);

View File

@ -204,6 +204,14 @@ class GeoWideRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) ||
geoShape.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) ||
geoShape.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -216,44 +224,6 @@ class GeoWideRectangle extends GeoBaseBBox {
.addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" comparing to "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(ULHC);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" both inside each other");
return OVERLAPS;
}
if (path.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) ||
path.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) ||
path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) ||
path.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" rectangle inside shape");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, eitherBound);

View File

@ -175,6 +175,13 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane);
}
@Override
public boolean intersects(final GeoShape geoShape) {
return geoShape.intersects(topPlane, topPlanePoints, eitherBound) ||
geoShape.intersects(leftPlane, leftPlanePoints, topPlane) ||
geoShape.intersects(rightPlane, rightPlanePoints, topPlane);
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);
@ -186,43 +193,6 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
.addPoint(ULHC).addPoint(URHC).addPoint(planetModel.SOUTH_POLE);
}
@Override
public int getRelationship(final GeoShape path) {
//System.err.println(this+" comparing to "+path);
final int insideRectangle = isShapeInsideBBox(path);
if (insideRectangle == SOME_INSIDE) {
//System.err.println(" some inside");
return OVERLAPS;
}
final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE);
if (insideRectangle == ALL_INSIDE && insideShape) {
//System.err.println(" both inside each other");
return OVERLAPS;
}
if (path.intersects(topPlane, topPlanePoints, eitherBound) ||
path.intersects(leftPlane, leftPlanePoints, topPlane) ||
path.intersects(rightPlane, rightPlanePoints, topPlane)) {
//System.err.println(" edges intersect");
return OVERLAPS;
}
if (insideRectangle == ALL_INSIDE) {
//System.err.println(" shape inside rectangle");
return WITHIN;
}
if (insideShape) {
//System.err.println(" rectangle inside shape");
return CONTAINS;
}
//System.err.println(" disjoint");
return DISJOINT;
}
@Override
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, eitherBound);

View File

@ -66,6 +66,11 @@ class GeoWorld extends GeoBaseBBox {
return false;
}
@Override
public boolean intersects(final GeoShape geoShape) {
return false;
}
@Override
public void getBounds(Bounds bounds) {
super.getBounds(bounds);

View File

@ -0,0 +1,842 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Check relationship between polygon and GeoShapes of composite polygons. Normally we construct
* the composite polygon (when possible) and the complex one.
*/
public class CompositeGeoPolygonRelationshipsTest {
@Test
public void testGeoCompositePolygon1() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
GeoPolygon polConvex = buildGeoPolygon(20.0, -60.4,
20.1, -60.4,
20.1, -60.3,
20.0, -60.3,
20.0, -60.3);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
20.1, -60.4,
20.1, -60.3,
20.0, -60.3);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoCompositePolygon2() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((20.9 -60.8, 21.1 -60.8, 21.1 -60.6, 20.9 -60.6,20.9 -60.8))
GeoPolygon polConvex = buildGeoPolygon(20.9, -60.8,
21.1, -60.8,
21.1, -60.6,
20.9, -60.6,
20.9, -60.6);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -60.8,
21.1, -60.8,
21.1, -60.6,
20.9, -60.6);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoCompositePolygon3() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((20.9 -61.1, 21.1 -61.1, 21.1 -60.9, 20.9 -60.9,20.9 -61.1))
GeoPolygon polConvex = buildGeoPolygon(20.9, -61.1,
21.1, -61.1,
21.1, -60.9,
20.9, -60.9,
20.9, -60.9);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.1,
21.1, -61.1,
21.1, -60.9,
20.9, -60.9);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoCompositePolygon4() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((20.9 -61.4, 21.1 -61.4, 21.1 -61.2, 20.9 -61.2,20.9 -61.4))
GeoPolygon polConvex = buildGeoPolygon(20.9, -61.4,
21.1, -61.4,
21.1, -61.2,
20.9, -61.2,
20.9, -61.2);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.4,
21.1, -61.4,
21.1, -61.2,
20.9, -61.2);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.WITHIN, rel);
}
@Test
public void testGeoCompositePolygon5() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originaConvexlPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((19 -62, 23 -62, 23 -60, 19 -60,19 -62))
GeoPolygon polConvex = buildGeoPolygon(19, -62,
23, -62,
23, -60,
19, -60,
19, -60);
GeoPolygon polConcave = buildConcaveGeoPolygon(19, -62,
23, -62,
23, -60,
19, -60);
//convex
int rel = originaConvexlPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originaConvexlPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originaConvexlPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originaConvexlPol);
assertEquals(GeoArea.OVERLAPS, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoCompositePolygon6() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((19 -62, 24 -62, 24 -60, 19 -60,19 -62))
GeoPolygon polConvex = buildGeoPolygon(19, -62,
24, -62,
24, -60,
19, -60,
19, -60);
GeoPolygon polConcave = buildConcaveGeoPolygon(19, -62,
24, -62,
24, -60,
19, -60);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.WITHIN, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.CONTAINS, rel);
}
@Test
public void testGeoCompositePolygon7() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
//POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
21, -61,
22.820804, -60.257713,
23.207901, -61.453298,
20.119948, -61.655652);
//POLYGON ((20.2 -61.4, 20.5 -61.4, 20.5 -60.8, 20.2 -60.8,20.2 -61.4))
GeoPolygon polConvex = buildGeoPolygon(20.2, -61.4,
20.5, -61.4,
20.5, -60.8,
20.2, -60.8,
20.2, -60.8);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.2, -61.4,
20.5, -61.4,
20.5, -60.8,
20.2, -60.8);
//convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoCompositePolygon8() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713,21 -61, 19.845091 -60.452631))
GeoPolygon originalPol = buildGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713,
21, -61);
GeoShape shape = getInsideCompositeShape();
int rel = originalPol.getRelationship(shape);
assertEquals(GeoArea.WITHIN, rel);
}
@Test
public void testGeoPolygonPole1() {
//POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
GeoPolygon compositePol= getCompositePolygon();
GeoPolygon complexPol= getComplexPolygon();
//POLYGON ((20.9 -61.4, 21.1 -61.4, 21.1 -61.2, 20.9 -61.2,20.9 -61.4))
GeoPolygon polConvex = buildGeoPolygon(20.9, -61.4,
21.1, -61.4,
21.1, -61.2,
20.9, -61.2,
20.9, -61.2);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.4,
21.1, -61.4,
21.1, -61.2,
20.9, -61.2);
int rel = compositePol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(compositePol);
assertEquals(GeoArea.DISJOINT, rel);
rel = compositePol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(compositePol);
assertEquals(GeoArea.WITHIN, rel);
rel = complexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(complexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = complexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(complexPol);
assertEquals(GeoArea.WITHIN, rel);
}
@Test
public void testGeoPolygonPole2() {
//POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
GeoPolygon compositePol= getCompositePolygon();
GeoPolygon complexPol= getComplexPolygon();
//POLYGON((-1 81, -1 79,1 79,1 81, -1 81))
GeoPolygon polConvex = buildGeoPolygon(-1,81,
-1,79,
1,79,
1,81,
1,81);
GeoPolygon polConcave = buildConcaveGeoPolygon(-1,81,
-1,79,
1,79,
1,81);
int rel = compositePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(compositePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = compositePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(compositePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = complexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(complexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = complexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(complexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoPolygonPole3() {
//POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
GeoPolygon compositePol= getCompositePolygon();
GeoPolygon complexPol= getComplexPolygon();
//POLYGON((-1 86, -1 84,1 84,1 86, -1 86))
GeoPolygon polConvex = buildGeoPolygon(-1,86,
-1,84,
1,84,
1,86,
1,86);
GeoPolygon polConcave = buildConcaveGeoPolygon(-1,86,
-1,84,
1,84,
1,86);
int rel = compositePol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(compositePol);
assertEquals(GeoArea.CONTAINS, rel);
rel = compositePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(compositePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = complexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(complexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = complexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(complexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testMultiPolygon1() {
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPolygon multiPol= getMultiPolygon();
//POLYGON((-145.8555 -5.13, -145.8540 -5.13, -145.8540 -5.12, -145.8555 -5.12, -145.8555 -5.13))
GeoPolygon polConvex = buildGeoPolygon(-145.8555, -5.13,
-145.8540, -5.13,
-145.8540, -5.12,
-145.8555, -5.12,
-145.8555, -5.12);
GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
-145.8540, -5.13,
-145.8540, -5.12,
-145.8555, -5.12);
int rel = multiPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(multiPol);
assertEquals(GeoArea.DISJOINT, rel);
assertEquals(false,multiPol.intersects(polConvex));
assertEquals(false,polConvex.intersects(multiPol));
rel = multiPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(multiPol);
assertEquals(GeoArea.WITHIN, rel);
assertEquals(false,multiPol.intersects(polConcave));
assertEquals(false,polConcave.intersects(multiPol));
}
@Test
public void testMultiPolygon2() {
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPolygon multiPol= getMultiPolygon();
//POLYGON((-145.8555 -5.13, -145.85 -5.13, -145.85 -5.12, -145.8555 -5.12, -145.8555 -5.13))
GeoPolygon polConvex = buildGeoPolygon(-145.8555, -5.13,
-145.85, -5.13,
-145.85, -5.12,
-145.8555, -5.12,
-145.8555, -5.12);
GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
-145.85, -5.13,
-145.85, -5.12,
-145.8555, -5.12);
int rel = multiPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(multiPol);
assertEquals(GeoArea.OVERLAPS, rel);
assertEquals(true,multiPol.intersects(polConvex));
assertEquals(true,polConvex.intersects(multiPol));
rel = multiPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(multiPol);
assertEquals(GeoArea.OVERLAPS, rel);
assertEquals(true,multiPol.intersects(polConcave));
assertEquals(true,polConcave.intersects(multiPol));
}
@Test
public void testMultiPolygon3() {
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPolygon multiPol= getMultiPolygon();
//POLYGON((-146 -5.18, -145.854 -5.18, -145.854 -5.11, -146 -5.11, -146 -5.18))
//Case overlapping one of the polygons so intersection is false!
GeoPolygon polConvex = buildGeoPolygon(-146, -5.18,
-145.854, -5.18,
-145.854, -5.11,
-146, -5.11,
-146, -5.11);
GeoPolygon polConcave = buildConcaveGeoPolygon(-146, -5.18,
-145.854, -5.18,
-145.854, -5.11,
-146, -5.11);
int rel = multiPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(multiPol);
assertEquals(GeoArea.OVERLAPS, rel);
assertEquals(false,multiPol.intersects(polConvex));
assertEquals(false,polConvex.intersects(multiPol));
rel = multiPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(multiPol);
assertEquals(GeoArea.OVERLAPS, rel);
assertEquals(false,multiPol.intersects(polConcave));
assertEquals(false,polConcave.intersects(multiPol));
}
@Test
public void testMultiPolygon4() {
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPolygon multiPol= getMultiPolygon();
//POLYGON((-145.88 -5.13, -145.87 -5.13, -145.87 -5.12, -145.88 -5.12, -145.88 -5.13))
GeoPolygon polConvex = buildGeoPolygon(-145.88, -5.13,
-145.87, -5.13,
-145.87, -5.12,
-145.88, -5.12,
-145.88, -5.12);
GeoPolygon polConcave = buildConcaveGeoPolygon(-145.88, -5.13,
-145.87, -5.13,
-145.87, -5.12,
-145.88, -5.12);
int rel = multiPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(multiPol);
assertEquals(GeoArea.CONTAINS, rel);
assertEquals(false,multiPol.intersects(polConvex));
assertEquals(false,polConvex.intersects(multiPol));
rel = multiPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(multiPol);
assertEquals(GeoArea.OVERLAPS, rel);
assertEquals(false,multiPol.intersects(polConcave));
assertEquals(false,polConcave.intersects(multiPol));
}
@Test
public void testMultiPolygon5() {
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPolygon multiPol= getMultiPolygon();
//POLYGON((-146 -5.18, -145 -5.18, -145 -5.11, -146 -5.11, -146 -5.18))
GeoPolygon polConvex = buildGeoPolygon(-146, -5.18,
-145, -5.18,
-145, -5.11,
-146, -5.11,
-146, -5.11);
GeoPolygon polConcave = buildConcaveGeoPolygon(-146, -5.18,
-145, -5.18,
-145, -5.11,
-146, -5.11);
int rel = multiPol.getRelationship(polConvex);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConvex.getRelationship(multiPol);
assertEquals(GeoArea.WITHIN, rel);
assertEquals(false,multiPol.intersects(polConvex));
rel = multiPol.getRelationship(polConcave);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConcave.getRelationship(multiPol);
assertEquals(GeoArea.DISJOINT, rel);
assertEquals(false,multiPol.intersects(polConcave));
}
private GeoPolygon buildGeoPolygon(double lon1,double lat1,
double lon2,double lat2,
double lon3,double lat3,
double lon4,double lat4,
double lon5,double lat5)
{
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat5), Geo3DUtil.fromDegrees(lon5));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
points.add(point5);
return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
}
private GeoPolygon buildConcaveGeoPolygon(double lon1,double lat1,
double lon2,double lat2,
double lon3,double lat3,
double lon4,double lat4)
{
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points);
}
private GeoPolygon getCompositePolygon(){
//POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(0));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(45));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(90));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(135));
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(180));
GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(-135));
GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(-90));
GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(-45));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
points.add(point5);
points.add(point6);
points.add(point7);
points.add(point8);
return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
}
private GeoPolygon getComplexPolygon(){
//POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(0));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(45));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(90));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(135));
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(180));
GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(-135));
GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(80), Geo3DUtil.fromDegrees(-90));
GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(85), Geo3DUtil.fromDegrees(-45));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
points.add(point5);
points.add(point6);
points.add(point7);
points.add(point8);
GeoPolygonFactory.PolygonDescription pd = new GeoPolygonFactory.PolygonDescription(points);
return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, Collections.singletonList(pd));
}
private GeoPolygon getMultiPolygon(){
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17543698881), Geo3DUtil.fromDegrees(-145.790967486));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11348060995), Geo3DUtil.fromDegrees(-145.790854979));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11339421216), Geo3DUtil.fromDegrees(-145.853073512));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17535061936), Geo3DUtil.fromDegrees(-145.853192037));
final List<GeoPoint> points1 = new ArrayList<>();
points1.add(point1);
points1.add(point2);
points1.add(point3);
points1.add(point4);
GeoPolygonFactory.PolygonDescription pd1 = new GeoPolygonFactory.PolygonDescription(points1);
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17527125408), Geo3DUtil.fromDegrees(-145.8563923));
GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11332154814), Geo3DUtil.fromDegrees(-145.856222168));
GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11317773171), Geo3DUtil.fromDegrees(-145.918433943));
GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17512738429), Geo3DUtil.fromDegrees(-145.918610092));
final List<GeoPoint> points2 = new ArrayList<>();
points2.add(point5);
points2.add(point6);
points2.add(point7);
points2.add(point8);
GeoPolygonFactory.PolygonDescription pd2 = new GeoPolygonFactory.PolygonDescription(points2);
final List<GeoPolygonFactory.PolygonDescription> pds = new ArrayList<>();
pds.add(pd1);
pds.add(pd2);
return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, pds);
}
public GeoShape getInsideCompositeShape(){
//MULTIPOLYGON(((19.945091 -60.552631, 20.319948 -61.555652, 20.9 -61.5, 20.9 -61, 19.945091 -60.552631)),
// ((21.1 -61.5, 23.107901 -61.253298, 22.720804 -60.457713,21.1 -61, 21.1 -61.5)))
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-60.552631), Geo3DUtil.fromDegrees(19.945091));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61.555652), Geo3DUtil.fromDegrees(20.319948));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61.5), Geo3DUtil.fromDegrees(20.9));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61), Geo3DUtil.fromDegrees(20.9));
final List<GeoPoint> points1 = new ArrayList<>();
points1.add(point1);
points1.add(point2);
points1.add(point3);
points1.add(point4);
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61.5), Geo3DUtil.fromDegrees(21.1));
GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61.253298), Geo3DUtil.fromDegrees(23.107901));
GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-60.457713), Geo3DUtil.fromDegrees(22.720804));
GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61), Geo3DUtil.fromDegrees(21.1));
final List<GeoPoint> points2 = new ArrayList<>();
points2.add(point5);
points2.add(point6);
points2.add(point7);
points2.add(point8);
GeoPolygon p1 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points1);
GeoPolygon p2 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points2);
GeoCompositeMembershipShape compositeMembershipShape = new GeoCompositeMembershipShape();
compositeMembershipShape.addShape(p1);
compositeMembershipShape.addShape(p2);
return compositeMembershipShape;
}
}

View File

@ -0,0 +1,285 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import org.apache.lucene.spatial3d.geom.PlanetModel;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPolygon;
import org.apache.lucene.spatial3d.geom.GeoCircle;
import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoCompositePolygon;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.geo.Polygon;
import org.apache.lucene.geo.GeoUtils;
import java.util.List;
import java.util.ArrayList;
class Geo3DUtil {
/** How many radians are in one earth surface meter */
final static double RADIANS_PER_METER = 1.0 / PlanetModel.WGS84_MEAN;
/** How many radians are in one degree */
final static double RADIANS_PER_DEGREE = Math.PI / 180.0;
private static final double MAX_VALUE = PlanetModel.WGS84.getMaximumMagnitude();
private static final int BITS = 32;
private static final double MUL = (0x1L<<BITS)/(2*MAX_VALUE);
static final double DECODE = getNextSafeDouble(1/MUL);
static final int MIN_ENCODED_VALUE = encodeValue(-MAX_VALUE);
static final int MAX_ENCODED_VALUE = encodeValue(MAX_VALUE);
public static int encodeValue(double x) {
if (x > MAX_VALUE) {
throw new IllegalArgumentException("value=" + x + " is out-of-bounds (greater than WGS84's planetMax=" + MAX_VALUE + ")");
}
if (x < -MAX_VALUE) {
throw new IllegalArgumentException("value=" + x + " is out-of-bounds (less than than WGS84's -planetMax=" + -MAX_VALUE + ")");
}
long result = (long) Math.floor(x / DECODE);
assert result >= Integer.MIN_VALUE;
assert result <= Integer.MAX_VALUE;
return (int) result;
}
public static double decodeValue(int x) {
double result;
if (x == MIN_ENCODED_VALUE) {
// We must special case this, because -MAX_VALUE is not guaranteed to land precisely at a floor value, and we don't ever want to
// return a value outside of the planet's range (I think?). The max value is "safe" because we floor during encode:
result = -MAX_VALUE;
} else if (x == MAX_ENCODED_VALUE) {
result = MAX_VALUE;
} else {
// We decode to the center value; this keeps the encoding stable
result = (x+0.5) * DECODE;
}
assert result >= -MAX_VALUE && result <= MAX_VALUE;
return result;
}
/** Returns smallest double that would encode to int x. */
// NOTE: keep this package private!!
static double decodeValueFloor(int x) {
return x * DECODE;
}
/** Returns a double value >= x such that if you multiply that value by an int, and then
* divide it by that int again, you get precisely the same value back */
private static double getNextSafeDouble(double x) {
// Move to double space:
long bits = Double.doubleToLongBits(x);
// Make sure we are beyond the actual maximum value:
bits += Integer.MAX_VALUE;
// Clear the bottom 32 bits:
bits &= ~((long) Integer.MAX_VALUE);
// Convert back to double:
double result = Double.longBitsToDouble(bits);
assert result > x;
return result;
}
/** Returns largest double that would encode to int x. */
// NOTE: keep this package private!!
static double decodeValueCeil(int x) {
assert x < Integer.MAX_VALUE;
return Math.nextDown((x+1) * DECODE);
}
/** Converts degress to radians */
static double fromDegrees(final double degrees) {
return degrees * RADIANS_PER_DEGREE;
}
/** Converts earth-surface meters to radians */
static double fromMeters(final double meters) {
return meters * RADIANS_PER_METER;
}
/**
* Convert a set of Polygon objects into a GeoPolygon.
* @param polygons are the Polygon objects.
* @return the GeoPolygon.
*/
static GeoPolygon fromPolygon(final Polygon... polygons) {
//System.err.println("Creating polygon...");
if (polygons.length < 1) {
throw new IllegalArgumentException("need at least one polygon");
}
final GeoPolygon shape;
if (polygons.length == 1) {
final GeoPolygon component = fromPolygon(polygons[0]);
if (component == null) {
// Polygon is degenerate
shape = new GeoCompositePolygon();
} else {
shape = component;
}
} else {
final GeoCompositePolygon poly = new GeoCompositePolygon();
for (final Polygon p : polygons) {
final GeoPolygon component = fromPolygon(p);
if (component != null) {
poly.addShape(component);
}
}
shape = poly;
}
return shape;
//System.err.println("...done");
}
/**
* Convert a Polygon object to a large GeoPolygon.
* @param polygons is the list of polygons to convert.
* @return the large GeoPolygon.
*/
static GeoPolygon fromLargePolygon(final Polygon... polygons) {
if (polygons.length < 1) {
throw new IllegalArgumentException("need at least one polygon");
}
return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.WGS84, convertToDescription(polygons));
}
/**
* Convert input parameters to a path.
* @param pathLatitudes latitude values for points of the path: must be within standard +/-90 coordinate bounds.
* @param pathLongitudes longitude values for points of the path: must be within standard +/-180 coordinate bounds.
* @param pathWidthMeters width of the path in meters.
* @return the path.
*/
static GeoPath fromPath(final double[] pathLatitudes, final double[] pathLongitudes, final double pathWidthMeters) {
if (pathLatitudes.length != pathLongitudes.length) {
throw new IllegalArgumentException("same number of latitudes and longitudes required");
}
final GeoPoint[] points = new GeoPoint[pathLatitudes.length];
for (int i = 0; i < pathLatitudes.length; i++) {
final double latitude = pathLatitudes[i];
final double longitude = pathLongitudes[i];
GeoUtils.checkLatitude(latitude);
GeoUtils.checkLongitude(longitude);
points[i] = new GeoPoint(PlanetModel.WGS84, fromDegrees(latitude), fromDegrees(longitude));
}
return GeoPathFactory.makeGeoPath(PlanetModel.WGS84, fromMeters(pathWidthMeters), points);
}
/**
* Convert input parameters to a circle.
* @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
* @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
* @param radiusMeters maximum distance from the center in meters: must be non-negative and finite.
* @return the circle.
*/
static GeoCircle fromDistance(final double latitude, final double longitude, final double radiusMeters) {
GeoUtils.checkLatitude(latitude);
GeoUtils.checkLongitude(longitude);
return GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84, fromDegrees(latitude), fromDegrees(longitude), fromMeters(radiusMeters));
}
/**
* Convert input parameters to a box.
* @param minLatitude latitude lower bound: must be within standard +/-90 coordinate bounds.
* @param maxLatitude latitude upper bound: must be within standard +/-90 coordinate bounds.
* @param minLongitude longitude lower bound: must be within standard +/-180 coordinate bounds.
* @param maxLongitude longitude upper bound: must be within standard +/-180 coordinate bounds.
* @return the box.
*/
static GeoBBox fromBox(final double minLatitude, final double maxLatitude, final double minLongitude, final double maxLongitude) {
GeoUtils.checkLatitude(minLatitude);
GeoUtils.checkLongitude(minLongitude);
GeoUtils.checkLatitude(maxLatitude);
GeoUtils.checkLongitude(maxLongitude);
return GeoBBoxFactory.makeGeoBBox(PlanetModel.WGS84,
Geo3DUtil.fromDegrees(maxLatitude), Geo3DUtil.fromDegrees(minLatitude), Geo3DUtil.fromDegrees(minLongitude), Geo3DUtil.fromDegrees(maxLongitude));
}
/**
* Convert a Polygon object into a GeoPolygon.
* This method uses
* @param polygon is the Polygon object.
* @return the GeoPolygon.
*/
private static GeoPolygon fromPolygon(final Polygon polygon) {
// First, assemble the "holes". The geo3d convention is to use the same polygon sense on the inner ring as the
// outer ring, so we process these recursively with reverseMe flipped.
final Polygon[] theHoles = polygon.getHoles();
final List<GeoPolygon> holeList = new ArrayList<>(theHoles.length);
for (final Polygon hole : theHoles) {
//System.out.println("Hole: "+hole);
final GeoPolygon component = fromPolygon(hole);
if (component != null) {
holeList.add(component);
}
}
// Now do the polygon itself
final double[] polyLats = polygon.getPolyLats();
final double[] polyLons = polygon.getPolyLons();
// I presume the arguments have already been checked
final List<GeoPoint> points = new ArrayList<>(polyLats.length-1);
// We skip the last point anyway because the API requires it to be repeated, and geo3d doesn't repeat it.
for (int i = 0; i < polyLats.length - 1; i++) {
final int index = polyLats.length - 2 - i;
points.add(new GeoPoint(PlanetModel.WGS84, fromDegrees(polyLats[index]), fromDegrees(polyLons[index])));
}
//System.err.println(" building polygon with "+points.size()+" points...");
final GeoPolygon rval = GeoPolygonFactory.makeGeoPolygon(PlanetModel.WGS84, points, holeList);
//System.err.println(" ...done");
return rval;
}
/**
* Convert a list of polygons to a list of polygon descriptions.
* @param polygons is the list of polygons to convert.
* @return the list of polygon descriptions.
*/
private static List<GeoPolygonFactory.PolygonDescription> convertToDescription(final Polygon... polygons) {
final List<GeoPolygonFactory.PolygonDescription> descriptions = new ArrayList<>(polygons.length);
for (final Polygon polygon : polygons) {
final Polygon[] theHoles = polygon.getHoles();
final List<GeoPolygonFactory.PolygonDescription> holes = convertToDescription(theHoles);
// Now do the polygon itself
final double[] polyLats = polygon.getPolyLats();
final double[] polyLons = polygon.getPolyLons();
// I presume the arguments have already been checked
final List<GeoPoint> points = new ArrayList<>(polyLats.length-1);
// We skip the last point anyway because the API requires it to be repeated, and geo3d doesn't repeat it.
for (int i = 0; i < polyLats.length - 1; i++) {
final int index = polyLats.length - 2 - i;
points.add(new GeoPoint(PlanetModel.WGS84, fromDegrees(polyLats[index]), fromDegrees(polyLons[index])));
}
descriptions.add(new GeoPolygonFactory.PolygonDescription(points, holes));
}
return descriptions;
}
}

View File

@ -975,7 +975,7 @@ shape:
points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).shapes.get(0);
GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).getShape(0);
GeoPolygon polygonConcave = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points);
assertEquals(polygon,polygonConcave);
}
@ -994,7 +994,7 @@ shape:
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points);
GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).shapes.get(0);
GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).getShape(0);
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole));
assertEquals(polygon,polygon2);
}
@ -1006,7 +1006,7 @@ shape:
points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, 0.5));
points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0.5));
points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0));
GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).shapes.get(0);
GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points)).getShape(0);
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points);
assertEquals(polygon,polygon2);
}
@ -1025,7 +1025,7 @@ shape:
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points);
GeoPolygon polygon = (GeoPolygon)((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).shapes.get(0);
GeoPolygon polygon = ((GeoCompositePolygon)GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,Collections.singletonList(hole))).getShape(0);
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole));
assertEquals(polygon,polygon2);
}

View File

@ -0,0 +1,944 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import org.apache.lucene.util.LuceneTestCase;
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomDouble;
/**
* Class for generating random Geo3dShapes. They can be generated under
* given constraints which are expressed as a shape and a relationship.
*
* note that convexity for polygons is defined as polygons that contains
* antipodal points, otherwise they are convex. Internally they can be
* created using GeoConvexPolygons and GeoConcavePolygons.
*
*/
public class RandomGeoShapeGenerator extends LuceneTestCase {
/* Max num of iterations to find right shape under given constrains */
final private static int MAX_SHAPE_ITERATIONS = 50;
/* Max num of iterations to find right point under given constrains */
final private static int MAX_POINT_ITERATIONS = 1000;
/* Supported shapes */
final protected static int CONVEX_POLYGON = 0;
final protected static int CONVEX_POLYGON_WITH_HOLES = 1;
final protected static int CONCAVE_POLYGON = 2;
final protected static int CONCAVE_POLYGON_WITH_HOLES = 3;
final protected static int COMPLEX_POLYGON = 4;
final protected static int CIRCLE = 5;
final protected static int RECTANGLE = 6;
final protected static int PATH = 7;
final protected static int COLLECTION = 8;
/* Helper shapes for generating constraints whch are just three sided polygons */
final protected static int CONVEX_SIMPLE_POLYGON = 500;
final protected static int CONCAVE_SIMPLE_POLYGON = 501;
/**
* Method that returns empty Constraints object..
*
* @return an empty Constraints object
*/
public Constraints getEmptyConstraint(){
return new Constraints();
}
/**
* Method that returns a random generated a random Shape code from all
* supported shapes.
*
* @return a random generated shape code
*/
public int randomShapeType(){
return random().nextInt(9);
}
/**
* Method that returns a random generated a random Shape code from all
* convex supported shapes.
*
* @return a random generated convex shape code
*/
public int randomConvexShapeType(){
int shapeType = randomShapeType();
while (isConcave(shapeType)){
shapeType = randomShapeType();
}
return shapeType;
}
/**
* Method that returns a random generated a random Shape code from all
* concave supported shapes.
*
* @return a random generated concave shape code
*/
public int randomConcaveShapeType(){
int shapeType = randomShapeType();
while (!isConcave(shapeType)){
shapeType = randomShapeType();
}
return shapeType;
}
/**
* Method that returns a random generated GeoAreaShape code from all
* supported GeoAreaShapes.
*
* We are removing Collections because it is difficult to create shapes
* with properties in some cases.
*
* @return a random generated polygon code
*/
public int randomGeoAreaShapeType(){
return random().nextInt(8);
}
/**
* Check if a shape code represents a concave shape
*
* @return true if the shape represented by the code is concave
*/
public boolean isConcave(int shapeType){
return (shapeType == CONCAVE_POLYGON);
}
/**
* Method that returns a random generated Planet model from the supported
* Planet models. currently SPHERE and WGS84
*
* @return a random generated Planet model
*/
public PlanetModel randomPlanetModel() {
final int shapeType = random().nextInt(2);
switch (shapeType) {
case 0: {
return PlanetModel.SPHERE;
}
case 1: {
return PlanetModel.WGS84;
}
default:
throw new IllegalStateException("Unexpected planet model");
}
}
/**
* Method that returns a random generated GeoPoint under given constraints. Returns
* NULL if it cannot find a point under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPoint.
*/
public GeoPoint randomGeoPoint(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_POINT_ITERATIONS) {
double lat = randomDouble();
if (Math.PI/2 - Math.abs(lat) <0){
continue;
}
double lon = randomDouble();
if (Math.PI - Math.abs(lat) <0){
continue;
}
iterations++;
GeoPoint point = new GeoPoint(planetModel, lat, lon);
if (constraints.isWithin(point)) {
return point;
}
}
return null;
}
/**
* Method that returns a random generated GeoAreaShape.
*
* @param shapeType The GeoAreaShape code.
* @param planetModel The planet model.
* @return The random generated GeoAreaShape.
*/
public GeoAreaShape randomGeoAreaShape(int shapeType, PlanetModel planetModel){
GeoAreaShape geoAreaShape = null;
while (geoAreaShape == null){
geoAreaShape = randomGeoAreaShape(shapeType,planetModel,new Constraints());
}
return geoAreaShape;
}
/**
* Method that returns a random generated GeoAreaShape under given constraints. Returns
* NULL if it cannot build the GeoAreaShape under the given constraints.
*
* @param shapeType The GeoAreaShape code.
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoAreaShape.
*/
public GeoAreaShape randomGeoAreaShape(int shapeType, PlanetModel planetModel, Constraints constraints){
return (GeoAreaShape)randomGeoShape(shapeType, planetModel, constraints);
}
/**
* Method that returns a random generated GeoShape.
*
* @param shapeType The shape code.
* @param planetModel The planet model.
* @return The random generated GeoShape.
*/
public GeoShape randomGeoShape(int shapeType, PlanetModel planetModel){
GeoShape geoShape = null;
while (geoShape == null){
geoShape = randomGeoShape(shapeType,planetModel,new Constraints());
}
return geoShape;
}
/**
* Method that returns a random generated GeoShape under given constraints. Returns
* NULL if it cannot build the GeoShape under the given constraints.
*
* @param shapeType The polygon code.
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoShape.
*/
public GeoShape randomGeoShape(int shapeType, PlanetModel planetModel, Constraints constraints){
switch (shapeType) {
case CONVEX_POLYGON: {
return convexPolygon(planetModel, constraints);
}
case CONVEX_POLYGON_WITH_HOLES: {
return convexPolygonWithHoles(planetModel, constraints);
}
case CONCAVE_POLYGON: {
return concavePolygon(planetModel, constraints);
}
case CONCAVE_POLYGON_WITH_HOLES: {
return concavePolygonWithHoles(planetModel, constraints);
}
case COMPLEX_POLYGON: {
return complexPolygon(planetModel, constraints);
}
case CIRCLE: {
return circle(planetModel, constraints);
}
case RECTANGLE: {
return rectangle(planetModel, constraints);
}
case PATH: {
return path(planetModel, constraints);
}
case COLLECTION: {
return collection(planetModel, constraints);
}
case CONVEX_SIMPLE_POLYGON: {
return simpleConvexPolygon(planetModel, constraints);
}
case CONCAVE_SIMPLE_POLYGON: {
return concaveSimplePolygon(planetModel, constraints);
}
default:
throw new IllegalStateException("Unexpected shape type");
}
}
/**
* Method that returns a random generated a GeoCircle under given constraints. Returns
* NULL if it cannot build the GeoCircle under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoCircle.
*/
private GeoCircle circle(PlanetModel planetModel , Constraints constraints) {
int iterations=0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
final GeoPoint center = randomGeoPoint(planetModel, constraints);
if (center == null){
continue;
}
final double radius = randomCutoffAngle();
try {
GeoCircle circle = GeoCircleFactory.makeGeoCircle(planetModel, center.getLatitude(), center.getLongitude(), radius);
if (!constraints.valid(circle)) {
continue;
}
return circle;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a GeoBBox under given constraints. Returns
* NULL if it cannot build the GeoBBox under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoBBox.
*/
private GeoBBox rectangle(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
final GeoPoint point1 = randomGeoPoint(planetModel, constraints);
if (point1 == null){
continue;
}
final GeoPoint point2 = randomGeoPoint(planetModel, constraints);
if (point2 == null){
continue;
}
double minLat = Math.min(point1.getLatitude(), point2.getLatitude());
double maxLat = Math.max(point1.getLatitude(), point2.getLatitude());
double minLon = Math.min(point1.getLongitude(), point2.getLongitude());
double maxLon = Math.max(point1.getLongitude(), point2.getLongitude());
try {
GeoBBox bbox = GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, minLon, maxLon);
if (!constraints.valid(bbox)) {
continue;
}
return bbox;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a GeoPath under given constraints. Returns
* NULL if it cannot build the GeoPath under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPath.
*/
private GeoPath path(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
int vertexCount = random().nextInt(2) + 2;
List<GeoPoint> geoPoints = points(vertexCount, planetModel, constraints);
double width =randomCutoffAngle();
try {
GeoPath path = GeoPathFactory.makeGeoPath(planetModel, width, geoPoints.toArray(new GeoPoint[geoPoints.size()]));
if (!constraints.valid(path)) {
continue;
}
return path;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a GeoCompositeMembershipShape under given constraints. Returns
* NULL if it cannot build the GGeoCompositeMembershipShape under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoCompositeMembershipShape.
*/
private GeoCompositeAreaShape collection(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
int numberShapes = random().nextInt(3) + 2;
GeoCompositeAreaShape collection = new GeoCompositeAreaShape();
for(int i=0; i<numberShapes;i++){
GeoPolygon member = convexPolygon(planetModel, constraints);
if (member != null){
collection.addShape(member);
}
}
if (collection.shapes.size() ==0){
continue;
}
return collection;
}
return null;
}
/**
* Method that returns a random generated a convex GeoPolygon under given constraints. Returns
* NULL if it cannot build the GePolygon under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon convexPolygon(PlanetModel planetModel, Constraints constraints) {
int vertexCount = random().nextInt(4) + 3;
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
if (!constraints.valid(polygon) || isConcave(planetModel, polygon)) {
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a convex GeoPolygon with holes under given constraints. Returns
* NULL if it cannot build the GeoPolygon with holes under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon convexPolygonWithHoles(PlanetModel planetModel, Constraints constraints) {
int vertexCount = random().nextInt(4) + 3;
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
//polygon should comply with all constraints except disjoint as we have holes
Constraints polygonConstraints = new Constraints();
polygonConstraints.putAll(constraints.getContains());
polygonConstraints.putAll(constraints.getWithin());
polygonConstraints.putAll(constraints.getDisjoint());
if (!polygonConstraints.valid(polygon) || isConcave(planetModel, polygon)){
continue;
}
//hole must overlap with polygon and comply with any CONTAINS constraint.
Constraints holeConstraints = new Constraints();
holeConstraints.putAll(constraints.getContains());
holeConstraints.put(polygon,GeoArea.OVERLAPS);
//Points must be with in the polygon and must comply
// CONTAINS and DISJOINT constraints
Constraints pointsConstraints = new Constraints();
pointsConstraints.put(polygon,GeoArea.WITHIN);
pointsConstraints.putAll(constraints.getContains());
pointsConstraints.putAll(constraints.getDisjoint());
List<GeoPolygon> holes = concavePolygonHoles(planetModel, holeConstraints, pointsConstraints);
//we should have at least one hole
if (holes.size() == 0){
continue;
}
polygon = GeoPolygonFactory.makeGeoPolygon(planetModel,orderedGeoPoints,holes);
if (!constraints.valid(polygon) || isConcave(planetModel, polygon)){
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random list if concave GeoPolygons under given constraints. Method
* use to generate convex holes. Note that constraints for points and holes are different,
*
* @param planetModel The planet model.
* @param holeConstraints The given constraints that a hole must comply.
* @param pointConstraints The given constraints that a point must comply.
* @return The random generated GeoPolygon.
*/
private List<GeoPolygon> concavePolygonHoles(PlanetModel planetModel,
Constraints holeConstraints,
Constraints pointConstraints) {
int iterations =0;
int holesCount = random().nextInt(3) + 1;
List<GeoPolygon> holes = new ArrayList<>();
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
int vertexCount = random().nextInt(3) + 3;
List<GeoPoint> geoPoints = points(vertexCount, planetModel, pointConstraints);
geoPoints = orderPoints(geoPoints);
Collections.reverse(geoPoints);
try {
GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints);
if (!holeConstraints.valid(hole) || isConvex(planetModel, hole)) {
continue;
}
holes.add(hole);
if (holes.size() == holesCount){
return holes;
}
pointConstraints.put(hole, GeoArea.DISJOINT);
} catch (IllegalArgumentException e) {
continue;
}
}
return holes;
}
/**
* Method that returns a random generated a concave GeoPolygon under given constraints. Returns
* NULL if it cannot build the concave GeoPolygon under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon concavePolygon(PlanetModel planetModel, Constraints constraints) {
int vertexCount = random().nextInt(4) + 3;
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
Collections.reverse(orderedGeoPoints);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
if (!constraints.valid(polygon) || isConvex(planetModel, polygon)) {
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a concave GeoPolygon with holes under given constraints. Returns
* NULL if it cannot build the GeoPolygon under the given constraints. Note that the final GeoPolygon is
* convex as the hole wraps the convex GeoPolygon.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon concavePolygonWithHoles(PlanetModel planetModel, Constraints constraints) {
int vertexCount = random().nextInt(4) + 3;
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
//we first build the hole. We consider all constraints except
// disjoint as we have a hole
Constraints holeConstraints = new Constraints();
holeConstraints.putAll(constraints.getContains());
holeConstraints.putAll(constraints.getWithin());
holeConstraints.putAll(constraints.getOverlaps());
GeoPolygon hole = convexPolygon(planetModel, holeConstraints);
if (hole == null){
continue;
}
// Now we get points for polygon. Must we with in the hole
// and we add contain constraints
Constraints pointConstraints = new Constraints();
pointConstraints.put(hole, GeoArea.WITHIN);
pointConstraints.putAll(constraints.getContains());
List<GeoPoint> geoPoints = points(vertexCount,planetModel, pointConstraints);
List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
Collections.reverse(orderedGeoPoints);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints, Collections.singletonList(hole));
//final polygon must be convex
if (!constraints.valid(polygon) || isConcave(planetModel,polygon)) {
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated complex GeoPolygon under given constraints. Returns
* NULL if it cannot build the complex GeoPolygon under the given constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon complexPolygon(PlanetModel planetModel, Constraints constraints) {
int polygonsCount =random().nextInt(2) + 1;
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPolygonFactory.PolygonDescription> polDescription = new ArrayList<>();
while(polDescription.size() < polygonsCount){
int vertexCount = random().nextInt(14) + 3;
List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
orderPoints(geoPoints);
polDescription.add(new GeoPolygonFactory.PolygonDescription(geoPoints));
}
try {
GeoPolygon polygon = GeoPolygonFactory.makeLargeGeoPolygon(planetModel,polDescription);
if (!constraints.valid(polygon)) {
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a concave square GeoPolygon under given constraints. Returns
* NULL if it cannot build the concave GeoPolygon under the given constraints. This shape is an utility
* to build constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon simpleConvexPolygon(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPoint> points = points(3,planetModel,constraints);
points = orderPoints(points);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoConvexPolygon(planetModel, points);
if(!constraints.valid(polygon) || isConcave(planetModel,polygon)){
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random generated a convex square GeoPolygon under given constraints. Returns
* NULL if it cannot build the convex GeoPolygon under the given constraints. This shape is an utility
* to build constraints.
*
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated GeoPolygon.
*/
private GeoPolygon concaveSimplePolygon(PlanetModel planetModel, Constraints constraints) {
int iterations = 0;
while (iterations < MAX_SHAPE_ITERATIONS) {
iterations++;
List<GeoPoint> points = points(3, planetModel, constraints);
points = orderPoints(points);
Collections.reverse(points);
try {
GeoPolygon polygon = GeoPolygonFactory.makeGeoConcavePolygon(planetModel, points);
if(!constraints.valid(polygon) || isConvex(planetModel, polygon)){
continue;
}
return polygon;
} catch (IllegalArgumentException e) {
continue;
}
}
return null;
}
/**
* Method that returns a random list of generated GeoPoints under given constraints. If it cannot
* find a point it will add a point that might not comply with the constraints.
*
* @param count The number of points
* @param planetModel The planet model.
* @param constraints The given constraints.
* @return The random generated List of GeoPoints.
*/
private List<GeoPoint> points(int count, PlanetModel planetModel, Constraints constraints){
List<GeoPoint> geoPoints = new ArrayList<>(count);
for(int i= 0; i< count; i++) {
GeoPoint point = randomGeoPoint(planetModel, constraints);
if (point == null){
point = randomGeoPoint(planetModel, new Constraints());
}
geoPoints.add(point);
}
return geoPoints;
}
/**
* Check if a GeoPolygon is pure concave. Note that our definition for concavity is that the polygon
* contains antipodal points.
*
* @param planetModel The planet model.
* @param shape The polygon to check.
* @return True if the polygon contains antipodal points.
*/
private boolean isConcave(PlanetModel planetModel, GeoPolygon shape){
return (shape.isWithin(planetModel.NORTH_POLE) && shape.isWithin(planetModel.SOUTH_POLE))||
(shape.isWithin(planetModel.MAX_X_POLE) && shape.isWithin(planetModel.MIN_X_POLE)) ||
(shape.isWithin(planetModel.MAX_Y_POLE) && shape.isWithin(planetModel.MIN_Y_POLE));
}
/**
* Check if a GeoPolygon is pure convex. Note that our definition for convexity is that the polygon
* does not contain antipodal points.
*
* @param planetModel The planet model.
* @param shape The polygon to check.
* @return True if the polygon dies not contains antipodal points.
*/
private boolean isConvex(PlanetModel planetModel, GeoPolygon shape){
return !isConcave(planetModel,shape);
}
/**
* Generates a random number between 0 and PI.
*
* @return the cutoff angle.
*/
private double randomCutoffAngle() {
while(true) {
double radius = randomDouble();
if (radius <0 || radius > Math.PI){
continue;
}
return radius;
}
}
/**
* Method that orders a lit of points anti-clock-wise to prevent crossing edges.
*
* @param originalPoints The points to order.
* @return The list of ordered points anti-clockwise.
*/
private List<GeoPoint> orderPoints(List<GeoPoint> originalPoints){
List<GeoPoint> points = new ArrayList<>(originalPoints.size());
points.addAll(originalPoints); //make a copy
GeoPoint lPoint = getPointLefLon(points);
points.remove(lPoint);
GeoPoint rPoint = getPointRigthLon(points);
points.remove(rPoint);
List<GeoPoint> APoints = getPointsBelowAndSort(points, lPoint);
List<GeoPoint> BPoints = getPointsAboveAndsort(points, lPoint);
List<GeoPoint> result = new ArrayList<>();
result.add(lPoint);
result.addAll(APoints);
result.add(rPoint);
result.addAll(BPoints);
return result;
}
private List<GeoPoint> getPointsAboveAndsort(List<GeoPoint> points,GeoPoint lPoint) {
List<GeoPoint> BPoints = new ArrayList<>();
for (GeoPoint point : points){
if(point.getLatitude() > lPoint.getLatitude()){
BPoints.add(point);
}
}
Collections.sort(BPoints, new Comparator<GeoPoint>() {
public int compare(GeoPoint idx1, GeoPoint idx2) {
return Double.compare(idx1.getLongitude(), idx2.getLongitude());
}
});
return BPoints;
}
private List<GeoPoint> getPointsBelowAndSort(List<GeoPoint> points,GeoPoint lPoint) {
List<GeoPoint> APoints = new ArrayList<>();
for (GeoPoint point : points){
if(point.getLatitude() < lPoint.getLatitude()){
APoints.add(point);
}
}
Collections.sort(APoints, new Comparator<GeoPoint>() {
public int compare(GeoPoint idx1, GeoPoint idx2) {
return Double.compare(idx1.getLongitude(), idx2.getLongitude());
}
});
return APoints;
}
private GeoPoint getPointLefLon(List<GeoPoint> points) {
GeoPoint lPoint = null;
for (GeoPoint point : points){
if(lPoint == null ){
lPoint = point;
}
else{
if (lPoint.getLongitude() > point.getLongitude()){
lPoint = point;
}
}
}
return lPoint;
}
private GeoPoint getPointRigthLon(List<GeoPoint> points) {
GeoPoint rPoint = null;
for (GeoPoint point : points){
if(rPoint == null ){
rPoint = point;
}
else{
if (rPoint.getLongitude() < point.getLongitude()){
rPoint = point;
}
}
}
return rPoint;
}
/**
* Class that holds the constraints that are given to
* build shapes. It consists in a list of GeoAreaShapes
* and relationships the new shape needs to satisfy.
*/
class Constraints extends HashMap<GeoAreaShape, Integer>{
/**
* Check if the shape is valid under the constraints.
*
* @param shape The shape to check
* @return true if the shape satisfy the constraints, else false.
*/
public boolean valid(GeoShape shape) {
if (shape == null){
return false;
}
for (GeoAreaShape constraint : keySet()) {
if (constraint.getRelationship(shape) != get(constraint)) {
return false;
}
}
return true;
}
/**
* Check if a point is Within the constraints.
*
* @param point The point to check
* @return true if the point satisfy the constraints, else false.
*/
public boolean isWithin(GeoPoint point) {
for (GeoShape constraint : keySet()) {
if (!(validPoint(point, constraint, get(constraint)))) {
return false;
}
}
return true;
}
/**
* Check if a point is Within one constraint given by a shape and a relationship.
*
* @param point The point to check
* @param shape The shape of the constraint
* @param relationship The relationship of the constraint.
* @return true if the point satisfy the constraint, else false.
*/
private boolean validPoint(GeoPoint point, GeoShape shape, int relationship) {
//For GeoCompositeMembershipShape we only consider the first shape to help
// converging
if (relationship == GeoArea.WITHIN && shape instanceof GeoCompositeMembershipShape) {
shape = (((GeoCompositeMembershipShape) shape).shapes.get(0));
}
switch (relationship) {
case GeoArea.DISJOINT:
return !shape.isWithin(point);
case GeoArea.OVERLAPS:
return true;
case GeoArea.CONTAINS:
return !shape.isWithin(point);
case GeoArea.WITHIN:
return shape.isWithin(point);
default:
return true;
}
}
/**
* Collect the CONTAINS constraints in the object
*
* @return the CONTAINS constraints.
*/
public Constraints getContains(){
return getConstraintsOfType(GeoArea.CONTAINS);
}
/**
* Collect the WITHIN constraints in the object
*
* @return the WITHIN constraints.
*/
public Constraints getWithin(){
return getConstraintsOfType(GeoArea.WITHIN);
}
/**
* Collect the OVERLAPS constraints in the object
*
* @return the OVERLAPS constraints.
*/
public Constraints getOverlaps(){
return getConstraintsOfType(GeoArea.OVERLAPS);
}
/**
* Collect the DISJOINT constraints in the object
*
* @return the DISJOINT constraints.
*/
public Constraints getDisjoint(){
return getConstraintsOfType(GeoArea.DISJOINT);
}
private Constraints getConstraintsOfType(int type){
Constraints constraints = new Constraints();
for (GeoAreaShape constraint : keySet()) {
if (type == get(constraint)) {
constraints.put(constraint, type);
}
}
return constraints;
}
}
}

View File

@ -0,0 +1,257 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.junit.Test;
/**
* Random test to check relationship between GeoAreaShapes and GeoShapes.
*/
public class RandomGeoShapeRelationshipTest extends RandomGeoShapeGenerator {
/**
* Test for WITHIN points. We build a WITHIN shape with respect the geoAreaShape
* and create a point WITHIN the shape. The resulting shape should be WITHIN
* the original shape.
*
*/
@Test
@Repeat(iterations = 5)
public void testRandomPointWithin() {
int referenceShapeType = CONVEX_POLYGON;
PlanetModel planetModel = randomPlanetModel();
int shapeType = randomShapeType();
GeoAreaShape shape = null;
GeoPoint point = null;
while (point == null) {
shape = randomGeoAreaShape(shapeType, planetModel);
Constraints constraints = getEmptyConstraint();
constraints.put(shape, GeoArea.WITHIN);
GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
if (reference != null) {
constraints = new Constraints();
constraints.put(reference, GeoArea.WITHIN);
point = randomGeoPoint(planetModel, constraints);
}
}
assertTrue(shape.isWithin(point));
}
/**
* Test for NOT WITHIN points. We build a DIJOINT shape with respect the geoAreaShape
* and create a point WITHIN that shape. The resulting shape should not be WITHIN
* the original shape.
*
*/
@Repeat(iterations = 5)
public void testRandomPointNotWithin() {
int referenceShapeType = CONVEX_POLYGON;
PlanetModel planetModel = randomPlanetModel();
int shapeType = randomShapeType();
GeoAreaShape shape = null;
GeoPoint point = null;
while (point == null) {
shape = randomGeoAreaShape(shapeType, planetModel);
Constraints constraints = getEmptyConstraint();
constraints.put(shape, GeoArea.DISJOINT);
GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
if (reference != null) {
constraints = new Constraints();
constraints.put(reference, GeoArea.WITHIN);
point = randomGeoPoint(planetModel, constraints);
}
}
assertFalse(shape.isWithin(point));
}
/**
* Test for disjoint shapes. We build a DISJOINT shape with respect the geoAreaShape
* and create shapes WITHIN that shapes. The resulting shape should be DISJOINT
* to the geoAreaShape.
*
* Note that both shapes cannot be concave.
*/
@Test
@Repeat(iterations = 5)
public void testRandomDisjoint() {
int referenceShapeType = CONVEX_SIMPLE_POLYGON;
PlanetModel planetModel = randomPlanetModel();
int geoAreaShapeType = randomGeoAreaShapeType();
int shapeType =randomConvexShapeType();
GeoShape shape = null;
GeoAreaShape geoAreaShape = null;
while (shape == null) {
geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
Constraints constraints = new Constraints();
constraints.put(geoAreaShape, GeoArea.DISJOINT);
GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
if (reference != null) {
constraints = getEmptyConstraint();
constraints.put(reference, GeoArea.WITHIN);
shape = randomGeoShape(shapeType, planetModel, constraints);
}
}
int rel = geoAreaShape.getRelationship(shape);
assertEquals(GeoArea.DISJOINT, rel);
if (shape instanceof GeoArea) {
rel = ((GeoArea)shape).getRelationship(geoAreaShape);
assertEquals(GeoArea.DISJOINT, rel);
}
}
/**
* Test for within shapes. We build a shape WITHIN the geoAreaShape and create
* shapes WITHIN that shape. The resulting shape should be WITHIN
* to the geoAreaShape.
*
* Note that if the geoAreaShape is not concave the other shape must be not concave.
*/
@Test
@Repeat(iterations = 5)
public void testRandomWithIn() {
PlanetModel planetModel = randomPlanetModel();
int geoAreaShapeType = randomGeoAreaShapeType();
int shapeType =randomShapeType();
int referenceShapeType = CONVEX_SIMPLE_POLYGON;
if (!isConcave(geoAreaShapeType)){
shapeType =randomConvexShapeType();
}
if(isConcave(shapeType)){//both concave
referenceShapeType = CONCAVE_SIMPLE_POLYGON;
}
GeoShape shape = null;
GeoAreaShape geoAreaShape = null;
while (shape == null) {
geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
Constraints constraints = new Constraints();
constraints.put(geoAreaShape, GeoArea.WITHIN);
GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
if (reference != null) {
constraints = new Constraints();
constraints.put(reference, GeoArea.WITHIN);
shape = randomGeoShape(shapeType, planetModel, constraints);
}
}
int rel = geoAreaShape.getRelationship(shape);
assertEquals(GeoArea.WITHIN, rel);
if (shape instanceof GeoArea) {
rel = ((GeoArea)shape).getRelationship(geoAreaShape);
assertEquals(GeoArea.CONTAINS, rel);
}
}
/**
* Test for contains shapes. We build a shape containing the geoAreaShape and create
* shapes WITHIN that shape. The resulting shape should CONTAIN
* the geoAreaShape.
*
* Note that if the geoAreaShape is concave the other shape must be concave.
* If shape is concave, the shape for reference should be concave as well.
*
*/
@Test
@Repeat(iterations = 1)
public void testRandomContains() {
int referenceShapeType = CONVEX_SIMPLE_POLYGON;
PlanetModel planetModel = randomPlanetModel();
int geoAreaShapeType = randomGeoAreaShapeType();
while (geoAreaShapeType == COLLECTION){
geoAreaShapeType = randomGeoAreaShapeType();
}
int shapeType = randomShapeType();
if (isConcave(geoAreaShapeType)){
shapeType = randomConcaveShapeType();
}
if (isConcave(shapeType)){
referenceShapeType = CONCAVE_SIMPLE_POLYGON;
}
GeoShape shape = null;
GeoAreaShape geoAreaShape = null;
while (shape == null) {
geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
Constraints constraints = getEmptyConstraint();
constraints.put(geoAreaShape, GeoArea.CONTAINS);
GeoPolygon reference =(GeoPolygon)randomGeoAreaShape(referenceShapeType, planetModel, constraints);
if (reference != null) {
constraints = getEmptyConstraint();
constraints.put(reference, GeoArea.CONTAINS);
shape = randomGeoShape(shapeType, planetModel, constraints);
}
}
int rel = geoAreaShape.getRelationship(shape);
assertEquals(GeoArea.CONTAINS, rel);
if (shape instanceof GeoArea) {
rel = ((GeoArea)shape).getRelationship(geoAreaShape);
assertEquals(GeoArea.WITHIN, rel);
}
}
/**
* Test for overlapping shapes. We build a shape that contains part of the
* geoAreaShape, is disjoint to other part and contains a disjoint shape. We create
* shapes according the criteria. The resulting shape should OVERLAP
* the geoAreaShape.
*/
@Test
@Repeat(iterations = 5)
public void testRandomOverlaps() {
PlanetModel planetModel = randomPlanetModel();
int geoAreaShapeType = randomGeoAreaShapeType();
int shapeType = randomShapeType();
GeoShape shape = null;
GeoAreaShape geoAreaShape = null;
while (shape == null) {
geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
Constraints constraints = getEmptyConstraint();
constraints.put(geoAreaShape,GeoArea.WITHIN);
GeoAreaShape reference1 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
if (reference1 == null){
continue;
}
constraints = getEmptyConstraint();
constraints.put(geoAreaShape, GeoArea.WITHIN);
constraints.put(reference1, GeoArea.DISJOINT);
GeoAreaShape reference2 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
if (reference2 == null){
continue;
}
constraints = getEmptyConstraint();
constraints.put(geoAreaShape, GeoArea.DISJOINT);
GeoAreaShape reference3 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
if (reference3 != null) {
constraints = new Constraints();
constraints.put(reference1, GeoArea.DISJOINT);
constraints.put(reference2, GeoArea.CONTAINS);
constraints.put(reference3, GeoArea.CONTAINS);
shape = randomGeoShape(shapeType, planetModel, constraints);
}
}
int rel = geoAreaShape.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
if (shape instanceof GeoArea) {
rel = ((GeoArea)shape).getRelationship(geoAreaShape);
assertEquals(GeoArea.OVERLAPS, rel);
}
}
}

View File

@ -0,0 +1,837 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.spatial3d.geom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Check relationship between polygon and GeoShapes of basic polygons. Normally we construct
* the convex, concave counterpart and the convex polygon as a complex polygon.
*/
public class SimpleGeoPolygonRelationshipsTest {
/**
* Test with two shapes with no crossing edges and no points in common in convex case.
*/
@Test
public void testGeoSimplePolygon1() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -60.4,
20.1, -60.4,
20.1, -60.3,
20.0, -60.3);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
20.1, -60.4,
20.1, -60.3,
20.0, -60.3);
//Convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);//Check
//Concave
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
//Complex
rel = originalComplexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalComplexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalComplexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalComplexPol);
assertEquals(GeoArea.WITHIN, rel);
}
/**
* Test with two shapes with crossing edges and some points inside in convex case.
*/
@Test
public void testGeoSimplePolygon2() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
//POLYGON ((20.0 -60.4, 23.1 -60.4, 23.1 -60.3, 20.0 -60.3,20.0 -60.4))
GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -60.4,
23.1, -60.4,
23.1, -60.3,
20.0, -60.3);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
23.1, -60.4,
23.1, -60.3,
20.0, -60.3);
//Convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//Concave
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
//Complex
rel = originalComplexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalComplexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalComplexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
/**
* Test with two shapes with no crossing edges and all points inside in convex case.
*/
@Test
public void testGeoSimplePolygon3() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
//POLYGON ((20.0 -61.1, 20.1 -61.1, 20.1 -60.5, 20.0 -60.5,20.0 -61.1))
GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -61.1,
20.1, -61.1,
20.1, -60.5,
20.0, -60.5);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -61.1,
20.1, -61.1,
20.1, -60.5,
20.0, -60.5);
//Convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//Concave
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.WITHIN, rel);//check
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.DISJOINT, rel);
//Complex
rel = originalComplexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalComplexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalComplexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalComplexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
/**
* Test with two shapes with crossing edges and no points inside in convex case.
*/
@Test
public void testGeoSimplePolygon4() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
//POLYGON ((20.0 -62.4, 20.1 -62.4, 20.1 -60.3, 20.0 -60.3,20.0 -62.4)) intersects no points inside
GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -62.4,
20.1, -62.4,
20.1, -60.3,
20.0, -60.3);
GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -62.4,
20.1, -62.4,
20.1, -60.3,
20.0, -60.3);
//Convex
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
//concave
rel = originalConcavePol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConcavePol);
assertEquals(GeoArea.OVERLAPS, rel);
//Complex
rel = originalComplexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalComplexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalComplexPol);
assertEquals(GeoArea.OVERLAPS, rel);
}
/**
* Test with two shapes with no crossing edges and polygon in hole in convex case.
*/
@Test
public void testGeoSimplePolygonWithHole1() {
//POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7);
GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
-135, -30,
-137, -30,
-137, -31, hole);
GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
-135, -30,
-137, -30,
-137, -31);
GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7, holeInv);
//POLYGON((-135.7 -30.6, -135.7 -30.45, -136 -30.45, -136 -30.6, -135.7 -30.6)) in the hole
GeoPolygon polConvex = buildConvexGeoPolygon(-135.7, -30.6,
-135.7, -30.45,
-136, -30.45,
-136, -30.6);
GeoPolygon polConcave = buildConcaveGeoPolygon(-135.7, -30.6,
-135.7, -30.45,
-136, -30.45,
-136, -30.6);
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);
rel = originalConvexPolInv.getRelationship(polConvex);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConvex.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConvexPolInv.getRelationship(polConcave);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.WITHIN, rel);
}
/**
* Test with two shapes with crossing edges in hole and some points inside in convex case.
*/
@Test
public void testGeoSimplePolygonWithHole2() {
//POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7);
GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
-135, -30,
-137, -30,
-137, -31, hole);
GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
-135, -30,
-137, -30,
-137, -31);
GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7, holeInv);
//POLYGON((-135.5 -31.2, -135.5 -30.8, -136 -30.8, -136 -31.2, -135.5 -31.2)) intersects the hole
GeoPolygon polConvex = buildConvexGeoPolygon(-135.5, -30.2,
-135.5, -30.8,
-136, -30.8,
-136, -30.2);
GeoPolygon polConcave = buildConcaveGeoPolygon(-135.5, -30.2,
-135.5, -30.8,
-136, -30.8,
-136, -30.2);
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPolInv.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPolInv.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.OVERLAPS, rel);
}
/**
* Test with two shapes with crossing edges and some points inside in convex case.
*/
@Test
public void testGeoSimplePolygonWithHole3() {
//POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7);
GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
-135, -30,
-137, -30,
-137, -31, hole);
GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
-135, -30,
-137, -30,
-137, -31);
GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7, holeInv);
//POLYGON((-135.2 -30.8, -135.2 -30.2, -136.8 -30.2, -136.8 -30.8, -135.2 -30.8)) inside the polygon covering the hole
GeoPolygon polConvex = buildConvexGeoPolygon(-135.2, -30.8,
-135.2, -30.3,
-136.8, -30.2,
-136.8, -30.8);
GeoPolygon polConcave = buildConcaveGeoPolygon(-135.2, -30.8,
-135.2, -30.3,
-136.8, -30.2,
-136.8, -30.8);
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPolInv.getRelationship(polConvex);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConvex.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPolInv.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.OVERLAPS, rel);
}
/**
* Test with two shapes with no crossing edges and all points inside in convex case.
*/
@Test
public void testGeoSimplePolygonWithHole4() {
//POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7);
GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
-135, -30,
-137, -30,
-137, -31, hole);
GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
-135, -30,
-137, -30,
-137, -31);
GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
-135.5, -30.4,
-136.5, -30.4,
-136.5, -30.7, holeInv);
// POLYGON((-135.7 -30.3, -135.7 -30.2, -136 -30.2, -136 -30.3, -135.7 -30.3))inside the polygon
GeoPolygon polConvex = buildConvexGeoPolygon(-135.7, -30.3,
-135.7, -30.2,
-136, -30.2,
-136, -30.3);
GeoPolygon polConcave = buildConcaveGeoPolygon(-135.7, -30.3,
-135.7, -30.2,
-136, -30.2,
-136, -30.3);
int rel = originalConvexPol.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPol.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConvexPolInv.getRelationship(polConvex);
assertEquals(GeoArea.WITHIN, rel);
rel = polConvex.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConvexPolInv.getRelationship(polConcave);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(originalConvexPolInv);
assertEquals(GeoArea.OVERLAPS, rel);
}
@Test
public void testGeoSimplePolygonWithCircle() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoCircle outCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-70), Geo3DUtil.fromDegrees(23), Geo3DUtil.fromDegrees(1));
int rel = originalConvexPol.getRelationship(outCircle);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConcavePol.getRelationship(outCircle);
assertEquals(GeoArea.WITHIN, rel);
rel = originalComplexPol.getRelationship(outCircle);
assertEquals(GeoArea.DISJOINT, rel);
GeoCircle overlapCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61.5), Geo3DUtil.fromDegrees(20), Geo3DUtil.fromDegrees(1));
rel = originalConvexPol.getRelationship(overlapCircle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(overlapCircle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(overlapCircle);
assertEquals(GeoArea.OVERLAPS, rel);
GeoCircle inCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61), Geo3DUtil.fromDegrees(21), Geo3DUtil.fromDegrees(0.1));
rel = originalConvexPol.getRelationship(inCircle);
assertEquals(GeoArea.WITHIN, rel);
rel = originalConcavePol.getRelationship(inCircle);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalComplexPol.getRelationship(inCircle);
assertEquals(GeoArea.WITHIN, rel);
GeoCircle onCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61), Geo3DUtil.fromDegrees(21), Geo3DUtil.fromDegrees(10.));
rel = originalConvexPol.getRelationship(onCircle);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConcavePol.getRelationship(onCircle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(onCircle);
assertEquals(GeoArea.CONTAINS, rel);
}
@Test
public void testGeoSimplePolygonWithBBox() {
//POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
20.119948, -61.655652,
23.207901, -61.453298,
22.820804, -60.257713);
GeoBBox outRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-69),
Geo3DUtil.fromDegrees(-70),
Geo3DUtil.fromDegrees(22),
Geo3DUtil.fromDegrees(23));
int rel = originalConvexPol.getRelationship(outRectangle);
assertEquals(GeoArea.DISJOINT, rel);
rel = outRectangle.getRelationship(originalConvexPol);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalConcavePol.getRelationship(outRectangle);
assertEquals(GeoArea.WITHIN, rel);
rel = originalComplexPol.getRelationship(outRectangle);
assertEquals(GeoArea.DISJOINT, rel);
GeoBBox overlapRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61),
Geo3DUtil.fromDegrees(-62),
Geo3DUtil.fromDegrees(22),
Geo3DUtil.fromDegrees(23));
rel = originalConvexPol.getRelationship(overlapRectangle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = overlapRectangle.getRelationship(originalConvexPol);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalConcavePol.getRelationship(overlapRectangle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(overlapRectangle);
assertEquals(GeoArea.OVERLAPS, rel);
GeoBBox inRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-61),
Geo3DUtil.fromDegrees(-61.1),
Geo3DUtil.fromDegrees(22.5),
Geo3DUtil.fromDegrees(23));
rel = originalConvexPol.getRelationship(inRectangle);
assertEquals(GeoArea.WITHIN, rel);
rel = inRectangle.getRelationship(originalConvexPol);
assertEquals(GeoArea.CONTAINS, rel);
rel = originalConcavePol.getRelationship(inRectangle);
assertEquals(GeoArea.DISJOINT, rel);
rel = originalComplexPol.getRelationship(inRectangle);
assertEquals(GeoArea.WITHIN, rel);
GeoBBox onRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-59),
Geo3DUtil.fromDegrees(-64.1),
Geo3DUtil.fromDegrees(18.5),
Geo3DUtil.fromDegrees(27));
rel = originalConvexPol.getRelationship(onRectangle);
assertEquals(GeoArea.CONTAINS, rel);
rel = onRectangle.getRelationship(originalConvexPol);
assertEquals(GeoArea.WITHIN, rel);
rel = originalConcavePol.getRelationship(onRectangle);
assertEquals(GeoArea.OVERLAPS, rel);
rel = originalComplexPol.getRelationship(onRectangle);
assertEquals(GeoArea.CONTAINS, rel);
}
@Test
public void testGeoSimplePolygonWithComposite() {
GeoShape shape = getCompositeShape();
//POLYGON((-145.8555 -5.13, -145.8540 -5.13, -145.8540 -5.12, -145.8555 -5.12, -145.8555 -5.13))
GeoPolygon polConvex = buildConvexGeoPolygon(-145.8555, -5.13,
-145.8540, -5.13,
-145.8540, -5.12,
-145.8555, -5.12);
GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
-145.8540, -5.13,
-145.8540, -5.12,
-145.8555, -5.12);
int rel = polConvex.getRelationship(shape);
assertEquals(GeoArea.DISJOINT, rel);
rel = polConcave.getRelationship(shape);
assertEquals(GeoArea.WITHIN, rel);
//POLYGON((-145.8555 -5.13, -145.85 -5.13, -145.85 -5.12, -145.8555 -5.12, -145.8555 -5.13))
polConvex = buildConvexGeoPolygon(-145.8555, -5.13,
-145.85, -5.13,
-145.85, -5.12,
-145.8555, -5.12);
polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
-145.85, -5.13,
-145.85, -5.12,
-145.8555, -5.12);
rel = polConvex.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
//POLYGON((-146 -5.18, -145.854 -5.18, -145.854 -5.11, -146 -5.11, -146 -5.18))
//Case overlaping on of the shapes
polConvex = buildConvexGeoPolygon(-146, -5.18,
-145.854, -5.18,
-145.854, -5.11,
-146, -5.11);
polConcave = buildConcaveGeoPolygon(-146, -5.18,
-145.854, -5.18,
-145.854, -5.11,
-146, -5.11);
rel = polConvex.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
rel = polConcave.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
//POLYGON((-145.88 -5.13, -145.87 -5.13, -145.87 -5.12, -145.88 -5.12, -145.88 -5.13))
polConvex = buildConvexGeoPolygon(-145.88, -5.13,
-145.87, -5.13,
-145.87, -5.12,
-145.88, -5.12);
polConcave = buildConcaveGeoPolygon(-145.88, -5.13,
-145.87, -5.13,
-145.87, -5.12,
-145.88, -5.12);
rel = polConvex.getRelationship(shape);
assertEquals(GeoArea.CONTAINS, rel);
rel = polConcave.getRelationship(shape);
assertEquals(GeoArea.OVERLAPS, rel);
}
private GeoPolygon buildConvexGeoPolygon(double lon1, double lat1,
double lon2, double lat2,
double lon3, double lat3,
double lon4, double lat4) {
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
}
private GeoPolygon buildConcaveGeoPolygon(double lon1, double lat1,
double lon2, double lat2,
double lon3, double lat3,
double lon4, double lat4) {
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE, points);
}
private GeoPolygon buildComplexGeoPolygon(double lon1, double lat1,
double lon2, double lat2,
double lon3, double lat3,
double lon4, double lat4) {
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
GeoPolygonFactory.PolygonDescription pd = new GeoPolygonFactory.PolygonDescription(points);
return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, Collections.singletonList(pd));
}
private GeoPolygon buildConvexGeoPolygonWithHole(double lon1, double lat1,
double lon2, double lat2,
double lon3, double lat3,
double lon4, double lat4,
GeoPolygon hole) {
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
//return new GeoConvexPolygon(PlanetModel.SPHERE,points, Collections.singletonList(hole));
return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, Collections.singletonList(hole));
}
private GeoPolygon buildConcaveGeoPolygonWithHole(double lon1, double lat1,
double lon2, double lat2,
double lon3, double lat3,
double lon4, double lat4,
GeoPolygon hole) {
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat1), Geo3DUtil.fromDegrees(lon1));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat2), Geo3DUtil.fromDegrees(lon2));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat3), Geo3DUtil.fromDegrees(lon3));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(lat4), Geo3DUtil.fromDegrees(lon4));
final List<GeoPoint> points = new ArrayList<>();
points.add(point1);
points.add(point2);
points.add(point3);
points.add(point4);
return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE, points, Collections.singletonList(hole));
}
private GeoShape getCompositeShape(){
//MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
//((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17543698881), Geo3DUtil.fromDegrees(-145.790967486));
GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11348060995), Geo3DUtil.fromDegrees(-145.790854979));
GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11339421216), Geo3DUtil.fromDegrees(-145.853073512));
GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17535061936), Geo3DUtil.fromDegrees(-145.853192037));
final List<GeoPoint> points1 = new ArrayList<>();
points1.add(point1);
points1.add(point2);
points1.add(point3);
points1.add(point4);
GeoPolygon pol1 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,points1);
GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17527125408), Geo3DUtil.fromDegrees(-145.8563923));
GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11332154814), Geo3DUtil.fromDegrees(-145.856222168));
GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.11317773171), Geo3DUtil.fromDegrees(-145.918433943));
GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Geo3DUtil.fromDegrees(-5.17512738429), Geo3DUtil.fromDegrees(-145.918610092));
final List<GeoPoint> points2 = new ArrayList<>();
points2.add(point5);
points2.add(point6);
points2.add(point7);
points2.add(point8);
GeoPolygon pol2 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points2);
GeoCompositeMembershipShape composite = new GeoCompositeMembershipShape();
composite.addShape(pol1);
composite.addShape(pol2);
return composite;
}
}

View File

@ -176,9 +176,11 @@ public class AnalyzingSuggesterTest extends LuceneTestCase {
Document nextDoc = lineFile.nextDoc();
String title = nextDoc.getField("title").stringValue();
int randomWeight = random().nextInt(100);
keys.add(new Input(title, randomWeight));
if (!mapping.containsKey(title) || mapping.get(title) < randomWeight) {
mapping.put(title, Long.valueOf(randomWeight));
int maxLen = Math.min(title.length(), 500);
String prefix = title.substring(0, maxLen);
keys.add(new Input(prefix, randomWeight));
if (!mapping.containsKey(prefix) || mapping.get(prefix) < randomWeight) {
mapping.put(prefix, Long.valueOf(randomWeight));
}
}
Analyzer indexAnalyzer = new MockAnalyzer(random());
@ -1252,10 +1254,9 @@ public class AnalyzingSuggesterTest extends LuceneTestCase {
suggester.build(new InputArrayIterator(new Input[] {
new Input(bigString, 7)}));
fail("did not hit expected exception");
} catch (StackOverflowError soe) {
// OK
} catch (IllegalArgumentException iae) {
// expected
assertTrue(iae.getMessage().contains("input automaton is too large"));
}
IOUtils.close(a, tempDir);
}

View File

@ -58,7 +58,7 @@ Upgrade Notes
no longer replaces down replicas for the collection immediately. Instead, replicas are only added
if a node containing them went down while autoAddReplicas was enabled. The params
autoReplicaFailoverBadNodeExpiration and autoReplicaFailoverWorkLoopDelay are no longer used.
2. Deprecations: Enabling/disabling autoAddReplicas cluster wide with the API will be deprecated; use
2. Deprecations: Enabling/disabling autoAddReplicas cluster wide with the API will be deprecated; use
suspend/resume trigger APIs with name='.auto_add_replicas' instead.
New Features
@ -70,6 +70,10 @@ New Features
* SOLR-11046: Add residuals Stream Evaluator (Joel Bernstein)
* SOLR-10858: Make UUIDUpdateProcessorFactory as Runtime URP (Amit Sarkar, noble)
* SOLR-11126: Node level health check handler (Anshum Gupta)
* SOLR-11031: Implement SystemLogListener for autoscaling (ab)
Bug Fixes
@ -89,6 +93,12 @@ Bug Fixes
* SOLR-11011: Assign.buildCoreName can lead to error in creating a new core when legacyCloud=false (Cao Manh Dat)
* SOLR-10944: Get expression fails to return EOF tuple (Susheel Kumar, Joel Bernstein)
* SOLR-6086: Replica is active during autowarming resulting in queries being sent to a replica that
may not have a registered searcher. This causes spikes in response times when adding a replica
in busy clusters. (Ludovic Boutros, Timothy Potter, shalin)
Optimizations
----------------------
@ -130,6 +140,11 @@ Other Changes
* SOLR-11131: Document 'assert' as a command option in bin/solr, and bin/solr.cmd scripts.
(Jason Gerlowski via Anshum Gupta)
* SOLR-11140: Remove unused parameter in (private) SolrMetricManager.prepareCloudPlugins method.
(Omar Abdelnabi via Christine Poerschke)
* SOLR-11187: contrib/ltr TestModelManagerPersistence improvements. (Yuki Yano via Christine Poerschke)
* SOLR-10397: Port 'autoAddReplicas' feature to the autoscaling framework and make it work with non-shared filesystems
(Cao Manh Dat, shalin)
@ -149,6 +164,8 @@ Jetty 9.3.14.v20161028
Upgrading from Solr 6.x
----------------------
* All Trie* numeric and date field types have been deprecated in favor of *Point field types.
* The default response type is now JSON ("wt=json") instead of XML, and line indentation is now on by default
("indent=on"). If you expect the responses to your queries to be returned in the previous format (XML
format, no indentation), you must now you must now explicitly pass in "wt=xml" and "indent=off" as query
@ -268,6 +285,11 @@ Upgrading from Solr 6.x
replaced with 'sourceNode' and 'targetNode' instead. The old names will continue to work for back-compatibility
but they will be removed in 8.0. See SOLR-11068 for more details.
* All deperated methods of ClusterState (except getZkClusterStateVersion())
have been removed. Use DocCollection methods instead.
* SOLR-11023: EnumField has been deprecated in favor of new EnumFieldType.
New Features
----------------------
* SOLR-9857, SOLR-9858: Collect aggregated metrics from nodes and shard leaders in overseer. (ab)
@ -355,6 +377,14 @@ New Features
* SOLR-10282: bin/solr support for enabling Kerberos authentication (Ishan Chattopadhyaya, Jason Gerlowski)
* SOLR-11093: Add support for PointFields for {!graph} query. (yonik)
* SOLR-10845: Add support for PointFields to {!graphTerms} query that is internally
used by some graph traversal streaming expressions. (yonik)
* SOLR-10939: Add support for PointsFields to {!join} query. Joined fields should
also have docValues enabled. (yonik)
Bug Fixes
----------------------
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.
@ -431,6 +461,22 @@ Bug Fixes
* SOLR-11136: Fix solrj XMLResponseParser when nested docs transformer is used with indented XML (hossman)
* SOLR-11130: V2Request in SolrJ should return the correct collection name so that the request is forwarded to the
correct node (noble)
* SOLR-11151: SolrInfoMBeanHandler.getDiff() ADD case non-functional: NPE when a bean value goes from null -> non-null.
(Steve Rowe)
* SOLR-11154: Child documents' return fields now include useDocValuesAsStored fields (Mohammed Sheeri Shaketi Nauage via
Ishan Chattopadhyaya)
* SOLR-11163: Fix contrib/ltr Normalizer persistence after solr core reload or restart.
(Yuki Yano via Christine Poerschke)
* SOLR-11182: A split shard failure on IOException should be logged (Varun Thacker)
* SOLR-10353: TestSQLHandler reproducible failure: No match found for function signature min(<NUMERIC>) (Kevin Risden)
Optimizations
----------------------
@ -451,6 +497,9 @@ Optimizations
* SOLR-10727: Avoid polluting the filter cache for certain types of faceting (typically ranges) when
the base docset is empty. (David Smiley)
* SOLR-11070: Make docValues range queries behave the same as Trie/Point fields for Double/Float Infinity cases
(Tomás Fernández Löbbe, Andrey Kudryavtsev)
Other Changes
----------------------
* SOLR-10236: Removed FieldType.getNumericType(). Use getNumberType() instead. (Tomás Fernández Löbbe)
@ -595,6 +644,44 @@ Other Changes
* SOLR-10494: Make default response format JSON (wt=json), and also indent text responses formats
(indent=on) by default (Trey Grainger & Cassandra Targett via hossman)
* SOLR-10760: Remove trie field types and fields from example schemas. (Steve Rowe)
* SOLR-11056: Add random range query test that compares results across Trie*, *Point and DocValue-only fields
(Tomás Fernández Löbbe)
* SOLR-10926: Increase the odds of randomly choosing point fields in our SolrTestCaseJ4 numeric type randomization.
(hossman, Steve Rowe)
* SOLR-10846: ExternalFileField/FloatFieldSource should throw a clear exception on initialization with
a Points-based keyField, which is not supported. (hossman, Steve Rowe)
* SOLR-11155: /analysis/field and /analysis/document requests should support points fields.
(Jason Gerlowski, Steve Rowe)
* SOLR-10756: Undeprecate ZkStateReader.updateClusterState(), mark as @lucene.internal, and rename to
forciblyRefreshAllClusterStateSlow(). (hossman, shalin, Steve Rowe)
* SOLR-11036: Separately report disk space metrics for solr.data.home and core root directory. (ab)
* SOLR-10919: ord & rord functions give confusing errors with PointFields. (hossman, Steve Rowe)
* SOLR-10847: Provide a clear exception when attempting to use the terms component with points fields.
(hossman, Steve Rowe)
* SOLR-9321: Remove deprecated methods of ClusterState. (Jason Gerlowski, ishan, Cao Manh Dat)
* SOLR-10033: When attempting to facet with facet.mincount=0 over points fields, raise mincount to 1
and log a warning. (Steve Rowe)
* SOLR-11178: Change error handling in AutoScalingHandler to be consistent w/ other APIs (noble)
* SOLR-11023: Added EnumFieldType, a non-Trie-based version of EnumField, and deprecated EnumField
in favor of EnumFieldType. (hossman, Steve Rowe)
* SOLR-10803: Mark all Trie/LegacyNumeric based fields @deprecated in Solr7. (Steve Rowe)
* SOLR-10821: Ref guide documentation for Autoscaling (Noble Paul, Cassandra Targett, shalin)
================== 6.7.0 ==================
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
@ -731,6 +818,11 @@ when using one of Exact*StatsCache (Mikhail Khludnev)
* SOLR-10908: CloudSolrStream.toExpression incorrectly handles fq clauses (Rohit Singh via Erick Erickson)
* SOLR-11198: downconfig downloads empty file as folder (Erick Erickson)
* SOLR-11177: CoreContainer.load needs to send lazily loaded core descriptors to the proper list rather than send
them all to the transient lists. (Erick Erickson) (note, not in 7.0, is in 7.1)
Optimizations
----------------------
* SOLR-10634: JSON Facet API: When a field/terms facet will retrieve all buckets (i.e. limit:-1)
@ -2375,6 +2467,7 @@ Bug Fixes
* SOLR-9391: LBHttpSolrClient.request now correctly returns Rsp.server when
previously skipped servers were successfully tried. (Christine Poerschke)
Optimizations
----------------------

View File

@ -978,7 +978,7 @@ if [[ "$SCRIPT_CMD" == "create" || "$SCRIPT_CMD" == "create_core" || "$SCRIPT_CM
exit 1
fi
if [ "$CREATE_CONFDIR" == "_default" ]; then
if [[ "$CREATE_CONFDIR" == "_default" ]] && ([[ "$CREATE_CONFNAME" == "" ]] || [[ "$CREATE_CONFNAME" == "_default" ]]); then
echo "WARNING: Using _default configset. Data driven schema functionality is enabled by default, which is"
echo " NOT RECOMMENDED for production use."
echo

View File

@ -25,7 +25,9 @@ import org.apache.solr.schema.TrieDateField;
/**
* An analytics wrapper for a multi-valued {@link TrieDateField} with DocValues enabled.
* @deprecated Trie fields are deprecated as of Solr 7.0
*/
@Deprecated
public class DateMultiTrieField extends LongMultiTrieField implements CastingDateValueStream {
public DateMultiTrieField(String fieldName) {

View File

@ -30,7 +30,9 @@ import org.apache.solr.schema.TrieDoubleField;
/**
* An analytics wrapper for a multi-valued {@link TrieDoubleField} with DocValues enabled.
* @deprecated Trie fields are deprecated as of Solr 7.0
*/
@Deprecated
public class DoubleMultiTrieField extends AnalyticsField implements CastingDoubleValueStream {
private SortedSetDocValues docValues;
private int count;

View File

@ -31,7 +31,9 @@ import org.apache.solr.schema.TrieFloatField;
/**
* An analytics wrapper for a multi-valued {@link TrieFloatField} with DocValues enabled.
* @deprecated Trie fields are deprecated as of Solr 7.0
*/
@Deprecated
public class FloatMultiTrieField extends AnalyticsField implements CastingFloatValueStream {
private SortedSetDocValues docValues;
private int count;

View File

@ -32,7 +32,9 @@ import org.apache.solr.schema.TrieIntField;
/**
* An analytics wrapper for a multi-valued {@link TrieIntField} with DocValues enabled.
* @deprecated Trie fields are deprecated as of Solr 7.0
*/
@Deprecated
public class IntMultiTrieField extends AnalyticsField implements CastingIntValueStream {
private SortedSetDocValues docValues;
private int count;

View File

@ -30,7 +30,9 @@ import org.apache.solr.schema.TrieLongField;
/**
* An analytics wrapper for a multi-valued {@link TrieLongField} with DocValues enabled.
* @deprecated Trie fields are deprecated as of Solr 7.0
*/
@Deprecated
public class LongMultiTrieField extends AnalyticsField implements CastingLongValueStream {
private SortedSetDocValues docValues;
private int count;

View File

@ -90,8 +90,8 @@ public class MinMaxNormalizer extends Normalizer {
@Override
public LinkedHashMap<String,Object> paramsToMap() {
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
params.put("min", '"'+Float.toString(min)+'"');
params.put("max", '"'+Float.toString(max)+'"');
params.put("min", Float.toString(min));
params.put("max", Float.toString(max));
return params;
}

View File

@ -82,8 +82,8 @@ public class StandardNormalizer extends Normalizer {
@Override
public LinkedHashMap<String,Object> paramsToMap() {
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
params.put("avg", '"'+Float.toString(avg)+'"');
params.put("std", '"'+Float.toString(std)+'"');
params.put("avg", Float.toString(avg));
params.put("std", Float.toString(std));
return params;
}

View File

@ -40,7 +40,7 @@ public class TestMinMaxNormalizer {
final MinMaxNormalizer mmn = (MinMaxNormalizer)n;
assertEquals(mmn.getMin(), expectedMin, 0.0);
assertEquals(mmn.getMax(), expectedMax, 0.0);
assertEquals("{min=\""+expectedMin+"\", max=\""+expectedMax+"\"}", mmn.paramsToMap().toString());
assertEquals("{min="+expectedMin+", max="+expectedMax+"}", mmn.paramsToMap().toString());
return n;
}
@ -118,4 +118,19 @@ public class TestMinMaxNormalizer {
value = 5;
assertEquals((value - 5f) / (10f - 5f), n.normalize(value), 0.0001);
}
@Test
public void testParamsToMap() {
final MinMaxNormalizer n1 = new MinMaxNormalizer();
n1.setMin(5.0f);
n1.setMax(10.0f);
final Map<String,Object> params = n1.paramsToMap();
final MinMaxNormalizer n2 = (MinMaxNormalizer) Normalizer.getInstance(
new SolrResourceLoader(),
MinMaxNormalizer.class.getName(),
params);
assertEquals(n1.getMin(), n2.getMin(), 1e-6);
assertEquals(n1.getMax(), n2.getMax(), 1e-6);
}
}

View File

@ -40,7 +40,7 @@ public class TestStandardNormalizer {
final StandardNormalizer sn = (StandardNormalizer)n;
assertEquals(sn.getAvg(), expectedAvg, 0.0);
assertEquals(sn.getStd(), expectedStd, 0.0);
assertEquals("{avg=\""+expectedAvg+"\", std=\""+expectedStd+"\"}", sn.paramsToMap().toString());
assertEquals("{avg="+expectedAvg+", std="+expectedStd+"}", sn.paramsToMap().toString());
return n;
}
@ -130,4 +130,19 @@ public class TestStandardNormalizer {
assertEquals((v - 10f) / (1.5f), norm.normalize(v), 0.0001);
}
}
@Test
public void testParamsToMap() {
final StandardNormalizer n1 = new StandardNormalizer();
n1.setAvg(2.0f);
n1.setStd(3.0f);
final Map<String, Object> params = n1.paramsToMap();
final StandardNormalizer n2 = (StandardNormalizer) Normalizer.getInstance(
new SolrResourceLoader(),
StandardNormalizer.class.getName(),
params);
assertEquals(n1.getAvg(), n2.getAvg(), 1e-6);
assertEquals(n1.getStd(), n2.getStd(), 1e-6);
}
}

View File

@ -23,14 +23,15 @@ import org.apache.commons.io.FileUtils;
import org.apache.solr.ltr.TestRerankBase;
import org.apache.solr.ltr.feature.ValueFeature;
import org.apache.solr.ltr.model.LinearModel;
import org.junit.Before;
import org.apache.solr.ltr.store.FeatureStore;
import org.junit.BeforeClass;
import org.junit.Test;
import org.noggit.ObjectBuilder;
public class TestModelManagerPersistence extends TestRerankBase {
@Before
public void init() throws Exception {
@BeforeClass
public static void init() throws Exception {
setupPersistenttest(true);
}
@ -98,24 +99,90 @@ public class TestModelManagerPersistence extends TestRerankBase {
"/responseHeader/status==0");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
"/features/==[]");
assertJQ(ManagedModelStore.REST_END_POINT + "/test-model2",
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/[0]/name=='test-model'");
restTestHarness.reload();
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
"/features/==[]");
assertJQ(ManagedModelStore.REST_END_POINT + "/test-model2",
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/[0]/name=='test-model'");
assertJDelete(ManagedModelStore.REST_END_POINT + "/test-model1",
assertJDelete(ManagedModelStore.REST_END_POINT + "/test-model",
"/responseHeader/status==0");
assertJDelete(ManagedFeatureStore.REST_END_POINT + "/test1",
"/responseHeader/status==0");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
"/features/==[]");
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
restTestHarness.reload();
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
"/features/==[]");
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
}
@Test
public void testFilePersistence() throws Exception {
// check whether models and features are empty
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/==[]");
// load models and features from files
loadFeatures("features-linear.json");
loadModels("linear-model.json");
// check loaded models and features
final String modelName = "6029760550880411648";
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/[0]/name=='"+modelName+"'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[0]/name=='title'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[1]/name=='description'");
// check persistence after reload
restTestHarness.reload();
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/[0]/name=='"+modelName+"'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[0]/name=='title'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[1]/name=='description'");
// check persistence after restart
jetty.stop();
jetty.start();
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/[0]/name=='"+modelName+"'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[0]/name=='title'");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/[1]/name=='description'");
// delete loaded models and features
restTestHarness.delete(ManagedModelStore.REST_END_POINT + "/"+modelName);
restTestHarness.delete(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME);
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/==[]");
// check persistence after reload
restTestHarness.reload();
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/==[]");
// check persistence after restart
jetty.stop();
jetty.start();
assertJQ(ManagedModelStore.REST_END_POINT,
"/models/==[]");
assertJQ(ManagedFeatureStore.REST_END_POINT + "/" + FeatureStore.DEFAULT_FEATURE_STORE_NAME,
"/features/==[]");
}
}

View File

@ -85,6 +85,7 @@
<artifact name="hadoop-hdfs" type="test" ext="jar" maven:classifier="tests" />
</dependency>
<dependency org="org.mortbay.jetty" name="jetty" rev="${/org.mortbay.jetty/jetty}" conf="test.DfsMiniCluster"/>
<dependency org="org.codehaus.janino" name="commons-compiler" rev="${/org.codehaus.janino/commons-compiler}" conf="compile"/>
<dependency org="org.mortbay.jetty" name="jetty-util" rev="${/org.mortbay.jetty/jetty-util}" conf="test.DfsMiniCluster"/>
<dependency org="com.sun.jersey" name="jersey-core" rev="${/com.sun.jersey/jersey-core}" conf="test.DfsMiniCluster"/>
<dependency org="com.sun.jersey" name="jersey-server" rev="${/com.sun.jersey/jersey-server}" conf="test.DfsMiniCluster"/>
@ -143,6 +144,7 @@
<dependency org="org.apache.calcite" name="calcite-core" rev="${/org.apache.calcite/calcite-core}" conf="compile"/>
<dependency org="org.apache.calcite" name="calcite-linq4j" rev="${/org.apache.calcite/calcite-linq4j}" conf="compile"/>
<dependency org="org.apache.calcite.avatica" name="avatica-core" rev="${/org.apache.calcite.avatica/avatica-core}" conf="compile"/>
<dependency org="org.apache.commons" name="commons-lang3" rev="${/org.apache.commons/commons-lang3}" conf="compile"/>
<dependency org="net.hydromatic" name="eigenbase-properties" rev="${/net.hydromatic/eigenbase-properties}" conf="compile"/>
<dependency org="org.codehaus.janino" name="janino" rev="${/org.codehaus.janino/janino}" conf="compile"/>
<dependency org="org.codehaus.janino" name="commons-compiler" rev="${/org.codehaus.janino/commons-compiler}" conf="compile"/>

View File

@ -115,12 +115,12 @@ public class Assign {
public static String assignNode(SolrZkClient client, DocCollection collection) {
// for backward compatibility;
int numReplicas = collection.getReplicas().size();
String coreNodeName = "core_node" + incAndGetId(client, collection.getName(), numReplicas * 20);
int defaultValue = defaultCounterValue(collection, false);
String coreNodeName = "core_node" + incAndGetId(client, collection.getName(), defaultValue);
while (collection.getReplica(coreNodeName) != null) {
// there is wee chance that, the new coreNodeName id not totally unique,
// but this will be guaranteed unique for new collections
coreNodeName = "core_node" + incAndGetId(client, collection.getName(), numReplicas * 20);
coreNodeName = "core_node" + incAndGetId(client, collection.getName(), defaultValue);
}
return coreNodeName;
}
@ -174,18 +174,34 @@ public class Assign {
return String.format(Locale.ROOT, "%s_%s_replica_%s%s", collectionName, shard, type.name().substring(0,1).toLowerCase(Locale.ROOT), replicaNum);
}
public static String buildCoreName(SolrZkClient zkClient, DocCollection collection, String shard, Replica.Type type) {
private static int defaultCounterValue(DocCollection collection, boolean newCollection) {
if (newCollection) return 0;
int defaultValue = collection.getReplicas().size();
if (collection.getReplicationFactor() != null) {
// numReplicas and replicationFactor * numSlices can be not equals,
// in case of many addReplicas or deleteReplicas are executed
defaultValue = Math.max(defaultValue,
collection.getReplicationFactor() * collection.getSlices().size());
}
return defaultValue * 20;
}
public static String buildCoreName(SolrZkClient zkClient, DocCollection collection, String shard, Replica.Type type, boolean newCollection) {
Slice slice = collection.getSlice(shard);
int numReplicas = collection.getReplicas().size();
int replicaNum = incAndGetId(zkClient, collection.getName(), numReplicas * 20);
int defaultValue = defaultCounterValue(collection, newCollection);
int replicaNum = incAndGetId(zkClient, collection.getName(), defaultValue);
String coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
while (existCoreName(coreName, slice)) {
replicaNum = incAndGetId(zkClient, collection.getName(), numReplicas * 20);
replicaNum = incAndGetId(zkClient, collection.getName(), defaultValue);
coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
}
return coreName;
}
public static String buildCoreName(SolrZkClient zkClient, DocCollection collection, String shard, Replica.Type type) {
return buildCoreName(zkClient, collection, shard, type, false);
}
private static boolean existCoreName(String coreName, Slice slice) {
if (slice == null) return false;
for (Replica replica : slice.getReplicas()) {

View File

@ -27,6 +27,7 @@ import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
@ -57,9 +58,9 @@ public class CloudUtil {
log.debug("checkSharedFSFailoverReplaced running for coreNodeName={} baseUrl={}", thisCnn, thisBaseUrl);
// if we see our core node name on a different base url, unload
Map<String,Slice> slicesMap = zkController.getClusterState().getSlicesMap(desc.getCloudDescriptor().getCollectionName());
if (slicesMap != null) {
final DocCollection docCollection = zkController.getClusterState().getCollectionOrNull(desc.getCloudDescriptor().getCollectionName());
if (docCollection != null && docCollection.getSlicesMap() != null) {
Map<String,Slice> slicesMap = docCollection.getSlicesMap();
for (Slice slice : slicesMap.values()) {
for (Replica replica : slice.getReplicas()) {

View File

@ -244,7 +244,7 @@ public class CreateCollectionCmd implements Cmd {
for (ReplicaPosition replicaPosition : replicaPositions) {
String nodeName = replicaPosition.node;
String coreName = Assign.buildCoreName(ocmh.zkStateReader.getZkClient(), zkStateReader.getClusterState().getCollection(collectionName),
replicaPosition.shard, replicaPosition.type);
replicaPosition.shard, replicaPosition.type, true);
log.debug(formatString("Creating core {0} as part of shard {1} of collection {2} on {3}"
, coreName, replicaPosition.shard, collectionName, nodeName));

View File

@ -65,16 +65,10 @@ public class DeleteShardCmd implements Cmd {
String sliceId = message.getStr(ZkStateReader.SHARD_ID_PROP);
log.info("Delete shard invoked");
Slice slice = clusterState.getSlice(collectionName, sliceId);
Slice slice = clusterState.getCollection(collectionName).getSlice(sliceId);
if (slice == null) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"No shard with name " + sliceId + " exists for collection " + collectionName);
if (slice == null) {
if (clusterState.hasCollection(collectionName)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"No shard with name " + sliceId + " exists for collection " + collectionName);
} else {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No collection with the specified name exists: " + collectionName);
}
}
// For now, only allow for deletions of Inactive slices or custom hashes (range==null).
// TODO: Add check for range gaps on Slice deletion
final Slice.State state = slice.getState();

View File

@ -31,6 +31,7 @@ import org.apache.solr.cloud.overseer.OverseerAction;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
@ -498,8 +499,7 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
ZkStateReader zkStateReader = zkController.getZkStateReader();
zkStateReader.forceUpdateCollection(collection);
ClusterState clusterState = zkStateReader.getClusterState();
Replica rep = (clusterState == null) ? null
: clusterState.getReplica(collection, leaderProps.getStr(ZkStateReader.CORE_NODE_NAME_PROP));
Replica rep = getReplica(clusterState, collection, leaderProps.getStr(ZkStateReader.CORE_NODE_NAME_PROP));
if (rep != null && rep.getState() != Replica.State.ACTIVE
&& rep.getState() != Replica.State.RECOVERING) {
log.debug("We have become the leader after core registration but are not in an ACTIVE state - publishing ACTIVE");
@ -507,6 +507,13 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
}
}
}
private Replica getReplica(ClusterState clusterState, String collectionName, String replicaName) {
if (clusterState == null) return null;
final DocCollection docCollection = clusterState.getCollectionOrNull(collectionName);
if (docCollection == null) return null;
return docCollection.getReplica(replicaName);
}
public void checkLIR(String coreName, boolean allReplicasInLine)
throws InterruptedException, KeeperException, IOException {
@ -604,7 +611,8 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
long timeoutAt = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeoutms, TimeUnit.MILLISECONDS);
final String shardsElectZkPath = electionPath + LeaderElector.ELECTION_NODE;
Slice slices = zkController.getClusterState().getSlice(collection, shardId);
DocCollection docCollection = zkController.getClusterState().getCollectionOrNull(collection);
Slice slices = (docCollection == null) ? null : docCollection.getSlice(shardId);
int cnt = 0;
while (!isClosed && !cc.isShutDown()) {
// wait for everyone to be up
@ -649,7 +657,8 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
}
Thread.sleep(500);
slices = zkController.getClusterState().getSlice(collection, shardId);
docCollection = zkController.getClusterState().getCollectionOrNull(collection);
slices = (docCollection == null) ? null : docCollection.getSlice(shardId);
cnt++;
}
return false;
@ -658,9 +667,10 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
// returns true if all replicas are found to be up, false if not
private boolean areAllReplicasParticipating() throws InterruptedException {
final String shardsElectZkPath = electionPath + LeaderElector.ELECTION_NODE;
Slice slices = zkController.getClusterState().getSlice(collection, shardId);
final DocCollection docCollection = zkController.getClusterState().getCollectionOrNull(collection);
if (slices != null) {
if (docCollection != null && docCollection.getSlice(shardId) != null) {
final Slice slices = docCollection.getSlice(shardId);
int found = 0;
try {
found = zkClient.getChildren(shardsElectZkPath, null, true).size();

View File

@ -150,7 +150,7 @@ public class Overseer implements Closeable {
//TODO consider removing 'refreshClusterState' and simply check if clusterState is null
if (refreshClusterState) {
try {
reader.updateClusterState();
reader.forciblyRefreshAllClusterStateSlow();
clusterState = reader.getClusterState();
zkStateWriter = new ZkStateWriter(reader, stats);
refreshClusterState = false;

View File

@ -522,10 +522,9 @@ public class OverseerCollectionMessageHandler implements OverseerMessageHandler
String waitForCoreNodeName(String collectionName, String msgNodeName, String msgCore) {
int retryCount = 320;
while (retryCount-- > 0) {
Map<String,Slice> slicesMap = zkStateReader.getClusterState()
.getSlicesMap(collectionName);
if (slicesMap != null) {
final DocCollection docCollection = zkStateReader.getClusterState().getCollectionOrNull(collectionName);
if (docCollection != null && docCollection.getSlicesMap() != null) {
Map<String,Slice> slicesMap = docCollection.getSlicesMap();
for (Slice slice : slicesMap.values()) {
for (Replica replica : slice.getReplicas()) {
// TODO: for really large clusters, we could 'index' on this

View File

@ -545,8 +545,8 @@ public class RecoveryStrategy implements Runnable, Closeable {
zkController.publish(core.getCoreDescriptor(), Replica.State.RECOVERING);
final Slice slice = zkStateReader.getClusterState().getSlice(cloudDesc.getCollectionName(),
cloudDesc.getShardId());
final Slice slice = zkStateReader.getClusterState().getCollection(cloudDesc.getCollectionName())
.getSlice(cloudDesc.getShardId());
try {
prevSendPreRecoveryHttpUriRequest.abort();

View File

@ -42,6 +42,7 @@ import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@ -95,8 +96,11 @@ import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrCoreInitializationException;
import org.apache.solr.handler.admin.ConfigSetsHandlerApi;
import org.apache.solr.logging.MDCLoggingContext;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.servlet.SolrDispatchFilter;
import org.apache.solr.update.UpdateLog;
import org.apache.solr.util.RTimer;
import org.apache.solr.util.RefCounted;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.ConnectionLossException;
@ -1002,7 +1006,8 @@ public class ZkController {
try {
// If we're a preferred leader, insert ourselves at the head of the queue
boolean joinAtHead = false;
Replica replica = zkStateReader.getClusterState().getReplica(collection, coreZkNodeName);
final DocCollection docCollection = zkStateReader.getClusterState().getCollectionOrNull(collection);
Replica replica = (docCollection == null) ? null : docCollection.getReplica(coreZkNodeName);
if (replica != null) {
joinAtHead = replica.getBool(SliceMutator.PREFERRED_LEADER_PROP, false);
}
@ -1054,7 +1059,7 @@ public class ZkController {
// we will call register again after zk expiration and on reload
if (!afterExpiration && !core.isReloaded() && ulog != null && !isTlogReplicaAndNotLeader) {
// disable recovery in case shard is in construction state (for shard splits)
Slice slice = getClusterState().getSlice(collection, shardId);
Slice slice = getClusterState().getCollection(collection).getSlice(shardId);
if (slice.getState() != Slice.State.CONSTRUCTION || !isLeader) {
Future<UpdateLog.RecoveryInfo> recoveryFuture = core.getUpdateHandler().getUpdateLog().recoverFromLog();
if (recoveryFuture != null) {
@ -1363,8 +1368,11 @@ public class ZkController {
props.put(ZkStateReader.CORE_NODE_NAME_PROP, coreNodeName);
}
try (SolrCore core = cc.getCore(cd.getName())) {
if (core != null && state == Replica.State.ACTIVE) {
ensureRegisteredSearcher(core);
}
if (core != null && core.getDirectoryFactory().isSharedStorage()) {
if (core != null && core.getDirectoryFactory().isSharedStorage()) {
if (core.getDirectoryFactory().isSharedStorage()) {
props.put("dataDir", core.getDataDir());
UpdateLog ulog = core.getUpdateHandler().getUpdateLog();
if (ulog != null) {
@ -1376,7 +1384,7 @@ public class ZkController {
// The core had failed to initialize (in a previous request, not this one), hence nothing to do here.
log.info("The core '{}' had failed to initialize before.", cd.getName());
}
ZkNodeProps m = new ZkNodeProps(props);
if (updateLastState) {
@ -1411,7 +1419,8 @@ public class ZkController {
assert false : "No collection was specified [" + collection + "]";
return;
}
Replica replica = zkStateReader.getClusterState().getReplica(collection, coreNodeName);
final DocCollection docCollection = zkStateReader.getClusterState().getCollectionOrNull(collection);
Replica replica = (docCollection == null) ? null : docCollection.getReplica(coreNodeName);
if (replica == null || replica.getType() != Type.PULL) {
ElectionContext context = electionContexts.remove(new ContextKey(collection, coreNodeName));
@ -1465,10 +1474,10 @@ public class ZkController {
int retryCount = 320;
log.debug("look for our core node name");
while (retryCount-- > 0) {
Map<String, Slice> slicesMap = zkStateReader.getClusterState()
.getSlicesMap(descriptor.getCloudDescriptor().getCollectionName());
if (slicesMap != null) {
final DocCollection docCollection = zkStateReader.getClusterState()
.getCollectionOrNull(descriptor.getCloudDescriptor().getCollectionName());
if (docCollection != null && docCollection.getSlicesMap() != null) {
final Map<String, Slice> slicesMap = docCollection.getSlicesMap();
for (Slice slice : slicesMap.values()) {
for (Replica replica : slice.getReplicas()) {
// TODO: for really large clusters, we could 'index' on this
@ -2576,4 +2585,52 @@ public class ZkController {
log.warn("Could not publish node as down: " + e.getMessage());
}
}
/**
* Ensures that a searcher is registered for the given core and if not, waits until one is registered
*/
private static void ensureRegisteredSearcher(SolrCore core) throws InterruptedException {
if (!core.getSolrConfig().useColdSearcher) {
RefCounted<SolrIndexSearcher> registeredSearcher = core.getRegisteredSearcher();
if (registeredSearcher != null) {
log.debug("Found a registered searcher: {} for core: {}", registeredSearcher.get(), core);
registeredSearcher.decref();
} else {
Future[] waitSearcher = new Future[1];
log.info("No registered searcher found for core: {}, waiting until a searcher is registered before publishing as active", core.getName());
final RTimer timer = new RTimer();
RefCounted<SolrIndexSearcher> searcher = null;
try {
searcher = core.getSearcher(false, true, waitSearcher, true);
boolean success = true;
if (waitSearcher[0] != null) {
log.debug("Waiting for first searcher of core {}, id: {} to be registered", core.getName(), core);
try {
waitSearcher[0].get();
} catch (ExecutionException e) {
log.warn("Wait for a searcher to be registered for core " + core.getName() + ",id: " + core + " failed due to: " + e, e);
success = false;
}
}
if (success) {
if (searcher == null) {
// should never happen
log.debug("Did not find a searcher even after the future callback for core: {}, id: {}!!!", core.getName(), core);
} else {
log.info("Found a registered searcher: {}, took: {} ms for core: {}, id: {}", searcher.get(), timer.getTime(), core.getName(), core);
}
}
} finally {
if (searcher != null) {
searcher.decref();
}
}
}
RefCounted<SolrIndexSearcher> newestSearcher = core.getNewestSearcher(false);
if (newestSearcher != null) {
log.debug("Found newest searcher: {} for core: {}, id: {}", newestSearcher.get(), core.getName(), core);
newestSearcher.decref();
}
}
}
}

View File

@ -262,6 +262,11 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
return currentConfig;
}
private void checkErr(CommandOperation op) {
if (!op.hasError()) return;
throw new ApiBag.ExceptionWithErrObject(SolrException.ErrorCode.BAD_REQUEST, "Error in command payload", CommandOperation.captureErrors(Collections.singletonList(op)));
}
private AutoScalingConfig handleSetClusterPreferences(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
List<Map<String, Object>> preferences = (List<Map<String, Object>>) op.getCommandData();

View File

@ -22,6 +22,7 @@ import static org.apache.solr.common.params.CommonParams.AUTHZ_PATH;
import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.CONFIGSETS_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.CORES_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.HEALTH_CHECK_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.INFO_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.METRICS_PATH;
import static org.apache.solr.common.params.CommonParams.ZK_PATH;
@ -80,6 +81,7 @@ import org.apache.solr.handler.SnapShooter;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.admin.HealthCheckHandler;
import org.apache.solr.handler.admin.InfoHandler;
import org.apache.solr.handler.admin.MetricsCollectorHandler;
import org.apache.solr.handler.admin.MetricsHandler;
@ -136,6 +138,7 @@ public class CoreContainer {
protected CoreAdminHandler coreAdminHandler = null;
protected CollectionsHandler collectionsHandler = null;
protected HealthCheckHandler healthCheckHandler = null;
private InfoHandler infoHandler;
protected ConfigSetsHandler configSetsHandler = null;
@ -524,6 +527,7 @@ public class CoreContainer {
createHandler(ZK_PATH, ZookeeperInfoHandler.class.getName(), ZookeeperInfoHandler.class);
collectionsHandler = createHandler(COLLECTIONS_HANDLER_PATH, cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
healthCheckHandler = createHandler(HEALTH_CHECK_HANDLER_PATH, cfg.getHealthCheckHandlerClass(), HealthCheckHandler.class);
infoHandler = createHandler(INFO_HANDLER_PATH, cfg.getInfoHandlerClass(), InfoHandler.class);
coreAdminHandler = createHandler(CORES_HANDLER_PATH, cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
configSetsHandler = createHandler(CONFIGSETS_HANDLER_PATH, cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
@ -558,10 +562,19 @@ public class CoreContainer {
true, "lazy", SolrInfoBean.Category.CONTAINER.toString(), "cores");
metricManager.registerGauge(null, registryName, () -> solrCores.getAllCoreNames().size() - solrCores.getLoadedCoreNames().size(),
true, "unloaded", SolrInfoBean.Category.CONTAINER.toString(), "cores");
metricManager.registerGauge(null, registryName, () -> cfg.getCoreRootDirectory().toFile().getTotalSpace(),
Path dataHome = cfg.getSolrDataHome() != null ? cfg.getSolrDataHome() : cfg.getCoreRootDirectory();
metricManager.registerGauge(null, registryName, () -> dataHome.toFile().getTotalSpace(),
true, "totalSpace", SolrInfoBean.Category.CONTAINER.toString(), "fs");
metricManager.registerGauge(null, registryName, () -> cfg.getCoreRootDirectory().toFile().getUsableSpace(),
metricManager.registerGauge(null, registryName, () -> dataHome.toFile().getUsableSpace(),
true, "usableSpace", SolrInfoBean.Category.CONTAINER.toString(), "fs");
metricManager.registerGauge(null, registryName, () -> dataHome.toAbsolutePath().toString(),
true, "path", SolrInfoBean.Category.CONTAINER.toString(), "fs");
metricManager.registerGauge(null, registryName, () -> cfg.getCoreRootDirectory().toFile().getTotalSpace(),
true, "totalSpace", SolrInfoBean.Category.CONTAINER.toString(), "fs", "coreRoot");
metricManager.registerGauge(null, registryName, () -> cfg.getCoreRootDirectory().toFile().getUsableSpace(),
true, "usableSpace", SolrInfoBean.Category.CONTAINER.toString(), "fs", "coreRoot");
metricManager.registerGauge(null, registryName, () -> cfg.getCoreRootDirectory().toAbsolutePath().toString(),
true, "path", SolrInfoBean.Category.CONTAINER.toString(), "fs", "coreRoot");
// add version information
metricManager.registerGauge(null, registryName, () -> this.getClass().getPackage().getSpecificationVersion(),
true, "specification", SolrInfoBean.Category.CONTAINER.toString(), "version");
@ -596,7 +609,7 @@ public class CoreContainer {
for (final CoreDescriptor cd : cds) {
if (cd.isTransient() || !cd.isLoadOnStartup()) {
solrCores.getTransientCacheHandler().addTransientDescriptor(cd.getName(), cd);
solrCores.addCoreDescriptor(cd);
} else if (asyncSolrCoreLoad) {
solrCores.markCoreAsLoading(cd);
}
@ -632,7 +645,7 @@ public class CoreContainer {
} finally {
if (asyncSolrCoreLoad && futures != null) {
coreContainerWorkExecutor.submit((Runnable) () -> {
coreContainerWorkExecutor.submit(() -> {
try {
for (Future<SolrCore> future : futures) {
try {
@ -833,7 +846,6 @@ public class CoreContainer {
core.close();
throw new IllegalStateException("This CoreContainer has been closed");
}
solrCores.addCoreDescriptor(cd);
SolrCore old = solrCores.putCore(cd, core);
/*
* set both the name of the descriptor and the name of the
@ -1477,6 +1489,8 @@ public class CoreContainer {
return collectionsHandler;
}
public HealthCheckHandler getHealthCheckHandler() { return healthCheckHandler; }
public InfoHandler getInfoHandler() {
return infoHandler;
}

View File

@ -47,6 +47,8 @@ public class NodeConfig {
private final String collectionsAdminHandlerClass;
private final String healthCheckHandlerClass;
private final String infoHandlerClass;
private final String configSetsHandlerClass;
@ -74,7 +76,7 @@ public class NodeConfig {
private NodeConfig(String nodeName, Path coreRootDirectory, Path solrDataHome, Path configSetBaseDirectory, String sharedLibDirectory,
PluginInfo shardHandlerFactoryConfig, UpdateShardHandlerConfig updateShardHandlerConfig,
String coreAdminHandlerClass, String collectionsAdminHandlerClass,
String infoHandlerClass, String configSetsHandlerClass,
String healthCheckHandlerClass, String infoHandlerClass, String configSetsHandlerClass,
LogWatcherConfig logWatcherConfig, CloudConfig cloudConfig, Integer coreLoadThreads,
int transientCacheSize, boolean useSchemaCache, String managementPath, SolrResourceLoader loader,
Properties solrProperties, PluginInfo[] backupRepositoryPlugins,
@ -88,6 +90,7 @@ public class NodeConfig {
this.updateShardHandlerConfig = updateShardHandlerConfig;
this.coreAdminHandlerClass = coreAdminHandlerClass;
this.collectionsAdminHandlerClass = collectionsAdminHandlerClass;
this.healthCheckHandlerClass = healthCheckHandlerClass;
this.infoHandlerClass = infoHandlerClass;
this.configSetsHandlerClass = configSetsHandlerClass;
this.logWatcherConfig = logWatcherConfig;
@ -146,6 +149,10 @@ public class NodeConfig {
return collectionsAdminHandlerClass;
}
public String getHealthCheckHandlerClass() {
return healthCheckHandlerClass;
}
public String getInfoHandlerClass() {
return infoHandlerClass;
}
@ -209,6 +216,7 @@ public class NodeConfig {
private UpdateShardHandlerConfig updateShardHandlerConfig = UpdateShardHandlerConfig.DEFAULT;
private String coreAdminHandlerClass = DEFAULT_ADMINHANDLERCLASS;
private String collectionsAdminHandlerClass = DEFAULT_COLLECTIONSHANDLERCLASS;
private String healthCheckHandlerClass = DEFAULT_HEALTHCHECKHANDLERCLASS;
private String infoHandlerClass = DEFAULT_INFOHANDLERCLASS;
private String configSetsHandlerClass = DEFAULT_CONFIGSETSHANDLERCLASS;
private LogWatcherConfig logWatcherConfig = new LogWatcherConfig(true, null, null, 50);
@ -236,6 +244,7 @@ public class NodeConfig {
private static final String DEFAULT_ADMINHANDLERCLASS = "org.apache.solr.handler.admin.CoreAdminHandler";
private static final String DEFAULT_INFOHANDLERCLASS = "org.apache.solr.handler.admin.InfoHandler";
private static final String DEFAULT_COLLECTIONSHANDLERCLASS = "org.apache.solr.handler.admin.CollectionsHandler";
private static final String DEFAULT_HEALTHCHECKHANDLERCLASS = "org.apache.solr.handler.admin.HealthCheckHandler";
private static final String DEFAULT_CONFIGSETSHANDLERCLASS = "org.apache.solr.handler.admin.ConfigSetsHandler";
public static final Set<String> DEFAULT_HIDDEN_SYS_PROPS = new HashSet<>(Arrays.asList(
@ -302,6 +311,11 @@ public class NodeConfig {
return this;
}
public NodeConfigBuilder setHealthCheckHandlerClass(String healthCheckHandlerClass) {
this.healthCheckHandlerClass = healthCheckHandlerClass;
return this;
}
public NodeConfigBuilder setInfoHandlerClass(String infoHandlerClass) {
this.infoHandlerClass = infoHandlerClass;
return this;
@ -366,7 +380,7 @@ public class NodeConfig {
public NodeConfig build() {
return new NodeConfig(nodeName, coreRootDirectory, solrDataHome, configSetBaseDirectory, sharedLibDirectory, shardHandlerFactoryConfig,
updateShardHandlerConfig, coreAdminHandlerClass, collectionsAdminHandlerClass, infoHandlerClass, configSetsHandlerClass,
updateShardHandlerConfig, coreAdminHandlerClass, collectionsAdminHandlerClass, healthCheckHandlerClass, infoHandlerClass, configSetsHandlerClass,
logWatcherConfig, cloudConfig, coreLoadThreads, transientCacheSize, useSchemaCache, managementPath, loader, solrProperties,
backupRepositoryPlugins, metricsConfig, transientCacheConfig);
}

View File

@ -241,6 +241,9 @@ public class SolrXmlConfig {
case "collectionsHandler":
builder.setCollectionsAdminHandlerClass(value);
break;
case "healthCheckHandler":
builder.setHealthCheckHandlerClass(value);
break;
case "infoHandler":
builder.setInfoHandlerClass(value);
break;

View File

@ -33,6 +33,7 @@ import org.apache.commons.lang.ArrayUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.BytesTermAttribute;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
@ -248,8 +249,14 @@ public abstract class AnalysisRequestHandlerBase extends RequestHandlerBase {
for (int i = 0; i < tokens.length; i++) {
AttributeSource token = tokens[i];
final NamedList<Object> tokenNamedList = new SimpleOrderedMap<>();
final TermToBytesRefAttribute termAtt = token.getAttribute(TermToBytesRefAttribute.class);
BytesRef rawBytes = termAtt.getBytesRef();
final BytesRef rawBytes;
if (token.hasAttribute(BytesTermAttribute.class)) {
final BytesTermAttribute bytesAtt = token.getAttribute(BytesTermAttribute.class);
rawBytes = bytesAtt.getBytesRef();
} else {
final TermToBytesRefAttribute termAtt = token.getAttribute(TermToBytesRefAttribute.class);
rawBytes = termAtt.getBytesRef();
}
final String text = fieldType.indexedToReadable(rawBytes, new CharsRefBuilder()).toString();
tokenNamedList.add("text", text);

View File

@ -42,6 +42,7 @@ import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.cloud.ZkController;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.ZkCoreNodeProps;
import org.apache.solr.common.cloud.ZkNodeProps;
@ -397,7 +398,8 @@ public class CdcrRequestHandler extends RequestHandlerBase implements SolrCoreAw
log.warn("Error when updating cluster state", e);
}
ClusterState cstate = zkController.getClusterState();
Collection<Slice> shards = cstate.getActiveSlices(collection);
DocCollection docCollection = cstate.getCollectionOrNull(collection);
Collection<Slice> shards = docCollection == null? null : docCollection.getActiveSlices();
ExecutorService parallelExecutor = ExecutorUtil.newMDCAwareCachedThreadPool(new DefaultSolrThreadFactory("parallelCdcrExecutor"));

View File

@ -47,6 +47,7 @@ import org.apache.solr.cloud.ZkController;
import org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.params.CommonParams;
@ -789,8 +790,9 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
List<String> activeReplicaCoreUrls = new ArrayList<>();
ClusterState clusterState = zkController.getZkStateReader().getClusterState();
Set<String> liveNodes = clusterState.getLiveNodes();
Collection<Slice> activeSlices = clusterState.getActiveSlices(collection);
if (activeSlices != null && activeSlices.size() > 0) {
final DocCollection docCollection = clusterState.getCollectionOrNull(collection);
if (docCollection != null && docCollection.getActiveSlices() != null && docCollection.getActiveSlices().size() > 0) {
final Collection<Slice> activeSlices = docCollection.getActiveSlices();
for (Slice next : activeSlices) {
Map<String, Replica> replicasMap = next.getReplicasMap();
if (replicasMap != null) {

View File

@ -200,7 +200,6 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
.withFunctionName("copyOf", CopyOfEvaluator.class)
.withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
.withFunctionName("cov", CovarianceEvaluator.class)
.withFunctionName("cumulativeProbability", CumulativeProbabilityEvaluator.class)
.withFunctionName("describe", DescribeEvaluator.class)
.withFunctionName("distance", DistanceEvaluator.class)
.withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
@ -219,6 +218,12 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
.withFunctionName("addAll", AddAllEvaluator.class)
.withFunctionName("residuals", ResidualsEvaluator.class)
.withFunctionName("plot", PlotStream.class)
.withFunctionName("normalDistribution", NormalDistributionEvaluator.class)
.withFunctionName("uniformDistribution", UniformDistributionEvaluator.class)
.withFunctionName("sample", SampleEvaluator.class)
.withFunctionName("kolmogorovSmirnov", KolmogorovSmirnovEvaluator.class)
.withFunctionName("ks", KolmogorovSmirnovEvaluator.class)
.withFunctionName("asc", AscEvaluator.class)
// Boolean Stream Evaluators
.withFunctionName("and", AndEvaluator.class)

View File

@ -1034,8 +1034,10 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
for (int i = 0; i < numRetries; i++) {
ClusterState clusterState = zkStateReader.getClusterState();
Collection<Slice> shards = clusterState.getSlices(collectionName);
if (shards != null) {
final DocCollection docCollection = clusterState.getCollectionOrNull(collectionName);
if (docCollection != null && docCollection.getSlices() != null) {
Collection<Slice> shards = docCollection.getSlices();
replicaNotAlive = null;
for (Slice shard : shards) {
Collection<Replica> replicas;

View File

@ -0,0 +1,110 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.handler.admin;
import java.lang.invoke.MethodHandles;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.FAILURE;
import static org.apache.solr.common.params.CommonParams.OK;
import static org.apache.solr.common.params.CommonParams.STATUS;
/*
* Health Check Handler for reporting the health of a specific node.
*
* This checks if the node is:
* 1. Connected to zookeeper
* 2. listed in 'live_nodes'.
*/
public class HealthCheckHandler extends RequestHandlerBase {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
CoreContainer coreContainer;
public HealthCheckHandler(final CoreContainer coreContainer) {
super();
this.coreContainer = coreContainer;
}
@Override
final public void init(NamedList args) {
}
public CoreContainer getCoreContainer() {
return this.coreContainer;
}
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
log.debug("Invoked HealthCheckHandler on [{}]", coreContainer.getZkController().getNodeName());
CoreContainer cores = getCoreContainer();
if(cores == null) {
rsp.setException(new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Core container not initialized"));
return;
}
if(!cores.isZooKeeperAware()) {
//TODO: Support standalone instances
rsp.setException(new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Health check is only available when running in SolrCloud mode"));
return;
}
ZkStateReader zkStateReader = cores.getZkController().getZkStateReader();
ClusterState clusterState = zkStateReader.getClusterState();
// Check for isConnected and isClosed
if(zkStateReader.getZkClient().isClosed() || !zkStateReader.getZkClient().isConnected()) {
rsp.add(STATUS, FAILURE);
rsp.setException(new SolrException(SolrException.ErrorCode.SERVICE_UNAVAILABLE, "Host Unavailable: Not connected to zk"));
return;
}
// Set status to true if this node is in live_nodes
if (clusterState.getLiveNodes().contains(cores.getZkController().getNodeName())) {
rsp.add(STATUS, OK);
} else {
rsp.add(STATUS, FAILURE);
rsp.setException(new SolrException(SolrException.ErrorCode.SERVICE_UNAVAILABLE, "Host Unavailable: Not in live nodes as per zk"));
}
rsp.setHttpCaching(false);
return;
}
@Override
public String getDescription() {
return "Health check handler for SolrCloud node";
}
@Override
public Category getCategory() {
return Category.ADMIN;
}
}

View File

@ -209,14 +209,16 @@ public class SolrInfoMBeanHandler extends RequestHandlerBase {
for(int i=0; i<ref.size(); i++) {
String name = ref.getName(i);
Object r = ref.getVal(i);
Object n = now.remove(name);
if(n == null) {
if(r!=null) {
out.add("REMOVE "+name, r);
Object n = now.get(name);
if (n == null) {
if (r != null) {
out.add("REMOVE " + name, r);
now.remove(name);
}
}
else {
out.add(name, diffObject(r,n));
else if (r != null) {
out.add(name, diffObject(r, n));
now.remove(name);
}
}

View File

@ -387,7 +387,7 @@ public class HttpShardHandler extends ShardHandler {
} else {
if (clusterState == null) {
clusterState = zkController.getClusterState();
slices = clusterState.getSlicesMap(cloudDescriptor.getCollectionName());
slices = clusterState.getCollection(cloudDescriptor.getCollectionName()).getSlicesMap();
}
String sliceName = rb.slices[i];

View File

@ -84,6 +84,7 @@ import org.apache.solr.update.IndexFingerprint;
import org.apache.solr.update.PeerSync;
import org.apache.solr.update.UpdateLog;
import org.apache.solr.util.RefCounted;
import org.apache.solr.util.TestInjection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -955,10 +956,15 @@ public class RealTimeGetComponent extends SearchComponent
}
public void processGetFingeprint(ResponseBuilder rb) throws IOException {
TestInjection.injectFailIndexFingerprintRequests();
SolrQueryRequest req = rb.req;
SolrParams params = req.getParams();
long maxVersion = params.getLong("getFingerprint", Long.MAX_VALUE);
if (TestInjection.injectWrongIndexFingerprint()) {
maxVersion = -1;
}
IndexFingerprint fingerprint = IndexFingerprint.getFingerprint(req.getCore(), Math.abs(maxVersion));
rb.rsp.add("fingerprint", fingerprint);
}

View File

@ -78,7 +78,7 @@ public class StatsValuesFactory {
return statsValue;
} else if (StrField.class.isInstance(fieldType)) {
return new StringStatsValues(statsField);
} else if (sf.getType().getClass().equals(EnumField.class)) {
} else if (AbstractEnumField.class.isInstance(fieldType)) {
return new EnumStatsValues(statsField);
} else {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,

View File

@ -108,6 +108,16 @@ public class TermsComponent extends SearchComponent {
rb.rsp.add("terms", termsResult);
if (fields == null || fields.length==0) return;
for (String field : fields) {
FieldType fieldType = rb.req.getSchema().getFieldTypeNoEx(field);
if (null != fieldType) {
if (fieldType.isPointField()) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"The terms component does not support Points-based field " + field);
}
}
}
boolean termStats = params.getBool(TermsParams.TERMS_STATS, false);

View File

@ -1005,8 +1005,7 @@ public class SolrMetricManager {
private List<PluginInfo> prepareCloudPlugins(PluginInfo[] pluginInfos, String group, String className,
Map<String, String> defaultAttributes,
Map<String, Object> defaultInitArgs,
PluginInfo defaultPlugin) {
Map<String, Object> defaultInitArgs) {
List<PluginInfo> result = new ArrayList<>();
if (pluginInfos == null) {
pluginInfos = new PluginInfo[0];
@ -1021,12 +1020,6 @@ public class SolrMetricManager {
result.add(info);
}
}
if (result.isEmpty() && defaultPlugin != null) {
defaultPlugin = preparePlugin(defaultPlugin, className, defaultAttributes, defaultInitArgs);
if (defaultPlugin != null) {
result.add(defaultPlugin);
}
}
return result;
}
@ -1077,7 +1070,7 @@ public class SolrMetricManager {
String registryName = core.getCoreMetricManager().getRegistryName();
// collect infos and normalize
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.shard.toString(), SolrShardReporter.class.getName(),
attrs, initArgs, null);
attrs, initArgs);
for (PluginInfo info : infos) {
try {
SolrMetricReporter reporter = loadReporter(registryName, core.getResourceLoader(), info,
@ -1100,7 +1093,7 @@ public class SolrMetricManager {
Map<String, Object> initArgs = new HashMap<>();
initArgs.put("period", DEFAULT_CLOUD_REPORTER_PERIOD);
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.cluster.toString(), SolrClusterReporter.class.getName(),
attrs, initArgs, null);
attrs, initArgs);
String registryName = getRegistryName(SolrInfoBean.Group.cluster);
for (PluginInfo info : infos) {
try {

View File

@ -407,7 +407,7 @@ final class NumericFacets {
private static NamedList<Integer> getCountsMultiValued(SolrIndexSearcher searcher, DocSet docs, String fieldName, int offset, int limit, int mincount, boolean missing, String sort) throws IOException {
// If facet.mincount=0 with PointFields the only option is to get the values from DocValues
// not currently supported. See SOLR-10033
// not currently supported. See SOLR-11174
mincount = Math.max(mincount, 1);
final SchemaField sf = searcher.getSchema().getField(fieldName);
final FieldType ft = sf.getType();

View File

@ -17,6 +17,7 @@
package org.apache.solr.request;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -93,6 +94,8 @@ import org.apache.solr.search.grouping.GroupingSpecification;
import org.apache.solr.util.BoundedTreeSet;
import org.apache.solr.util.DefaultSolrThreadFactory;
import org.apache.solr.util.RTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.SORT;
@ -103,6 +106,7 @@ import static org.apache.solr.common.params.CommonParams.SORT;
* to leverage any of its functionality.
*/
public class SimpleFacets {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/** The main set of documents all facet counts should be relative to */
protected DocSet docsOrig;
@ -492,10 +496,19 @@ public class SimpleFacets {
+ FacetParams.FACET_CONTAINS + ", "
+ FacetParams.FACET_EXCLUDETERMS + ") are not supported on numeric types");
}
// We should do this, but mincount=0 is currently the default
// if (ft.isPointField() && mincount <= 0) {
// throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_MINCOUNT + " <= 0 is not supported on point types");
// }
if (ft.isPointField() && mincount <= 0) { // default is mincount=0. See SOLR-10033 & SOLR-11174.
String warningMessage
= "Raising facet.mincount from " + mincount + " to 1, because field " + field + " is Points-based.";
LOG.warn(warningMessage);
List<String> warnings = (List<String>)rb.rsp.getResponseHeader().get("warnings");
if (null == warnings) {
warnings = new ArrayList<>();
rb.rsp.getResponseHeader().add("warnings", warnings);
}
warnings.add(warningMessage);
mincount = 1;
}
counts = NumericFacets.getCounts(searcher, docs, field, offset, limit, mincount, missing, sort);
} else {
PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, docs, field, offset, limit, mincount, missing, sort, prefix, termFilter);

Some files were not shown because too many files have changed in this diff Show More