mirror of https://github.com/apache/lucene.git
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:
commit
1f6a94551f
|
@ -210,14 +210,14 @@
|
||||||
++lineNumber;
|
++lineNumber;
|
||||||
if (underSourceHeader) { // This line is either a single source line, or the boundary of a code block
|
if (underSourceHeader) { // This line is either a single source line, or the boundary of a code block
|
||||||
inCodeBlock = blockBoundaryPattern.matcher(it).matches();
|
inCodeBlock = blockBoundaryPattern.matcher(it).matches();
|
||||||
if ( ! blockTitlePattern.matcher(it).matches()) { // Keep underSourceHeader=true
|
if ( ! blockTitlePattern.matcher(it).matches()) {
|
||||||
underSourceHeader = false;
|
underSourceHeader = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inCodeBlock) {
|
if (inCodeBlock) {
|
||||||
inCodeBlock = ! blockBoundaryPattern.matcher(it).matches();
|
inCodeBlock = ! blockBoundaryPattern.matcher(it).matches();
|
||||||
} else {
|
} else {
|
||||||
underSourceHeader = sourceHeaderPattern.matcher(it).matches();
|
underSourceHeader = sourceHeaderPattern.matcher(it).lookingAt();
|
||||||
if ( ! underSourceHeader) {
|
if ( ! underSourceHeader) {
|
||||||
def unescapedSymbolMatcher = unescapedSymbolPattern.matcher(it);
|
def unescapedSymbolMatcher = unescapedSymbolPattern.matcher(it);
|
||||||
if (unescapedSymbolMatcher.find()) {
|
if (unescapedSymbolMatcher.find()) {
|
||||||
|
|
|
@ -14,7 +14,6 @@ Changes in Runtime Behavior
|
||||||
|
|
||||||
|
|
||||||
======================= Lucene 7.1.0 =======================
|
======================= Lucene 7.1.0 =======================
|
||||||
(No Changes)
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
|
|
||||||
|
@ -22,6 +21,20 @@ Optimizations
|
||||||
SortedSetDocValuesFacetCounts and others) builds its map (Robert
|
SortedSetDocValuesFacetCounts and others) builds its map (Robert
|
||||||
Muir, Adrien Grand, Mike McCandless)
|
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 =======================
|
======================= Lucene 7.0.0 =======================
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
|
|
|
@ -61,6 +61,8 @@ import com.ibm.icu.text.Normalizer2;
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final class ICUFoldingFilter extends ICUNormalizer2Filter {
|
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(
|
private static final Normalizer2 normalizer = Normalizer2.getInstance(
|
||||||
ICUFoldingFilter.class.getResourceAsStream("utr30.nrm"),
|
ICUFoldingFilter.class.getResourceAsStream("utr30.nrm"),
|
||||||
"utr30", Normalizer2.Mode.COMPOSE);
|
"utr30", Normalizer2.Mode.COMPOSE);
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package org.apache.lucene.analysis.icu.segmentation;
|
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.lang.UScript;
|
||||||
import com.ibm.icu.text.BreakIterator;
|
import com.ibm.icu.text.BreakIterator;
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ import com.ibm.icu.text.BreakIterator;
|
||||||
*/
|
*/
|
||||||
final class CompositeBreakIterator {
|
final class CompositeBreakIterator {
|
||||||
private final ICUTokenizerConfig config;
|
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 BreakIteratorWrapper rbbi;
|
||||||
private final ScriptIterator scriptIterator;
|
private final ScriptIterator scriptIterator;
|
||||||
|
|
|
@ -60,6 +60,10 @@ public class DefaultICUTokenizerConfig extends ICUTokenizerConfig {
|
||||||
// we keep the cjk breaking separate, thats because it cannot be customized (because dictionary
|
// 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)
|
// 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);
|
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
|
// the same as ROOT, except no dictionary segmentation for cjk
|
||||||
private static final BreakIterator defaultBreakIterator =
|
private static final BreakIterator defaultBreakIterator =
|
||||||
readBreakIterator("Default.brk");
|
readBreakIterator("Default.brk");
|
||||||
|
|
|
@ -33,7 +33,6 @@ import org.apache.lucene.util.IOUtils;
|
||||||
|
|
||||||
import com.ibm.icu.lang.UCharacter;
|
import com.ibm.icu.lang.UCharacter;
|
||||||
import com.ibm.icu.lang.UProperty;
|
import com.ibm.icu.lang.UProperty;
|
||||||
import com.ibm.icu.lang.UScript;
|
|
||||||
import com.ibm.icu.text.BreakIterator;
|
import com.ibm.icu.text.BreakIterator;
|
||||||
import com.ibm.icu.text.RuleBasedBreakIterator;
|
import com.ibm.icu.text.RuleBasedBreakIterator;
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ public class ICUTokenizerFactory extends TokenizerFactory implements ResourceLoa
|
||||||
if (tailored.isEmpty()) {
|
if (tailored.isEmpty()) {
|
||||||
config = new DefaultICUTokenizerConfig(cjkAsWords, myanmarAsWords);
|
config = new DefaultICUTokenizerConfig(cjkAsWords, myanmarAsWords);
|
||||||
} else {
|
} 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()) {
|
for (Map.Entry<Integer,String> entry : tailored.entrySet()) {
|
||||||
int code = entry.getKey();
|
int code = entry.getKey();
|
||||||
String resourcePath = entry.getValue();
|
String resourcePath = entry.getValue();
|
||||||
|
|
|
@ -212,8 +212,6 @@ final class DocumentsWriterPerThreadPool {
|
||||||
state.unlock();
|
state.unlock();
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
freeList.add(state);
|
freeList.add(state);
|
||||||
// In case any thread is waiting, wake one of them up since we just released a thread state:
|
|
||||||
notify();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.lucene.index;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -298,7 +297,7 @@ public class TieredMergePolicy extends MergePolicy {
|
||||||
// call size() once per segment and sort by that:
|
// call size() once per segment and sort by that:
|
||||||
Map<SegmentCommitInfo,Long> sizeInBytes = getSegmentSizes(writer, infos.asList());
|
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
|
// Compute total index bytes & print details about the index
|
||||||
long totIndexBytes = 0;
|
long totIndexBytes = 0;
|
||||||
|
@ -439,9 +438,7 @@ public class TieredMergePolicy extends MergePolicy {
|
||||||
}
|
}
|
||||||
final OneMerge merge = new OneMerge(best);
|
final OneMerge merge = new OneMerge(best);
|
||||||
spec.add(merge);
|
spec.add(merge);
|
||||||
for(SegmentCommitInfo info : merge.segments) {
|
toBeMerged.addAll(merge.segments);
|
||||||
toBeMerged.add(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose(writer)) {
|
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);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(eligible, new SegmentByteSizeDescending(sizeInBytes));
|
eligible.sort(new SegmentByteSizeDescending(sizeInBytes));
|
||||||
|
|
||||||
if (verbose(writer)) {
|
if (verbose(writer)) {
|
||||||
message("eligible=" + eligible, writer);
|
message("eligible=" + eligible, writer);
|
||||||
|
@ -614,7 +611,7 @@ public class TieredMergePolicy extends MergePolicy {
|
||||||
// call size() once per segment and sort by that:
|
// call size() once per segment and sort by that:
|
||||||
Map<SegmentCommitInfo,Long> sizeInBytes = getSegmentSizes(writer, infos.asList());
|
Map<SegmentCommitInfo,Long> sizeInBytes = getSegmentSizes(writer, infos.asList());
|
||||||
|
|
||||||
Collections.sort(eligible, new SegmentByteSizeDescending(sizeInBytes));
|
eligible.sort(new SegmentByteSizeDescending(sizeInBytes));
|
||||||
|
|
||||||
if (verbose(writer)) {
|
if (verbose(writer)) {
|
||||||
message("eligible=" + eligible, writer);
|
message("eligible=" + eligible, writer);
|
||||||
|
|
|
@ -58,6 +58,11 @@ final public class Operations {
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_MAX_DETERMINIZED_STATES = 10000;
|
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() {}
|
private Operations() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1018,7 +1023,7 @@ final public class Operations {
|
||||||
if (a.getNumStates() == 0) {
|
if (a.getNumStates() == 0) {
|
||||||
return true;
|
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.)
|
* there are never transitions to dead states.)
|
||||||
*/
|
*/
|
||||||
// TODO: not great that this is recursive... in theory a
|
// TODO: not great that this is recursive... in theory a
|
||||||
// large automata could exceed java's stack
|
// 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) {
|
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);
|
path.set(state);
|
||||||
int numTransitions = a.initTransition(state, scratch);
|
int numTransitions = a.initTransition(state, scratch);
|
||||||
for(int t=0;t<numTransitions;t++) {
|
for(int t=0;t<numTransitions;t++) {
|
||||||
a.getTransition(state, t, scratch);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1264,7 +1272,7 @@ final public class Operations {
|
||||||
int numStates = a.getNumStates();
|
int numStates = a.getNumStates();
|
||||||
int[] states = new int[numStates];
|
int[] states = new int[numStates];
|
||||||
final BitSet visited = new BitSet(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) {
|
if (upto < states.length) {
|
||||||
// There were dead states
|
// There were dead states
|
||||||
|
@ -1283,14 +1291,19 @@ final public class Operations {
|
||||||
return states;
|
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();
|
Transition t = new Transition();
|
||||||
int count = a.initTransition(state, t);
|
int count = a.initTransition(state, t);
|
||||||
for (int i=0;i<count;i++) {
|
for (int i=0;i<count;i++) {
|
||||||
a.getNextTransition(t);
|
a.getNextTransition(t);
|
||||||
if (!visited.get(t.dest)) {
|
if (!visited.get(t.dest)) {
|
||||||
visited.set(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;
|
states[upto] = state;
|
||||||
|
|
|
@ -542,19 +542,21 @@ public class RegExp {
|
||||||
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
||||||
break;
|
break;
|
||||||
case REGEXP_REPEAT_MIN:
|
case REGEXP_REPEAT_MIN:
|
||||||
a = Operations.repeat(
|
a = exp1.toAutomatonInternal(automata, automaton_provider, maxDeterminizedStates);
|
||||||
exp1.toAutomatonInternal(automata, automaton_provider,
|
int minNumStates = (a.getNumStates() - 1) * min;
|
||||||
maxDeterminizedStates),
|
if (minNumStates > maxDeterminizedStates) {
|
||||||
min);
|
throw new TooComplexToDeterminizeException(a, minNumStates);
|
||||||
|
}
|
||||||
|
a = Operations.repeat(a, min);
|
||||||
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
||||||
break;
|
break;
|
||||||
case REGEXP_REPEAT_MINMAX:
|
case REGEXP_REPEAT_MINMAX:
|
||||||
a = Operations.repeat(
|
a = exp1.toAutomatonInternal(automata, automaton_provider, maxDeterminizedStates);
|
||||||
exp1.toAutomatonInternal(automata, automaton_provider,
|
int minMaxNumStates = (a.getNumStates() - 1) * max;
|
||||||
maxDeterminizedStates),
|
if (minMaxNumStates > maxDeterminizedStates) {
|
||||||
min,
|
throw new TooComplexToDeterminizeException(a, minMaxNumStates);
|
||||||
max);
|
}
|
||||||
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
a = Operations.repeat(a, min, max);
|
||||||
break;
|
break;
|
||||||
case REGEXP_COMPLEMENT:
|
case REGEXP_COMPLEMENT:
|
||||||
a = Operations.complement(
|
a = Operations.complement(
|
||||||
|
|
|
@ -54,7 +54,7 @@ import org.apache.lucene.util.RamUsageEstimator;
|
||||||
/** Represents an finite state machine (FST), using a
|
/** Represents an finite state machine (FST), using a
|
||||||
* compact byte[] format.
|
* compact byte[] format.
|
||||||
* <p> The format is similar to what's used by Morfologik
|
* <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
|
* <p> See the {@link org.apache.lucene.util.fst package
|
||||||
* documentation} for some simple examples.
|
* documentation} for some simple examples.
|
||||||
|
|
|
@ -52,8 +52,7 @@ public class TestOperations extends LuceneTestCase {
|
||||||
for (BytesRef bref : strings) {
|
for (BytesRef bref : strings) {
|
||||||
eachIndividual[i++] = Automata.makeString(bref.utf8ToString());
|
eachIndividual[i++] = Automata.makeString(bref.utf8ToString());
|
||||||
}
|
}
|
||||||
return Operations.determinize(Operations.union(Arrays.asList(eachIndividual)),
|
return Operations.determinize(Operations.union(Arrays.asList(eachIndividual)), DEFAULT_MAX_DETERMINIZED_STATES);
|
||||||
DEFAULT_MAX_DETERMINIZED_STATES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test concatenation with empty language returns empty */
|
/** Test concatenation with empty language returns empty */
|
||||||
|
@ -61,6 +60,7 @@ public class TestOperations extends LuceneTestCase {
|
||||||
Automaton a = Automata.makeString("a");
|
Automaton a = Automata.makeString("a");
|
||||||
Automaton concat = Operations.concatenate(a, Automata.makeEmpty());
|
Automaton concat = Operations.concatenate(a, Automata.makeEmpty());
|
||||||
assertTrue(Operations.isEmpty(concat));
|
assertTrue(Operations.isEmpty(concat));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test optimization to concatenate() with empty String to an NFA */
|
/** 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.
|
* Returns the set of all accepted strings.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,13 +19,6 @@ package org.apache.lucene.util.automaton;
|
||||||
|
|
||||||
import org.apache.lucene.util.LuceneTestCase;
|
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 {
|
public class TestRegExp extends LuceneTestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +47,14 @@ public class TestRegExp extends LuceneTestCase {
|
||||||
assertTrue(expected.getMessage().contains(source));
|
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
|
// LUCENE-6713
|
||||||
public void testSerializeTooManyStatesToDeterminizeExc() throws Exception {
|
public void testSerializeTooManyStatesToDeterminizeExc() throws Exception {
|
||||||
// LUCENE-6046
|
// LUCENE-6046
|
||||||
|
@ -62,16 +63,6 @@ public class TestRegExp extends LuceneTestCase {
|
||||||
new RegExp(source).toAutomaton();
|
new RegExp(source).toAutomaton();
|
||||||
});
|
});
|
||||||
assertTrue(expected.getMessage().contains(source));
|
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
|
// LUCENE-6046
|
||||||
|
|
|
@ -84,15 +84,16 @@ io.netty.netty-all.version = 4.0.36.Final
|
||||||
|
|
||||||
/org.apache.ant/ant = 1.8.2
|
/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.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-core = ${org.apache.calcite.version}
|
||||||
/org.apache.calcite/calcite-linq4j = ${org.apache.calcite.version}
|
/org.apache.calcite/calcite-linq4j = ${org.apache.calcite.version}
|
||||||
|
|
||||||
/org.apache.commons/commons-compress = 1.11
|
/org.apache.commons/commons-compress = 1.11
|
||||||
/org.apache.commons/commons-exec = 1.3
|
/org.apache.commons/commons-exec = 1.3
|
||||||
|
/org.apache.commons/commons-lang3 = 3.6
|
||||||
/org.apache.commons/commons-math3 = 3.4.1
|
/org.apache.commons/commons-math3 = 3.4.1
|
||||||
|
|
||||||
org.apache.curator.version = 2.8.0
|
org.apache.curator.version = 2.8.0
|
||||||
|
|
|
@ -29,12 +29,15 @@ import org.apache.lucene.index.PointValues.IntersectVisitor;
|
||||||
import org.apache.lucene.index.PointValues.Relation;
|
import org.apache.lucene.index.PointValues.Relation;
|
||||||
import org.apache.lucene.search.ConstantScoreScorer;
|
import org.apache.lucene.search.ConstantScoreScorer;
|
||||||
import org.apache.lucene.search.ConstantScoreWeight;
|
import org.apache.lucene.search.ConstantScoreWeight;
|
||||||
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.Scorer;
|
import org.apache.lucene.search.Scorer;
|
||||||
import org.apache.lucene.search.ScorerSupplier;
|
import org.apache.lucene.search.ScorerSupplier;
|
||||||
import org.apache.lucene.search.Weight;
|
import org.apache.lucene.search.Weight;
|
||||||
|
import org.apache.lucene.util.BitSetIterator;
|
||||||
import org.apache.lucene.util.DocIdSetBuilder;
|
import org.apache.lucene.util.DocIdSetBuilder;
|
||||||
|
import org.apache.lucene.util.FixedBitSet;
|
||||||
import org.apache.lucene.util.NumericUtils;
|
import org.apache.lucene.util.NumericUtils;
|
||||||
import org.apache.lucene.util.StringHelper;
|
import org.apache.lucene.util.StringHelper;
|
||||||
|
|
||||||
|
@ -128,75 +131,11 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
LatLonPoint.checkCompatible(fieldInfo);
|
LatLonPoint.checkCompatible(fieldInfo);
|
||||||
|
|
||||||
// matching docids
|
// matching docids
|
||||||
DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
|
DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
|
||||||
final IntersectVisitor visitor =
|
final IntersectVisitor visitor = getIntersectVisitor(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
final Weight weight = this;
|
final Weight weight = this;
|
||||||
return new ScorerSupplier() {
|
return new ScorerSupplier() {
|
||||||
|
|
||||||
|
@ -204,6 +143,19 @@ final class LatLonPointDistanceQuery extends Query {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scorer get(boolean randomAccess) throws IOException {
|
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);
|
values.intersect(visitor);
|
||||||
return new ConstantScoreScorer(weight, score(), result.build().iterator());
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,18 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.search;
|
package org.apache.lucene.search;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.lucene.document.Document;
|
import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.document.LatLonPoint;
|
import org.apache.lucene.document.LatLonPoint;
|
||||||
import org.apache.lucene.geo.BaseGeoPointTestCase;
|
import org.apache.lucene.geo.BaseGeoPointTestCase;
|
||||||
import org.apache.lucene.geo.Polygon;
|
|
||||||
import org.apache.lucene.geo.GeoEncodingUtils;
|
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 {
|
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
|
||||||
|
|
||||||
|
@ -53,4 +60,32 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
|
||||||
protected double quantizeLon(double lonRaw) {
|
protected double quantizeLon(double lonRaw) {
|
||||||
return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea {
|
public interface GeoBBox extends GeoAreaShape, GeoSizeable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand box by specified angle.
|
* Expand box by specified angle.
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.internal
|
* @lucene.internal
|
||||||
*/
|
*/
|
||||||
abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
|
abstract class GeoBaseBBox extends GeoBaseAreaShape implements GeoBBox {
|
||||||
|
|
||||||
/** Construct, given planet model.
|
/** Construct, given planet model.
|
||||||
*@param planetModel is the planet model.
|
*@param planetModel is the planet model.
|
||||||
|
@ -31,42 +31,6 @@ abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
|
||||||
super(planetModel);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public abstract class GeoBaseDistanceShape extends GeoBaseMembershipShape implements GeoDistanceShape {
|
public abstract class GeoBaseDistanceShape extends GeoBaseAreaShape implements GeoDistanceShape {
|
||||||
|
|
||||||
/** Constructor.
|
/** Constructor.
|
||||||
*@param planetModel is the planet model to use.
|
*@param planetModel is the planet model to use.
|
||||||
|
|
|
@ -21,7 +21,7 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.internal
|
* @lucene.internal
|
||||||
*/
|
*/
|
||||||
abstract class GeoBasePolygon extends GeoBaseMembershipShape implements GeoPolygon {
|
abstract class GeoBasePolygon extends GeoBaseAreaShape implements GeoPolygon {
|
||||||
|
|
||||||
/** Constructor.
|
/** Constructor.
|
||||||
*@param planetModel is the planet model to use.
|
*@param planetModel is the planet model to use.
|
||||||
|
|
|
@ -369,6 +369,32 @@ class GeoComplexPolygon extends GeoBasePolygon {
|
||||||
return true;
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
|
@ -492,7 +518,7 @@ class GeoComplexPolygon extends GeoBasePolygon {
|
||||||
if (left != null && left.traverse(edgeIterator, minValue, maxValue) == false) {
|
if (left != null && left.traverse(edgeIterator, minValue, maxValue) == false) {
|
||||||
return 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;
|
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.
|
/** Count the number of verifiable edge crossings.
|
||||||
*/
|
*/
|
||||||
private class LinearCrossingEdgeIterator implements EdgeIterator {
|
private class LinearCrossingEdgeIterator implements EdgeIterator {
|
||||||
|
|
|
@ -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 + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,97 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.spatial3d.geom;
|
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
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public class GeoCompositeMembershipShape implements GeoMembershipShape {
|
public class GeoCompositeMembershipShape extends GeoBaseCompositeMembershipShape<GeoMembershipShape> implements GeoMembershipShape {
|
||||||
/** The list of shapes. */
|
|
||||||
protected final List<GeoMembershipShape> shapes = new ArrayList<GeoMembershipShape>();
|
|
||||||
|
|
||||||
/** Constructor.
|
|
||||||
*/
|
|
||||||
public GeoCompositeMembershipShape() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a shape to the composite.
|
* Constructor.
|
||||||
*@param shape is the shape to add.
|
|
||||||
*/
|
*/
|
||||||
public void addShape(final GeoMembershipShape shape) {
|
public GeoCompositeMembershipShape() {
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,15 +17,20 @@
|
||||||
package org.apache.lucene.spatial3d.geom;
|
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
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public class GeoCompositePolygon extends GeoCompositeMembershipShape implements GeoPolygon {
|
public class GeoCompositePolygon extends GeoBaseCompositeAreaShape<GeoPolygon> implements GeoPolygon {
|
||||||
/** Constructor.
|
/**
|
||||||
|
* Constructor.
|
||||||
*/
|
*/
|
||||||
public GeoCompositePolygon() {
|
public GeoCompositePolygon() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GeoCompositePolygon: {" + shapes + '}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -366,6 +366,27 @@ class GeoConcavePolygon extends GeoBasePolygon {
|
||||||
return false;
|
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.
|
/** A membership implementation representing polygon edges that must apply.
|
||||||
*/
|
*/
|
||||||
protected static class EitherBound implements Membership {
|
protected static class EitherBound implements Membership {
|
||||||
|
|
|
@ -356,6 +356,27 @@ class GeoConvexPolygon extends GeoBasePolygon {
|
||||||
return false;
|
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.
|
/** A membership implementation representing polygon edges that must apply.
|
||||||
*/
|
*/
|
||||||
protected static class EitherBound implements Membership {
|
protected static class EitherBound implements Membership {
|
||||||
|
|
|
@ -154,6 +154,11 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, leftPlane, rightPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -164,7 +169,7 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public int getRelationship(final GeoShape path) {
|
||||||
//System.err.println("getting relationship between "+this+" and "+path);
|
//System.err.println("getting relationship between "+this+" and "+path);
|
||||||
if (path.intersects(plane, planePoints, leftPlane, rightPlane)) {
|
if (intersects(path)) {
|
||||||
//System.err.println(" overlaps");
|
//System.err.println(" overlaps");
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,11 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds);
|
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(final GeoShape geoShape) {
|
||||||
|
return geoShape.intersects(plane, planePoints);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -98,7 +103,7 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
|
||||||
// work with no area endpoints. So we rely entirely on intersections.
|
// work with no area endpoints. So we rely entirely on intersections.
|
||||||
//System.out.println("Got here! latitude="+latitude+" path="+path);
|
//System.out.println("Got here! latitude="+latitude+" path="+path);
|
||||||
|
|
||||||
if (path.intersects(plane, planePoints)) {
|
if (intersects(path)) {
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,11 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane);
|
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(final GeoShape geoShape) {
|
||||||
|
return geoShape.intersects(plane, planePoints, boundingPlane);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -107,7 +112,7 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public int getRelationship(final GeoShape path) {
|
||||||
// Look for intersections.
|
// Look for intersections.
|
||||||
if (path.intersects(plane, planePoints, boundingPlane))
|
if (intersects(path))
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
|
|
||||||
if (path.isWithin(interiorPoint))
|
if (path.isWithin(interiorPoint))
|
||||||
|
|
|
@ -66,6 +66,11 @@ class GeoDegeneratePoint extends GeoPoint implements GeoBBox, GeoCircle {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(GeoShape geoShape) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
bounds.addPoint(this);
|
bounds.addPoint(this);
|
||||||
|
|
|
@ -144,6 +144,11 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane, topPlane, bottomPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -154,7 +159,7 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public int getRelationship(final GeoShape path) {
|
||||||
//System.err.println(this+" relationship to "+path);
|
//System.err.println(this+" relationship to "+path);
|
||||||
if (path.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane)) {
|
if (intersects(path)) {
|
||||||
//System.err.println(" overlaps");
|
//System.err.println(" overlaps");
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance {
|
public interface GeoDistanceShape extends GeoAreaShape, GeoDistance {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute a bound based on a provided distance measure.
|
* Compute a bound based on a provided distance measure.
|
||||||
|
|
|
@ -119,6 +119,12 @@ class GeoLatitudeZone extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds, topPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -127,46 +133,6 @@ class GeoLatitudeZone extends GeoBaseBBox {
|
||||||
.addHorizontalPlane(planetModel, bottomLat, bottomPlane);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane);
|
||||||
|
|
|
@ -127,6 +127,12 @@ class GeoLongitudeSlice extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds, leftPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -138,33 +144,6 @@ class GeoLongitudeSlice extends GeoBaseBBox {
|
||||||
.addPoint(planetModel.SOUTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane);
|
||||||
|
|
|
@ -98,6 +98,12 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds);
|
p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(final GeoShape geoShape) {
|
||||||
|
return
|
||||||
|
geoShape.intersects(bottomPlane, planePoints);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -105,38 +111,6 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
|
||||||
.addHorizontalPlane(planetModel, bottomLat, bottomPlane);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
return distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z);
|
return distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z);
|
||||||
|
|
|
@ -174,6 +174,14 @@ class GeoNorthRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, bottomPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -185,43 +193,6 @@ class GeoNorthRectangle extends GeoBaseBBox {
|
||||||
.addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, leftPlane, rightPlane);
|
||||||
|
|
|
@ -21,6 +21,6 @@ package org.apache.lucene.spatial3d.geom;
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public interface GeoPolygon extends GeoMembershipShape {
|
public interface GeoPolygon extends GeoAreaShape {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,14 @@ class GeoRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -207,43 +215,6 @@ class GeoRectangle extends GeoBaseBBox {
|
||||||
.addPoint(ULHC).addPoint(URHC).addPoint(LLHC).addPoint(LRHC);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, leftPlane, rightPlane);
|
||||||
|
|
|
@ -101,6 +101,11 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds);
|
return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(final GeoShape geoShape) {
|
||||||
|
return geoShape.intersects(topPlane, planePoints);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -108,38 +113,6 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
|
||||||
.addHorizontalPlane(planetModel, topLat, topPlane);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);
|
return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);
|
||||||
|
|
|
@ -172,6 +172,13 @@ class GeoSouthRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -183,42 +190,6 @@ class GeoSouthRectangle extends GeoBaseBBox {
|
||||||
.addPoint(URHC).addPoint(ULHC).addPoint(planetModel.SOUTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, leftPlane, rightPlane);
|
||||||
|
|
|
@ -139,6 +139,14 @@ class GeoStandardCircle extends GeoBaseCircle {
|
||||||
return circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
|
|
@ -288,6 +288,23 @@ class GeoStandardPath extends GeoBasePath {
|
||||||
return false;
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -549,6 +566,17 @@ class GeoStandardPath extends GeoBasePath {
|
||||||
return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
|
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.
|
/** Get the bounds for a segment endpoint.
|
||||||
*@param planetModel is the planet model.
|
*@param planetModel is the planet model.
|
||||||
*@param bounds are the bounds to be modified.
|
*@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);
|
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.
|
/** Get the bounds for a segment endpoint.
|
||||||
*@param planetModel is the planet model.
|
*@param planetModel is the planet model.
|
||||||
*@param bounds are the bounds to be modified.
|
*@param bounds are the bounds to be modified.
|
||||||
|
|
|
@ -165,6 +165,13 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
|
||||||
return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, eitherBound);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -176,7 +183,7 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public int getRelationship(final GeoShape path) {
|
||||||
if (path.intersects(plane, planePoints, eitherBound)) {
|
if (intersects(path)) {
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,14 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -150,30 +158,6 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
|
||||||
.addPoint(planetModel.SOUTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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
|
// Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally
|
||||||
|
|
|
@ -176,6 +176,16 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -187,44 +197,6 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
|
||||||
.addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, eitherBound);
|
||||||
|
|
|
@ -204,6 +204,14 @@ class GeoWideRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -216,44 +224,6 @@ class GeoWideRectangle extends GeoBaseBBox {
|
||||||
.addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, eitherBound);
|
||||||
|
|
|
@ -175,6 +175,13 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
|
||||||
p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane);
|
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
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
@ -186,43 +193,6 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
|
||||||
.addPoint(ULHC).addPoint(URHC).addPoint(planetModel.SOUTH_POLE);
|
.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
|
@Override
|
||||||
protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
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);
|
final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, eitherBound);
|
||||||
|
|
|
@ -66,6 +66,11 @@ class GeoWorld extends GeoBaseBBox {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean intersects(final GeoShape geoShape) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getBounds(Bounds bounds) {
|
public void getBounds(Bounds bounds) {
|
||||||
super.getBounds(bounds);
|
super.getBounds(bounds);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -975,7 +975,7 @@ shape:
|
||||||
points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
|
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.1, -0.5));
|
||||||
points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
|
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);
|
GeoPolygon polygonConcave = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points);
|
||||||
assertEquals(polygon,polygonConcave);
|
assertEquals(polygon,polygonConcave);
|
||||||
}
|
}
|
||||||
|
@ -994,7 +994,7 @@ shape:
|
||||||
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
|
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
|
||||||
GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points);
|
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));
|
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole));
|
||||||
assertEquals(polygon,polygon2);
|
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.0, 0.5));
|
||||||
points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0.5));
|
points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0.5));
|
||||||
points.add(new GeoPoint(PlanetModel.SPHERE, 0.5, 0));
|
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);
|
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points);
|
||||||
assertEquals(polygon,polygon2);
|
assertEquals(polygon,polygon2);
|
||||||
}
|
}
|
||||||
|
@ -1025,7 +1025,7 @@ shape:
|
||||||
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
|
hole_points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
|
||||||
GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,hole_points);
|
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));
|
GeoPolygon polygon2 = GeoPolygonFactory.makeGeoConvexPolygon(PlanetModel.SPHERE,points,Collections.singletonList(hole));
|
||||||
assertEquals(polygon,polygon2);
|
assertEquals(polygon,polygon2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -176,9 +176,11 @@ public class AnalyzingSuggesterTest extends LuceneTestCase {
|
||||||
Document nextDoc = lineFile.nextDoc();
|
Document nextDoc = lineFile.nextDoc();
|
||||||
String title = nextDoc.getField("title").stringValue();
|
String title = nextDoc.getField("title").stringValue();
|
||||||
int randomWeight = random().nextInt(100);
|
int randomWeight = random().nextInt(100);
|
||||||
keys.add(new Input(title, randomWeight));
|
int maxLen = Math.min(title.length(), 500);
|
||||||
if (!mapping.containsKey(title) || mapping.get(title) < randomWeight) {
|
String prefix = title.substring(0, maxLen);
|
||||||
mapping.put(title, Long.valueOf(randomWeight));
|
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());
|
Analyzer indexAnalyzer = new MockAnalyzer(random());
|
||||||
|
@ -1252,10 +1254,9 @@ public class AnalyzingSuggesterTest extends LuceneTestCase {
|
||||||
suggester.build(new InputArrayIterator(new Input[] {
|
suggester.build(new InputArrayIterator(new Input[] {
|
||||||
new Input(bigString, 7)}));
|
new Input(bigString, 7)}));
|
||||||
fail("did not hit expected exception");
|
fail("did not hit expected exception");
|
||||||
} catch (StackOverflowError soe) {
|
|
||||||
// OK
|
|
||||||
} catch (IllegalArgumentException iae) {
|
} catch (IllegalArgumentException iae) {
|
||||||
// expected
|
// expected
|
||||||
|
assertTrue(iae.getMessage().contains("input automaton is too large"));
|
||||||
}
|
}
|
||||||
IOUtils.close(a, tempDir);
|
IOUtils.close(a, tempDir);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ Upgrade Notes
|
||||||
no longer replaces down replicas for the collection immediately. Instead, replicas are only added
|
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
|
if a node containing them went down while autoAddReplicas was enabled. The params
|
||||||
autoReplicaFailoverBadNodeExpiration and autoReplicaFailoverWorkLoopDelay are no longer used.
|
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.
|
suspend/resume trigger APIs with name='.auto_add_replicas' instead.
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
|
@ -70,6 +70,10 @@ New Features
|
||||||
|
|
||||||
* SOLR-11046: Add residuals Stream Evaluator (Joel Bernstein)
|
* 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)
|
* SOLR-11031: Implement SystemLogListener for autoscaling (ab)
|
||||||
|
|
||||||
Bug Fixes
|
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-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
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -130,6 +140,11 @@ Other Changes
|
||||||
* SOLR-11131: Document 'assert' as a command option in bin/solr, and bin/solr.cmd scripts.
|
* SOLR-11131: Document 'assert' as a command option in bin/solr, and bin/solr.cmd scripts.
|
||||||
(Jason Gerlowski via Anshum Gupta)
|
(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
|
* SOLR-10397: Port 'autoAddReplicas' feature to the autoscaling framework and make it work with non-shared filesystems
|
||||||
(Cao Manh Dat, shalin)
|
(Cao Manh Dat, shalin)
|
||||||
|
|
||||||
|
@ -149,6 +164,8 @@ Jetty 9.3.14.v20161028
|
||||||
Upgrading from Solr 6.x
|
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
|
* 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
|
("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
|
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
|
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.
|
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
|
New Features
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-9857, SOLR-9858: Collect aggregated metrics from nodes and shard leaders in overseer. (ab)
|
* 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-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
|
Bug Fixes
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.
|
* 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-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
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -451,6 +497,9 @@ Optimizations
|
||||||
* SOLR-10727: Avoid polluting the filter cache for certain types of faceting (typically ranges) when
|
* SOLR-10727: Avoid polluting the filter cache for certain types of faceting (typically ranges) when
|
||||||
the base docset is empty. (David Smiley)
|
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
|
Other Changes
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-10236: Removed FieldType.getNumericType(). Use getNumberType() instead. (Tomás Fernández Löbbe)
|
* 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
|
* 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)
|
(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 ==================
|
================== 6.7.0 ==================
|
||||||
|
|
||||||
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
|
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-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
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-10634: JSON Facet API: When a field/terms facet will retrieve all buckets (i.e. limit:-1)
|
* 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
|
* SOLR-9391: LBHttpSolrClient.request now correctly returns Rsp.server when
|
||||||
previously skipped servers were successfully tried. (Christine Poerschke)
|
previously skipped servers were successfully tried. (Christine Poerschke)
|
||||||
|
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -978,7 +978,7 @@ if [[ "$SCRIPT_CMD" == "create" || "$SCRIPT_CMD" == "create_core" || "$SCRIPT_CM
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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 "WARNING: Using _default configset. Data driven schema functionality is enabled by default, which is"
|
||||||
echo " NOT RECOMMENDED for production use."
|
echo " NOT RECOMMENDED for production use."
|
||||||
echo
|
echo
|
||||||
|
|
|
@ -25,7 +25,9 @@ import org.apache.solr.schema.TrieDateField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An analytics wrapper for a multi-valued {@link TrieDateField} with DocValues enabled.
|
* 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 class DateMultiTrieField extends LongMultiTrieField implements CastingDateValueStream {
|
||||||
|
|
||||||
public DateMultiTrieField(String fieldName) {
|
public DateMultiTrieField(String fieldName) {
|
||||||
|
|
|
@ -30,7 +30,9 @@ import org.apache.solr.schema.TrieDoubleField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An analytics wrapper for a multi-valued {@link TrieDoubleField} with DocValues enabled.
|
* 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 {
|
public class DoubleMultiTrieField extends AnalyticsField implements CastingDoubleValueStream {
|
||||||
private SortedSetDocValues docValues;
|
private SortedSetDocValues docValues;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
|
@ -31,7 +31,9 @@ import org.apache.solr.schema.TrieFloatField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An analytics wrapper for a multi-valued {@link TrieFloatField} with DocValues enabled.
|
* 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 {
|
public class FloatMultiTrieField extends AnalyticsField implements CastingFloatValueStream {
|
||||||
private SortedSetDocValues docValues;
|
private SortedSetDocValues docValues;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
|
@ -32,7 +32,9 @@ import org.apache.solr.schema.TrieIntField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An analytics wrapper for a multi-valued {@link TrieIntField} with DocValues enabled.
|
* 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 {
|
public class IntMultiTrieField extends AnalyticsField implements CastingIntValueStream {
|
||||||
private SortedSetDocValues docValues;
|
private SortedSetDocValues docValues;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
|
@ -30,7 +30,9 @@ import org.apache.solr.schema.TrieLongField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An analytics wrapper for a multi-valued {@link TrieLongField} with DocValues enabled.
|
* 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 {
|
public class LongMultiTrieField extends AnalyticsField implements CastingLongValueStream {
|
||||||
private SortedSetDocValues docValues;
|
private SortedSetDocValues docValues;
|
||||||
private int count;
|
private int count;
|
||||||
|
|
|
@ -90,8 +90,8 @@ public class MinMaxNormalizer extends Normalizer {
|
||||||
@Override
|
@Override
|
||||||
public LinkedHashMap<String,Object> paramsToMap() {
|
public LinkedHashMap<String,Object> paramsToMap() {
|
||||||
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
|
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
|
||||||
params.put("min", '"'+Float.toString(min)+'"');
|
params.put("min", Float.toString(min));
|
||||||
params.put("max", '"'+Float.toString(max)+'"');
|
params.put("max", Float.toString(max));
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,8 @@ public class StandardNormalizer extends Normalizer {
|
||||||
@Override
|
@Override
|
||||||
public LinkedHashMap<String,Object> paramsToMap() {
|
public LinkedHashMap<String,Object> paramsToMap() {
|
||||||
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
|
final LinkedHashMap<String,Object> params = new LinkedHashMap<>(2, 1.0f);
|
||||||
params.put("avg", '"'+Float.toString(avg)+'"');
|
params.put("avg", Float.toString(avg));
|
||||||
params.put("std", '"'+Float.toString(std)+'"');
|
params.put("std", Float.toString(std));
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class TestMinMaxNormalizer {
|
||||||
final MinMaxNormalizer mmn = (MinMaxNormalizer)n;
|
final MinMaxNormalizer mmn = (MinMaxNormalizer)n;
|
||||||
assertEquals(mmn.getMin(), expectedMin, 0.0);
|
assertEquals(mmn.getMin(), expectedMin, 0.0);
|
||||||
assertEquals(mmn.getMax(), expectedMax, 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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,4 +118,19 @@ public class TestMinMaxNormalizer {
|
||||||
value = 5;
|
value = 5;
|
||||||
assertEquals((value - 5f) / (10f - 5f), n.normalize(value), 0.0001);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class TestStandardNormalizer {
|
||||||
final StandardNormalizer sn = (StandardNormalizer)n;
|
final StandardNormalizer sn = (StandardNormalizer)n;
|
||||||
assertEquals(sn.getAvg(), expectedAvg, 0.0);
|
assertEquals(sn.getAvg(), expectedAvg, 0.0);
|
||||||
assertEquals(sn.getStd(), expectedStd, 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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,4 +130,19 @@ public class TestStandardNormalizer {
|
||||||
assertEquals((v - 10f) / (1.5f), norm.normalize(v), 0.0001);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,14 +23,15 @@ import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.solr.ltr.TestRerankBase;
|
import org.apache.solr.ltr.TestRerankBase;
|
||||||
import org.apache.solr.ltr.feature.ValueFeature;
|
import org.apache.solr.ltr.feature.ValueFeature;
|
||||||
import org.apache.solr.ltr.model.LinearModel;
|
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.junit.Test;
|
||||||
import org.noggit.ObjectBuilder;
|
import org.noggit.ObjectBuilder;
|
||||||
|
|
||||||
public class TestModelManagerPersistence extends TestRerankBase {
|
public class TestModelManagerPersistence extends TestRerankBase {
|
||||||
|
|
||||||
@Before
|
@BeforeClass
|
||||||
public void init() throws Exception {
|
public static void init() throws Exception {
|
||||||
setupPersistenttest(true);
|
setupPersistenttest(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,24 +99,90 @@ public class TestModelManagerPersistence extends TestRerankBase {
|
||||||
"/responseHeader/status==0");
|
"/responseHeader/status==0");
|
||||||
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
|
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
|
||||||
"/features/==[]");
|
"/features/==[]");
|
||||||
assertJQ(ManagedModelStore.REST_END_POINT + "/test-model2",
|
assertJQ(ManagedModelStore.REST_END_POINT,
|
||||||
"/models/[0]/name=='test-model'");
|
"/models/[0]/name=='test-model'");
|
||||||
restTestHarness.reload();
|
restTestHarness.reload();
|
||||||
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
|
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test2",
|
||||||
"/features/==[]");
|
"/features/==[]");
|
||||||
assertJQ(ManagedModelStore.REST_END_POINT + "/test-model2",
|
assertJQ(ManagedModelStore.REST_END_POINT,
|
||||||
"/models/[0]/name=='test-model'");
|
"/models/[0]/name=='test-model'");
|
||||||
|
|
||||||
assertJDelete(ManagedModelStore.REST_END_POINT + "/test-model1",
|
assertJDelete(ManagedModelStore.REST_END_POINT + "/test-model",
|
||||||
"/responseHeader/status==0");
|
"/responseHeader/status==0");
|
||||||
assertJDelete(ManagedFeatureStore.REST_END_POINT + "/test1",
|
assertJDelete(ManagedFeatureStore.REST_END_POINT + "/test1",
|
||||||
"/responseHeader/status==0");
|
"/responseHeader/status==0");
|
||||||
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
|
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
|
||||||
"/features/==[]");
|
"/features/==[]");
|
||||||
|
assertJQ(ManagedModelStore.REST_END_POINT,
|
||||||
|
"/models/==[]");
|
||||||
restTestHarness.reload();
|
restTestHarness.reload();
|
||||||
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
|
assertJQ(ManagedFeatureStore.REST_END_POINT + "/test1",
|
||||||
"/features/==[]");
|
"/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/==[]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
<artifact name="hadoop-hdfs" type="test" ext="jar" maven:classifier="tests" />
|
<artifact name="hadoop-hdfs" type="test" ext="jar" maven:classifier="tests" />
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency org="org.mortbay.jetty" name="jetty" rev="${/org.mortbay.jetty/jetty}" conf="test.DfsMiniCluster"/>
|
<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="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-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"/>
|
<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-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" 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.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="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="janino" rev="${/org.codehaus.janino/janino}" conf="compile"/>
|
||||||
<dependency org="org.codehaus.janino" name="commons-compiler" rev="${/org.codehaus.janino/commons-compiler}" conf="compile"/>
|
<dependency org="org.codehaus.janino" name="commons-compiler" rev="${/org.codehaus.janino/commons-compiler}" conf="compile"/>
|
||||||
|
|
|
@ -115,12 +115,12 @@ public class Assign {
|
||||||
|
|
||||||
public static String assignNode(SolrZkClient client, DocCollection collection) {
|
public static String assignNode(SolrZkClient client, DocCollection collection) {
|
||||||
// for backward compatibility;
|
// for backward compatibility;
|
||||||
int numReplicas = collection.getReplicas().size();
|
int defaultValue = defaultCounterValue(collection, false);
|
||||||
String coreNodeName = "core_node" + incAndGetId(client, collection.getName(), numReplicas * 20);
|
String coreNodeName = "core_node" + incAndGetId(client, collection.getName(), defaultValue);
|
||||||
while (collection.getReplica(coreNodeName) != null) {
|
while (collection.getReplica(coreNodeName) != null) {
|
||||||
// there is wee chance that, the new coreNodeName id not totally unique,
|
// there is wee chance that, the new coreNodeName id not totally unique,
|
||||||
// but this will be guaranteed unique for new collections
|
// 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;
|
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);
|
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);
|
Slice slice = collection.getSlice(shard);
|
||||||
int numReplicas = collection.getReplicas().size();
|
int defaultValue = defaultCounterValue(collection, newCollection);
|
||||||
int replicaNum = incAndGetId(zkClient, collection.getName(), numReplicas * 20);
|
int replicaNum = incAndGetId(zkClient, collection.getName(), defaultValue);
|
||||||
String coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
|
String coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
|
||||||
while (existCoreName(coreName, slice)) {
|
while (existCoreName(coreName, slice)) {
|
||||||
replicaNum = incAndGetId(zkClient, collection.getName(), numReplicas * 20);
|
replicaNum = incAndGetId(zkClient, collection.getName(), defaultValue);
|
||||||
coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
|
coreName = buildCoreName(collection.getName(), shard, type, replicaNum);
|
||||||
}
|
}
|
||||||
return coreName;
|
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) {
|
private static boolean existCoreName(String coreName, Slice slice) {
|
||||||
if (slice == null) return false;
|
if (slice == null) return false;
|
||||||
for (Replica replica : slice.getReplicas()) {
|
for (Replica replica : slice.getReplicas()) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
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.Replica;
|
||||||
import org.apache.solr.common.cloud.Slice;
|
import org.apache.solr.common.cloud.Slice;
|
||||||
import org.apache.solr.common.cloud.SolrZkClient;
|
import org.apache.solr.common.cloud.SolrZkClient;
|
||||||
|
@ -57,9 +58,9 @@ public class CloudUtil {
|
||||||
log.debug("checkSharedFSFailoverReplaced running for coreNodeName={} baseUrl={}", thisCnn, thisBaseUrl);
|
log.debug("checkSharedFSFailoverReplaced running for coreNodeName={} baseUrl={}", thisCnn, thisBaseUrl);
|
||||||
|
|
||||||
// if we see our core node name on a different base url, unload
|
// if we see our core node name on a different base url, unload
|
||||||
Map<String,Slice> slicesMap = zkController.getClusterState().getSlicesMap(desc.getCloudDescriptor().getCollectionName());
|
final DocCollection docCollection = zkController.getClusterState().getCollectionOrNull(desc.getCloudDescriptor().getCollectionName());
|
||||||
|
if (docCollection != null && docCollection.getSlicesMap() != null) {
|
||||||
if (slicesMap != null) {
|
Map<String,Slice> slicesMap = docCollection.getSlicesMap();
|
||||||
for (Slice slice : slicesMap.values()) {
|
for (Slice slice : slicesMap.values()) {
|
||||||
for (Replica replica : slice.getReplicas()) {
|
for (Replica replica : slice.getReplicas()) {
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ public class CreateCollectionCmd implements Cmd {
|
||||||
for (ReplicaPosition replicaPosition : replicaPositions) {
|
for (ReplicaPosition replicaPosition : replicaPositions) {
|
||||||
String nodeName = replicaPosition.node;
|
String nodeName = replicaPosition.node;
|
||||||
String coreName = Assign.buildCoreName(ocmh.zkStateReader.getZkClient(), zkStateReader.getClusterState().getCollection(collectionName),
|
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}"
|
log.debug(formatString("Creating core {0} as part of shard {1} of collection {2} on {3}"
|
||||||
, coreName, replicaPosition.shard, collectionName, nodeName));
|
, coreName, replicaPosition.shard, collectionName, nodeName));
|
||||||
|
|
||||||
|
|
|
@ -65,16 +65,10 @@ public class DeleteShardCmd implements Cmd {
|
||||||
String sliceId = message.getStr(ZkStateReader.SHARD_ID_PROP);
|
String sliceId = message.getStr(ZkStateReader.SHARD_ID_PROP);
|
||||||
|
|
||||||
log.info("Delete shard invoked");
|
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).
|
// For now, only allow for deletions of Inactive slices or custom hashes (range==null).
|
||||||
// TODO: Add check for range gaps on Slice deletion
|
// TODO: Add check for range gaps on Slice deletion
|
||||||
final Slice.State state = slice.getState();
|
final Slice.State state = slice.getState();
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.solr.cloud.overseer.OverseerAction;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
import org.apache.solr.common.cloud.ClusterState;
|
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.Replica;
|
||||||
import org.apache.solr.common.cloud.Slice;
|
import org.apache.solr.common.cloud.Slice;
|
||||||
import org.apache.solr.common.cloud.SolrZkClient;
|
import org.apache.solr.common.cloud.SolrZkClient;
|
||||||
|
@ -498,8 +499,7 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
|
||||||
ZkStateReader zkStateReader = zkController.getZkStateReader();
|
ZkStateReader zkStateReader = zkController.getZkStateReader();
|
||||||
zkStateReader.forceUpdateCollection(collection);
|
zkStateReader.forceUpdateCollection(collection);
|
||||||
ClusterState clusterState = zkStateReader.getClusterState();
|
ClusterState clusterState = zkStateReader.getClusterState();
|
||||||
Replica rep = (clusterState == null) ? null
|
Replica rep = getReplica(clusterState, collection, leaderProps.getStr(ZkStateReader.CORE_NODE_NAME_PROP));
|
||||||
: clusterState.getReplica(collection, leaderProps.getStr(ZkStateReader.CORE_NODE_NAME_PROP));
|
|
||||||
if (rep != null && rep.getState() != Replica.State.ACTIVE
|
if (rep != null && rep.getState() != Replica.State.ACTIVE
|
||||||
&& rep.getState() != Replica.State.RECOVERING) {
|
&& rep.getState() != Replica.State.RECOVERING) {
|
||||||
log.debug("We have become the leader after core registration but are not in an ACTIVE state - publishing ACTIVE");
|
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)
|
public void checkLIR(String coreName, boolean allReplicasInLine)
|
||||||
throws InterruptedException, KeeperException, IOException {
|
throws InterruptedException, KeeperException, IOException {
|
||||||
|
@ -604,7 +611,8 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
|
||||||
long timeoutAt = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeoutms, TimeUnit.MILLISECONDS);
|
long timeoutAt = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeoutms, TimeUnit.MILLISECONDS);
|
||||||
final String shardsElectZkPath = electionPath + LeaderElector.ELECTION_NODE;
|
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;
|
int cnt = 0;
|
||||||
while (!isClosed && !cc.isShutDown()) {
|
while (!isClosed && !cc.isShutDown()) {
|
||||||
// wait for everyone to be up
|
// wait for everyone to be up
|
||||||
|
@ -649,7 +657,8 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(500);
|
Thread.sleep(500);
|
||||||
slices = zkController.getClusterState().getSlice(collection, shardId);
|
docCollection = zkController.getClusterState().getCollectionOrNull(collection);
|
||||||
|
slices = (docCollection == null) ? null : docCollection.getSlice(shardId);
|
||||||
cnt++;
|
cnt++;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -658,9 +667,10 @@ final class ShardLeaderElectionContext extends ShardLeaderElectionContextBase {
|
||||||
// returns true if all replicas are found to be up, false if not
|
// returns true if all replicas are found to be up, false if not
|
||||||
private boolean areAllReplicasParticipating() throws InterruptedException {
|
private boolean areAllReplicasParticipating() throws InterruptedException {
|
||||||
final String shardsElectZkPath = electionPath + LeaderElector.ELECTION_NODE;
|
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;
|
int found = 0;
|
||||||
try {
|
try {
|
||||||
found = zkClient.getChildren(shardsElectZkPath, null, true).size();
|
found = zkClient.getChildren(shardsElectZkPath, null, true).size();
|
||||||
|
|
|
@ -150,7 +150,7 @@ public class Overseer implements Closeable {
|
||||||
//TODO consider removing 'refreshClusterState' and simply check if clusterState is null
|
//TODO consider removing 'refreshClusterState' and simply check if clusterState is null
|
||||||
if (refreshClusterState) {
|
if (refreshClusterState) {
|
||||||
try {
|
try {
|
||||||
reader.updateClusterState();
|
reader.forciblyRefreshAllClusterStateSlow();
|
||||||
clusterState = reader.getClusterState();
|
clusterState = reader.getClusterState();
|
||||||
zkStateWriter = new ZkStateWriter(reader, stats);
|
zkStateWriter = new ZkStateWriter(reader, stats);
|
||||||
refreshClusterState = false;
|
refreshClusterState = false;
|
||||||
|
|
|
@ -522,10 +522,9 @@ public class OverseerCollectionMessageHandler implements OverseerMessageHandler
|
||||||
String waitForCoreNodeName(String collectionName, String msgNodeName, String msgCore) {
|
String waitForCoreNodeName(String collectionName, String msgNodeName, String msgCore) {
|
||||||
int retryCount = 320;
|
int retryCount = 320;
|
||||||
while (retryCount-- > 0) {
|
while (retryCount-- > 0) {
|
||||||
Map<String,Slice> slicesMap = zkStateReader.getClusterState()
|
final DocCollection docCollection = zkStateReader.getClusterState().getCollectionOrNull(collectionName);
|
||||||
.getSlicesMap(collectionName);
|
if (docCollection != null && docCollection.getSlicesMap() != null) {
|
||||||
if (slicesMap != null) {
|
Map<String,Slice> slicesMap = docCollection.getSlicesMap();
|
||||||
|
|
||||||
for (Slice slice : slicesMap.values()) {
|
for (Slice slice : slicesMap.values()) {
|
||||||
for (Replica replica : slice.getReplicas()) {
|
for (Replica replica : slice.getReplicas()) {
|
||||||
// TODO: for really large clusters, we could 'index' on this
|
// TODO: for really large clusters, we could 'index' on this
|
||||||
|
|
|
@ -545,8 +545,8 @@ public class RecoveryStrategy implements Runnable, Closeable {
|
||||||
zkController.publish(core.getCoreDescriptor(), Replica.State.RECOVERING);
|
zkController.publish(core.getCoreDescriptor(), Replica.State.RECOVERING);
|
||||||
|
|
||||||
|
|
||||||
final Slice slice = zkStateReader.getClusterState().getSlice(cloudDesc.getCollectionName(),
|
final Slice slice = zkStateReader.getClusterState().getCollection(cloudDesc.getCollectionName())
|
||||||
cloudDesc.getShardId());
|
.getSlice(cloudDesc.getShardId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
prevSendPreRecoveryHttpUriRequest.abort();
|
prevSendPreRecoveryHttpUriRequest.abort();
|
||||||
|
|
|
@ -42,6 +42,7 @@ import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.core.SolrCoreInitializationException;
|
||||||
import org.apache.solr.handler.admin.ConfigSetsHandlerApi;
|
import org.apache.solr.handler.admin.ConfigSetsHandlerApi;
|
||||||
import org.apache.solr.logging.MDCLoggingContext;
|
import org.apache.solr.logging.MDCLoggingContext;
|
||||||
|
import org.apache.solr.search.SolrIndexSearcher;
|
||||||
import org.apache.solr.servlet.SolrDispatchFilter;
|
import org.apache.solr.servlet.SolrDispatchFilter;
|
||||||
import org.apache.solr.update.UpdateLog;
|
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.CreateMode;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
import org.apache.zookeeper.KeeperException.ConnectionLossException;
|
import org.apache.zookeeper.KeeperException.ConnectionLossException;
|
||||||
|
@ -1002,7 +1006,8 @@ public class ZkController {
|
||||||
try {
|
try {
|
||||||
// If we're a preferred leader, insert ourselves at the head of the queue
|
// If we're a preferred leader, insert ourselves at the head of the queue
|
||||||
boolean joinAtHead = false;
|
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) {
|
if (replica != null) {
|
||||||
joinAtHead = replica.getBool(SliceMutator.PREFERRED_LEADER_PROP, false);
|
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
|
// we will call register again after zk expiration and on reload
|
||||||
if (!afterExpiration && !core.isReloaded() && ulog != null && !isTlogReplicaAndNotLeader) {
|
if (!afterExpiration && !core.isReloaded() && ulog != null && !isTlogReplicaAndNotLeader) {
|
||||||
// disable recovery in case shard is in construction state (for shard splits)
|
// 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) {
|
if (slice.getState() != Slice.State.CONSTRUCTION || !isLeader) {
|
||||||
Future<UpdateLog.RecoveryInfo> recoveryFuture = core.getUpdateHandler().getUpdateLog().recoverFromLog();
|
Future<UpdateLog.RecoveryInfo> recoveryFuture = core.getUpdateHandler().getUpdateLog().recoverFromLog();
|
||||||
if (recoveryFuture != null) {
|
if (recoveryFuture != null) {
|
||||||
|
@ -1363,8 +1368,11 @@ public class ZkController {
|
||||||
props.put(ZkStateReader.CORE_NODE_NAME_PROP, coreNodeName);
|
props.put(ZkStateReader.CORE_NODE_NAME_PROP, coreNodeName);
|
||||||
}
|
}
|
||||||
try (SolrCore core = cc.getCore(cd.getName())) {
|
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 != null && core.getDirectoryFactory().isSharedStorage()) {
|
if (core.getDirectoryFactory().isSharedStorage()) {
|
||||||
props.put("dataDir", core.getDataDir());
|
props.put("dataDir", core.getDataDir());
|
||||||
UpdateLog ulog = core.getUpdateHandler().getUpdateLog();
|
UpdateLog ulog = core.getUpdateHandler().getUpdateLog();
|
||||||
if (ulog != null) {
|
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.
|
// 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());
|
log.info("The core '{}' had failed to initialize before.", cd.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
ZkNodeProps m = new ZkNodeProps(props);
|
ZkNodeProps m = new ZkNodeProps(props);
|
||||||
|
|
||||||
if (updateLastState) {
|
if (updateLastState) {
|
||||||
|
@ -1411,7 +1419,8 @@ public class ZkController {
|
||||||
assert false : "No collection was specified [" + collection + "]";
|
assert false : "No collection was specified [" + collection + "]";
|
||||||
return;
|
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) {
|
if (replica == null || replica.getType() != Type.PULL) {
|
||||||
ElectionContext context = electionContexts.remove(new ContextKey(collection, coreNodeName));
|
ElectionContext context = electionContexts.remove(new ContextKey(collection, coreNodeName));
|
||||||
|
@ -1465,10 +1474,10 @@ public class ZkController {
|
||||||
int retryCount = 320;
|
int retryCount = 320;
|
||||||
log.debug("look for our core node name");
|
log.debug("look for our core node name");
|
||||||
while (retryCount-- > 0) {
|
while (retryCount-- > 0) {
|
||||||
Map<String, Slice> slicesMap = zkStateReader.getClusterState()
|
final DocCollection docCollection = zkStateReader.getClusterState()
|
||||||
.getSlicesMap(descriptor.getCloudDescriptor().getCollectionName());
|
.getCollectionOrNull(descriptor.getCloudDescriptor().getCollectionName());
|
||||||
if (slicesMap != null) {
|
if (docCollection != null && docCollection.getSlicesMap() != null) {
|
||||||
|
final Map<String, Slice> slicesMap = docCollection.getSlicesMap();
|
||||||
for (Slice slice : slicesMap.values()) {
|
for (Slice slice : slicesMap.values()) {
|
||||||
for (Replica replica : slice.getReplicas()) {
|
for (Replica replica : slice.getReplicas()) {
|
||||||
// TODO: for really large clusters, we could 'index' on this
|
// 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());
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,6 +262,11 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
|
||||||
return currentConfig;
|
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,
|
private AutoScalingConfig handleSetClusterPreferences(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op,
|
||||||
AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
|
AutoScalingConfig currentConfig) throws KeeperException, InterruptedException, IOException {
|
||||||
List<Map<String, Object>> preferences = (List<Map<String, Object>>) op.getCommandData();
|
List<Map<String, Object>> preferences = (List<Map<String, Object>>) op.getCommandData();
|
||||||
|
|
|
@ -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.COLLECTIONS_HANDLER_PATH;
|
||||||
import static org.apache.solr.common.params.CommonParams.CONFIGSETS_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.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.INFO_HANDLER_PATH;
|
||||||
import static org.apache.solr.common.params.CommonParams.METRICS_PATH;
|
import static org.apache.solr.common.params.CommonParams.METRICS_PATH;
|
||||||
import static org.apache.solr.common.params.CommonParams.ZK_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.CollectionsHandler;
|
||||||
import org.apache.solr.handler.admin.ConfigSetsHandler;
|
import org.apache.solr.handler.admin.ConfigSetsHandler;
|
||||||
import org.apache.solr.handler.admin.CoreAdminHandler;
|
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.InfoHandler;
|
||||||
import org.apache.solr.handler.admin.MetricsCollectorHandler;
|
import org.apache.solr.handler.admin.MetricsCollectorHandler;
|
||||||
import org.apache.solr.handler.admin.MetricsHandler;
|
import org.apache.solr.handler.admin.MetricsHandler;
|
||||||
|
@ -136,6 +138,7 @@ public class CoreContainer {
|
||||||
|
|
||||||
protected CoreAdminHandler coreAdminHandler = null;
|
protected CoreAdminHandler coreAdminHandler = null;
|
||||||
protected CollectionsHandler collectionsHandler = null;
|
protected CollectionsHandler collectionsHandler = null;
|
||||||
|
protected HealthCheckHandler healthCheckHandler = null;
|
||||||
|
|
||||||
private InfoHandler infoHandler;
|
private InfoHandler infoHandler;
|
||||||
protected ConfigSetsHandler configSetsHandler = null;
|
protected ConfigSetsHandler configSetsHandler = null;
|
||||||
|
@ -524,6 +527,7 @@ public class CoreContainer {
|
||||||
|
|
||||||
createHandler(ZK_PATH, ZookeeperInfoHandler.class.getName(), ZookeeperInfoHandler.class);
|
createHandler(ZK_PATH, ZookeeperInfoHandler.class.getName(), ZookeeperInfoHandler.class);
|
||||||
collectionsHandler = createHandler(COLLECTIONS_HANDLER_PATH, cfg.getCollectionsHandlerClass(), CollectionsHandler.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);
|
infoHandler = createHandler(INFO_HANDLER_PATH, cfg.getInfoHandlerClass(), InfoHandler.class);
|
||||||
coreAdminHandler = createHandler(CORES_HANDLER_PATH, cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
|
coreAdminHandler = createHandler(CORES_HANDLER_PATH, cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
|
||||||
configSetsHandler = createHandler(CONFIGSETS_HANDLER_PATH, cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
|
configSetsHandler = createHandler(CONFIGSETS_HANDLER_PATH, cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
|
||||||
|
@ -558,10 +562,19 @@ public class CoreContainer {
|
||||||
true, "lazy", SolrInfoBean.Category.CONTAINER.toString(), "cores");
|
true, "lazy", SolrInfoBean.Category.CONTAINER.toString(), "cores");
|
||||||
metricManager.registerGauge(null, registryName, () -> solrCores.getAllCoreNames().size() - solrCores.getLoadedCoreNames().size(),
|
metricManager.registerGauge(null, registryName, () -> solrCores.getAllCoreNames().size() - solrCores.getLoadedCoreNames().size(),
|
||||||
true, "unloaded", SolrInfoBean.Category.CONTAINER.toString(), "cores");
|
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");
|
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");
|
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
|
// add version information
|
||||||
metricManager.registerGauge(null, registryName, () -> this.getClass().getPackage().getSpecificationVersion(),
|
metricManager.registerGauge(null, registryName, () -> this.getClass().getPackage().getSpecificationVersion(),
|
||||||
true, "specification", SolrInfoBean.Category.CONTAINER.toString(), "version");
|
true, "specification", SolrInfoBean.Category.CONTAINER.toString(), "version");
|
||||||
|
@ -596,7 +609,7 @@ public class CoreContainer {
|
||||||
|
|
||||||
for (final CoreDescriptor cd : cds) {
|
for (final CoreDescriptor cd : cds) {
|
||||||
if (cd.isTransient() || !cd.isLoadOnStartup()) {
|
if (cd.isTransient() || !cd.isLoadOnStartup()) {
|
||||||
solrCores.getTransientCacheHandler().addTransientDescriptor(cd.getName(), cd);
|
solrCores.addCoreDescriptor(cd);
|
||||||
} else if (asyncSolrCoreLoad) {
|
} else if (asyncSolrCoreLoad) {
|
||||||
solrCores.markCoreAsLoading(cd);
|
solrCores.markCoreAsLoading(cd);
|
||||||
}
|
}
|
||||||
|
@ -632,7 +645,7 @@ public class CoreContainer {
|
||||||
} finally {
|
} finally {
|
||||||
if (asyncSolrCoreLoad && futures != null) {
|
if (asyncSolrCoreLoad && futures != null) {
|
||||||
|
|
||||||
coreContainerWorkExecutor.submit((Runnable) () -> {
|
coreContainerWorkExecutor.submit(() -> {
|
||||||
try {
|
try {
|
||||||
for (Future<SolrCore> future : futures) {
|
for (Future<SolrCore> future : futures) {
|
||||||
try {
|
try {
|
||||||
|
@ -833,7 +846,6 @@ public class CoreContainer {
|
||||||
core.close();
|
core.close();
|
||||||
throw new IllegalStateException("This CoreContainer has been closed");
|
throw new IllegalStateException("This CoreContainer has been closed");
|
||||||
}
|
}
|
||||||
solrCores.addCoreDescriptor(cd);
|
|
||||||
SolrCore old = solrCores.putCore(cd, core);
|
SolrCore old = solrCores.putCore(cd, core);
|
||||||
/*
|
/*
|
||||||
* set both the name of the descriptor and the name of the
|
* set both the name of the descriptor and the name of the
|
||||||
|
@ -1477,6 +1489,8 @@ public class CoreContainer {
|
||||||
return collectionsHandler;
|
return collectionsHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HealthCheckHandler getHealthCheckHandler() { return healthCheckHandler; }
|
||||||
|
|
||||||
public InfoHandler getInfoHandler() {
|
public InfoHandler getInfoHandler() {
|
||||||
return infoHandler;
|
return infoHandler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ public class NodeConfig {
|
||||||
|
|
||||||
private final String collectionsAdminHandlerClass;
|
private final String collectionsAdminHandlerClass;
|
||||||
|
|
||||||
|
private final String healthCheckHandlerClass;
|
||||||
|
|
||||||
private final String infoHandlerClass;
|
private final String infoHandlerClass;
|
||||||
|
|
||||||
private final String configSetsHandlerClass;
|
private final String configSetsHandlerClass;
|
||||||
|
@ -74,7 +76,7 @@ public class NodeConfig {
|
||||||
private NodeConfig(String nodeName, Path coreRootDirectory, Path solrDataHome, Path configSetBaseDirectory, String sharedLibDirectory,
|
private NodeConfig(String nodeName, Path coreRootDirectory, Path solrDataHome, Path configSetBaseDirectory, String sharedLibDirectory,
|
||||||
PluginInfo shardHandlerFactoryConfig, UpdateShardHandlerConfig updateShardHandlerConfig,
|
PluginInfo shardHandlerFactoryConfig, UpdateShardHandlerConfig updateShardHandlerConfig,
|
||||||
String coreAdminHandlerClass, String collectionsAdminHandlerClass,
|
String coreAdminHandlerClass, String collectionsAdminHandlerClass,
|
||||||
String infoHandlerClass, String configSetsHandlerClass,
|
String healthCheckHandlerClass, String infoHandlerClass, String configSetsHandlerClass,
|
||||||
LogWatcherConfig logWatcherConfig, CloudConfig cloudConfig, Integer coreLoadThreads,
|
LogWatcherConfig logWatcherConfig, CloudConfig cloudConfig, Integer coreLoadThreads,
|
||||||
int transientCacheSize, boolean useSchemaCache, String managementPath, SolrResourceLoader loader,
|
int transientCacheSize, boolean useSchemaCache, String managementPath, SolrResourceLoader loader,
|
||||||
Properties solrProperties, PluginInfo[] backupRepositoryPlugins,
|
Properties solrProperties, PluginInfo[] backupRepositoryPlugins,
|
||||||
|
@ -88,6 +90,7 @@ public class NodeConfig {
|
||||||
this.updateShardHandlerConfig = updateShardHandlerConfig;
|
this.updateShardHandlerConfig = updateShardHandlerConfig;
|
||||||
this.coreAdminHandlerClass = coreAdminHandlerClass;
|
this.coreAdminHandlerClass = coreAdminHandlerClass;
|
||||||
this.collectionsAdminHandlerClass = collectionsAdminHandlerClass;
|
this.collectionsAdminHandlerClass = collectionsAdminHandlerClass;
|
||||||
|
this.healthCheckHandlerClass = healthCheckHandlerClass;
|
||||||
this.infoHandlerClass = infoHandlerClass;
|
this.infoHandlerClass = infoHandlerClass;
|
||||||
this.configSetsHandlerClass = configSetsHandlerClass;
|
this.configSetsHandlerClass = configSetsHandlerClass;
|
||||||
this.logWatcherConfig = logWatcherConfig;
|
this.logWatcherConfig = logWatcherConfig;
|
||||||
|
@ -146,6 +149,10 @@ public class NodeConfig {
|
||||||
return collectionsAdminHandlerClass;
|
return collectionsAdminHandlerClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHealthCheckHandlerClass() {
|
||||||
|
return healthCheckHandlerClass;
|
||||||
|
}
|
||||||
|
|
||||||
public String getInfoHandlerClass() {
|
public String getInfoHandlerClass() {
|
||||||
return infoHandlerClass;
|
return infoHandlerClass;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +216,7 @@ public class NodeConfig {
|
||||||
private UpdateShardHandlerConfig updateShardHandlerConfig = UpdateShardHandlerConfig.DEFAULT;
|
private UpdateShardHandlerConfig updateShardHandlerConfig = UpdateShardHandlerConfig.DEFAULT;
|
||||||
private String coreAdminHandlerClass = DEFAULT_ADMINHANDLERCLASS;
|
private String coreAdminHandlerClass = DEFAULT_ADMINHANDLERCLASS;
|
||||||
private String collectionsAdminHandlerClass = DEFAULT_COLLECTIONSHANDLERCLASS;
|
private String collectionsAdminHandlerClass = DEFAULT_COLLECTIONSHANDLERCLASS;
|
||||||
|
private String healthCheckHandlerClass = DEFAULT_HEALTHCHECKHANDLERCLASS;
|
||||||
private String infoHandlerClass = DEFAULT_INFOHANDLERCLASS;
|
private String infoHandlerClass = DEFAULT_INFOHANDLERCLASS;
|
||||||
private String configSetsHandlerClass = DEFAULT_CONFIGSETSHANDLERCLASS;
|
private String configSetsHandlerClass = DEFAULT_CONFIGSETSHANDLERCLASS;
|
||||||
private LogWatcherConfig logWatcherConfig = new LogWatcherConfig(true, null, null, 50);
|
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_ADMINHANDLERCLASS = "org.apache.solr.handler.admin.CoreAdminHandler";
|
||||||
private static final String DEFAULT_INFOHANDLERCLASS = "org.apache.solr.handler.admin.InfoHandler";
|
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_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";
|
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(
|
public static final Set<String> DEFAULT_HIDDEN_SYS_PROPS = new HashSet<>(Arrays.asList(
|
||||||
|
@ -302,6 +311,11 @@ public class NodeConfig {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NodeConfigBuilder setHealthCheckHandlerClass(String healthCheckHandlerClass) {
|
||||||
|
this.healthCheckHandlerClass = healthCheckHandlerClass;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public NodeConfigBuilder setInfoHandlerClass(String infoHandlerClass) {
|
public NodeConfigBuilder setInfoHandlerClass(String infoHandlerClass) {
|
||||||
this.infoHandlerClass = infoHandlerClass;
|
this.infoHandlerClass = infoHandlerClass;
|
||||||
return this;
|
return this;
|
||||||
|
@ -366,7 +380,7 @@ public class NodeConfig {
|
||||||
|
|
||||||
public NodeConfig build() {
|
public NodeConfig build() {
|
||||||
return new NodeConfig(nodeName, coreRootDirectory, solrDataHome, configSetBaseDirectory, sharedLibDirectory, shardHandlerFactoryConfig,
|
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,
|
logWatcherConfig, cloudConfig, coreLoadThreads, transientCacheSize, useSchemaCache, managementPath, loader, solrProperties,
|
||||||
backupRepositoryPlugins, metricsConfig, transientCacheConfig);
|
backupRepositoryPlugins, metricsConfig, transientCacheConfig);
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,9 @@ public class SolrXmlConfig {
|
||||||
case "collectionsHandler":
|
case "collectionsHandler":
|
||||||
builder.setCollectionsAdminHandlerClass(value);
|
builder.setCollectionsAdminHandlerClass(value);
|
||||||
break;
|
break;
|
||||||
|
case "healthCheckHandler":
|
||||||
|
builder.setHealthCheckHandlerClass(value);
|
||||||
|
break;
|
||||||
case "infoHandler":
|
case "infoHandler":
|
||||||
builder.setInfoHandlerClass(value);
|
builder.setInfoHandlerClass(value);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.apache.commons.lang.ArrayUtils;
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
import org.apache.lucene.analysis.TokenStream;
|
||||||
import org.apache.lucene.analysis.Tokenizer;
|
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.CharTermAttribute;
|
||||||
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
|
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
|
||||||
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
|
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++) {
|
for (int i = 0; i < tokens.length; i++) {
|
||||||
AttributeSource token = tokens[i];
|
AttributeSource token = tokens[i];
|
||||||
final NamedList<Object> tokenNamedList = new SimpleOrderedMap<>();
|
final NamedList<Object> tokenNamedList = new SimpleOrderedMap<>();
|
||||||
final TermToBytesRefAttribute termAtt = token.getAttribute(TermToBytesRefAttribute.class);
|
final BytesRef rawBytes;
|
||||||
BytesRef rawBytes = termAtt.getBytesRef();
|
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();
|
final String text = fieldType.indexedToReadable(rawBytes, new CharsRefBuilder()).toString();
|
||||||
tokenNamedList.add("text", text);
|
tokenNamedList.add("text", text);
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.apache.solr.client.solrj.request.UpdateRequest;
|
||||||
import org.apache.solr.cloud.ZkController;
|
import org.apache.solr.cloud.ZkController;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.cloud.ClusterState;
|
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.Slice;
|
||||||
import org.apache.solr.common.cloud.ZkCoreNodeProps;
|
import org.apache.solr.common.cloud.ZkCoreNodeProps;
|
||||||
import org.apache.solr.common.cloud.ZkNodeProps;
|
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);
|
log.warn("Error when updating cluster state", e);
|
||||||
}
|
}
|
||||||
ClusterState cstate = zkController.getClusterState();
|
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"));
|
ExecutorService parallelExecutor = ExecutorUtil.newMDCAwareCachedThreadPool(new DefaultSolrThreadFactory("parallelCdcrExecutor"));
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.apache.solr.cloud.ZkController;
|
||||||
import org.apache.solr.cloud.ZkSolrResourceLoader;
|
import org.apache.solr.cloud.ZkSolrResourceLoader;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.cloud.ClusterState;
|
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.Replica;
|
||||||
import org.apache.solr.common.cloud.Slice;
|
import org.apache.solr.common.cloud.Slice;
|
||||||
import org.apache.solr.common.params.CommonParams;
|
import org.apache.solr.common.params.CommonParams;
|
||||||
|
@ -789,8 +790,9 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
|
||||||
List<String> activeReplicaCoreUrls = new ArrayList<>();
|
List<String> activeReplicaCoreUrls = new ArrayList<>();
|
||||||
ClusterState clusterState = zkController.getZkStateReader().getClusterState();
|
ClusterState clusterState = zkController.getZkStateReader().getClusterState();
|
||||||
Set<String> liveNodes = clusterState.getLiveNodes();
|
Set<String> liveNodes = clusterState.getLiveNodes();
|
||||||
Collection<Slice> activeSlices = clusterState.getActiveSlices(collection);
|
final DocCollection docCollection = clusterState.getCollectionOrNull(collection);
|
||||||
if (activeSlices != null && activeSlices.size() > 0) {
|
if (docCollection != null && docCollection.getActiveSlices() != null && docCollection.getActiveSlices().size() > 0) {
|
||||||
|
final Collection<Slice> activeSlices = docCollection.getActiveSlices();
|
||||||
for (Slice next : activeSlices) {
|
for (Slice next : activeSlices) {
|
||||||
Map<String, Replica> replicasMap = next.getReplicasMap();
|
Map<String, Replica> replicasMap = next.getReplicasMap();
|
||||||
if (replicasMap != null) {
|
if (replicasMap != null) {
|
||||||
|
|
|
@ -200,7 +200,6 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
|
||||||
.withFunctionName("copyOf", CopyOfEvaluator.class)
|
.withFunctionName("copyOf", CopyOfEvaluator.class)
|
||||||
.withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
|
.withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
|
||||||
.withFunctionName("cov", CovarianceEvaluator.class)
|
.withFunctionName("cov", CovarianceEvaluator.class)
|
||||||
.withFunctionName("cumulativeProbability", CumulativeProbabilityEvaluator.class)
|
|
||||||
.withFunctionName("describe", DescribeEvaluator.class)
|
.withFunctionName("describe", DescribeEvaluator.class)
|
||||||
.withFunctionName("distance", DistanceEvaluator.class)
|
.withFunctionName("distance", DistanceEvaluator.class)
|
||||||
.withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
|
.withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
|
||||||
|
@ -219,6 +218,12 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
|
||||||
.withFunctionName("addAll", AddAllEvaluator.class)
|
.withFunctionName("addAll", AddAllEvaluator.class)
|
||||||
.withFunctionName("residuals", ResidualsEvaluator.class)
|
.withFunctionName("residuals", ResidualsEvaluator.class)
|
||||||
.withFunctionName("plot", PlotStream.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
|
// Boolean Stream Evaluators
|
||||||
.withFunctionName("and", AndEvaluator.class)
|
.withFunctionName("and", AndEvaluator.class)
|
||||||
|
|
|
@ -1034,8 +1034,10 @@ public class CollectionsHandler extends RequestHandlerBase implements Permission
|
||||||
for (int i = 0; i < numRetries; i++) {
|
for (int i = 0; i < numRetries; i++) {
|
||||||
ClusterState clusterState = zkStateReader.getClusterState();
|
ClusterState clusterState = zkStateReader.getClusterState();
|
||||||
|
|
||||||
Collection<Slice> shards = clusterState.getSlices(collectionName);
|
final DocCollection docCollection = clusterState.getCollectionOrNull(collectionName);
|
||||||
if (shards != null) {
|
|
||||||
|
if (docCollection != null && docCollection.getSlices() != null) {
|
||||||
|
Collection<Slice> shards = docCollection.getSlices();
|
||||||
replicaNotAlive = null;
|
replicaNotAlive = null;
|
||||||
for (Slice shard : shards) {
|
for (Slice shard : shards) {
|
||||||
Collection<Replica> replicas;
|
Collection<Replica> replicas;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -209,14 +209,16 @@ public class SolrInfoMBeanHandler extends RequestHandlerBase {
|
||||||
for(int i=0; i<ref.size(); i++) {
|
for(int i=0; i<ref.size(); i++) {
|
||||||
String name = ref.getName(i);
|
String name = ref.getName(i);
|
||||||
Object r = ref.getVal(i);
|
Object r = ref.getVal(i);
|
||||||
Object n = now.remove(name);
|
Object n = now.get(name);
|
||||||
if(n == null) {
|
if (n == null) {
|
||||||
if(r!=null) {
|
if (r != null) {
|
||||||
out.add("REMOVE "+name, r);
|
out.add("REMOVE " + name, r);
|
||||||
|
now.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (r != null) {
|
||||||
out.add(name, diffObject(r,n));
|
out.add(name, diffObject(r, n));
|
||||||
|
now.remove(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -387,7 +387,7 @@ public class HttpShardHandler extends ShardHandler {
|
||||||
} else {
|
} else {
|
||||||
if (clusterState == null) {
|
if (clusterState == null) {
|
||||||
clusterState = zkController.getClusterState();
|
clusterState = zkController.getClusterState();
|
||||||
slices = clusterState.getSlicesMap(cloudDescriptor.getCollectionName());
|
slices = clusterState.getCollection(cloudDescriptor.getCollectionName()).getSlicesMap();
|
||||||
}
|
}
|
||||||
String sliceName = rb.slices[i];
|
String sliceName = rb.slices[i];
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,7 @@ import org.apache.solr.update.IndexFingerprint;
|
||||||
import org.apache.solr.update.PeerSync;
|
import org.apache.solr.update.PeerSync;
|
||||||
import org.apache.solr.update.UpdateLog;
|
import org.apache.solr.update.UpdateLog;
|
||||||
import org.apache.solr.util.RefCounted;
|
import org.apache.solr.util.RefCounted;
|
||||||
|
import org.apache.solr.util.TestInjection;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -955,10 +956,15 @@ public class RealTimeGetComponent extends SearchComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processGetFingeprint(ResponseBuilder rb) throws IOException {
|
public void processGetFingeprint(ResponseBuilder rb) throws IOException {
|
||||||
|
TestInjection.injectFailIndexFingerprintRequests();
|
||||||
|
|
||||||
SolrQueryRequest req = rb.req;
|
SolrQueryRequest req = rb.req;
|
||||||
SolrParams params = req.getParams();
|
SolrParams params = req.getParams();
|
||||||
|
|
||||||
long maxVersion = params.getLong("getFingerprint", Long.MAX_VALUE);
|
long maxVersion = params.getLong("getFingerprint", Long.MAX_VALUE);
|
||||||
|
if (TestInjection.injectWrongIndexFingerprint()) {
|
||||||
|
maxVersion = -1;
|
||||||
|
}
|
||||||
IndexFingerprint fingerprint = IndexFingerprint.getFingerprint(req.getCore(), Math.abs(maxVersion));
|
IndexFingerprint fingerprint = IndexFingerprint.getFingerprint(req.getCore(), Math.abs(maxVersion));
|
||||||
rb.rsp.add("fingerprint", fingerprint);
|
rb.rsp.add("fingerprint", fingerprint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class StatsValuesFactory {
|
||||||
return statsValue;
|
return statsValue;
|
||||||
} else if (StrField.class.isInstance(fieldType)) {
|
} else if (StrField.class.isInstance(fieldType)) {
|
||||||
return new StringStatsValues(statsField);
|
return new StringStatsValues(statsField);
|
||||||
} else if (sf.getType().getClass().equals(EnumField.class)) {
|
} else if (AbstractEnumField.class.isInstance(fieldType)) {
|
||||||
return new EnumStatsValues(statsField);
|
return new EnumStatsValues(statsField);
|
||||||
} else {
|
} else {
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
|
|
@ -108,6 +108,16 @@ public class TermsComponent extends SearchComponent {
|
||||||
rb.rsp.add("terms", termsResult);
|
rb.rsp.add("terms", termsResult);
|
||||||
|
|
||||||
if (fields == null || fields.length==0) return;
|
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);
|
boolean termStats = params.getBool(TermsParams.TERMS_STATS, false);
|
||||||
|
|
||||||
|
|
|
@ -1005,8 +1005,7 @@ public class SolrMetricManager {
|
||||||
|
|
||||||
private List<PluginInfo> prepareCloudPlugins(PluginInfo[] pluginInfos, String group, String className,
|
private List<PluginInfo> prepareCloudPlugins(PluginInfo[] pluginInfos, String group, String className,
|
||||||
Map<String, String> defaultAttributes,
|
Map<String, String> defaultAttributes,
|
||||||
Map<String, Object> defaultInitArgs,
|
Map<String, Object> defaultInitArgs) {
|
||||||
PluginInfo defaultPlugin) {
|
|
||||||
List<PluginInfo> result = new ArrayList<>();
|
List<PluginInfo> result = new ArrayList<>();
|
||||||
if (pluginInfos == null) {
|
if (pluginInfos == null) {
|
||||||
pluginInfos = new PluginInfo[0];
|
pluginInfos = new PluginInfo[0];
|
||||||
|
@ -1021,12 +1020,6 @@ public class SolrMetricManager {
|
||||||
result.add(info);
|
result.add(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result.isEmpty() && defaultPlugin != null) {
|
|
||||||
defaultPlugin = preparePlugin(defaultPlugin, className, defaultAttributes, defaultInitArgs);
|
|
||||||
if (defaultPlugin != null) {
|
|
||||||
result.add(defaultPlugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,7 +1070,7 @@ public class SolrMetricManager {
|
||||||
String registryName = core.getCoreMetricManager().getRegistryName();
|
String registryName = core.getCoreMetricManager().getRegistryName();
|
||||||
// collect infos and normalize
|
// collect infos and normalize
|
||||||
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.shard.toString(), SolrShardReporter.class.getName(),
|
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.shard.toString(), SolrShardReporter.class.getName(),
|
||||||
attrs, initArgs, null);
|
attrs, initArgs);
|
||||||
for (PluginInfo info : infos) {
|
for (PluginInfo info : infos) {
|
||||||
try {
|
try {
|
||||||
SolrMetricReporter reporter = loadReporter(registryName, core.getResourceLoader(), info,
|
SolrMetricReporter reporter = loadReporter(registryName, core.getResourceLoader(), info,
|
||||||
|
@ -1100,7 +1093,7 @@ public class SolrMetricManager {
|
||||||
Map<String, Object> initArgs = new HashMap<>();
|
Map<String, Object> initArgs = new HashMap<>();
|
||||||
initArgs.put("period", DEFAULT_CLOUD_REPORTER_PERIOD);
|
initArgs.put("period", DEFAULT_CLOUD_REPORTER_PERIOD);
|
||||||
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.cluster.toString(), SolrClusterReporter.class.getName(),
|
List<PluginInfo> infos = prepareCloudPlugins(pluginInfos, SolrInfoBean.Group.cluster.toString(), SolrClusterReporter.class.getName(),
|
||||||
attrs, initArgs, null);
|
attrs, initArgs);
|
||||||
String registryName = getRegistryName(SolrInfoBean.Group.cluster);
|
String registryName = getRegistryName(SolrInfoBean.Group.cluster);
|
||||||
for (PluginInfo info : infos) {
|
for (PluginInfo info : infos) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -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 {
|
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
|
// 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);
|
mincount = Math.max(mincount, 1);
|
||||||
final SchemaField sf = searcher.getSchema().getField(fieldName);
|
final SchemaField sf = searcher.getSchema().getField(fieldName);
|
||||||
final FieldType ft = sf.getType();
|
final FieldType ft = sf.getType();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.solr.request;
|
package org.apache.solr.request;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
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.BoundedTreeSet;
|
||||||
import org.apache.solr.util.DefaultSolrThreadFactory;
|
import org.apache.solr.util.DefaultSolrThreadFactory;
|
||||||
import org.apache.solr.util.RTimer;
|
import org.apache.solr.util.RTimer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static org.apache.solr.common.params.CommonParams.SORT;
|
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.
|
* to leverage any of its functionality.
|
||||||
*/
|
*/
|
||||||
public class SimpleFacets {
|
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 */
|
/** The main set of documents all facet counts should be relative to */
|
||||||
protected DocSet docsOrig;
|
protected DocSet docsOrig;
|
||||||
|
@ -492,10 +496,19 @@ public class SimpleFacets {
|
||||||
+ FacetParams.FACET_CONTAINS + ", "
|
+ FacetParams.FACET_CONTAINS + ", "
|
||||||
+ FacetParams.FACET_EXCLUDETERMS + ") are not supported on numeric types");
|
+ 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) { // default is mincount=0. See SOLR-10033 & SOLR-11174.
|
||||||
// if (ft.isPointField() && mincount <= 0) {
|
String warningMessage
|
||||||
// throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_MINCOUNT + " <= 0 is not supported on point types");
|
= "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);
|
counts = NumericFacets.getCounts(searcher, docs, field, offset, limit, mincount, missing, sort);
|
||||||
} else {
|
} else {
|
||||||
PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, docs, field, offset, limit, mincount, missing, sort, prefix, termFilter);
|
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
Loading…
Reference in New Issue