diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java index dc517f257537a..4bb33937579c2 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/ValuesSourceReaderBenchmark.java @@ -189,6 +189,11 @@ public String indexName() { return "benchmark"; } + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return MappedFieldType.FieldExtractPreference.NONE; + } + @Override public SearchLookup lookup() { throw new UnsupportedOperationException(); diff --git a/docs/changelog/104218.yaml b/docs/changelog/104218.yaml new file mode 100644 index 0000000000000..b3051008dc47b --- /dev/null +++ b/docs/changelog/104218.yaml @@ -0,0 +1,6 @@ +pr: 104218 +summary: "Support ST_CENTROID over spatial points" +area: "ES|QL" +type: enhancement +issues: + - 104656 diff --git a/docs/reference/esql/functions/aggregation-functions.asciidoc b/docs/reference/esql/functions/aggregation-functions.asciidoc index bd501ea49f158..91293728fd45c 100644 --- a/docs/reference/esql/functions/aggregation-functions.asciidoc +++ b/docs/reference/esql/functions/aggregation-functions.asciidoc @@ -16,6 +16,7 @@ The <> function supports these aggregate functions: * <> * <> * <> +* <> * <> // end::agg_list[] @@ -27,4 +28,5 @@ include::median.asciidoc[] include::median-absolute-deviation.asciidoc[] include::min.asciidoc[] include::percentile.asciidoc[] +include::st_centroid.asciidoc[] include::sum.asciidoc[] diff --git a/docs/reference/esql/functions/st_centroid.asciidoc b/docs/reference/esql/functions/st_centroid.asciidoc new file mode 100644 index 0000000000000..abed1e71eab8f --- /dev/null +++ b/docs/reference/esql/functions/st_centroid.asciidoc @@ -0,0 +1,18 @@ +[discrete] +[[esql-agg-st-centroid]] +=== `ST_CENTROID` + +Calculate the spatial centroid over a field with spatial point geometry type. + +[source.merge.styled,esql] +---- +include::{esql-specs}/spatial.csv-spec[tag=st_centroid-airports] +---- +[%header.monospaced.styled,format=dsv,separator=|] +|=== +include::{esql-specs}/spatial.csv-spec[tag=st_centroid-airports-result] +|=== + +Supported types: + +include::types/st_centroid.asciidoc[] diff --git a/docs/reference/esql/functions/types/st_centroid.asciidoc b/docs/reference/esql/functions/types/st_centroid.asciidoc new file mode 100644 index 0000000000000..cbafb9d0fa6dc --- /dev/null +++ b/docs/reference/esql/functions/types/st_centroid.asciidoc @@ -0,0 +1,6 @@ +[%header.monospaced.styled,format=dsv,separator=|] +|=== +v | result +geo_point | geo_point +cartesian_point | cartesian_point +|=== diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java index abd2a4c8fa622..e1fbc2e149441 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapperTests.java @@ -265,7 +265,7 @@ protected IngestScriptSupport ingestScriptSupport() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> ((BytesRef) v).utf8ToString(); } } diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java index d89e1aa6a9772..efdf3c09bbe92 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java @@ -438,7 +438,7 @@ public List invalidExample() throws IOException { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> (Number) v; } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java index d5098e1021a1c..7cadec68f3e61 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/AbstractGeometryFieldMapper.java @@ -143,6 +143,10 @@ protected Object parseSourceValue(Object value) { @Override public BlockLoader blockLoader(BlockLoaderContext blContext) { // Currently we can only load from source in ESQL + return blockLoaderFromSource(blContext); + } + + protected BlockLoader blockLoaderFromSource(BlockLoaderContext blContext) { ValueFetcher fetcher = valueFetcher(blContext.sourcePaths(name()), nullValue, GeometryFormatterFactory.WKB); // TODO consider optimization using BlockSourceReader.lookupFromFieldNames(blContext.fieldNames(), name()) return new BlockSourceReader.GeometriesBlockLoader(fetcher, BlockSourceReader.lookupMatchingAll()); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/AbstractPointGeometryFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/AbstractPointGeometryFieldMapper.java index be6e00d5c7b45..9136a0dfbf550 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/AbstractPointGeometryFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/AbstractPointGeometryFieldMapper.java @@ -22,6 +22,8 @@ import java.util.function.Function; import java.util.function.Supplier; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; + /** Base class for spatial fields that only support indexing points */ public abstract class AbstractPointGeometryFieldMapper extends AbstractGeometryFieldMapper { @@ -176,5 +178,13 @@ protected AbstractPointFieldType( protected Object nullValueAsSource(T nullValue) { return nullValue == null ? null : nullValue.toWKT(); } + + @Override + public BlockLoader blockLoader(BlockLoaderContext blContext) { + if (blContext.fieldExtractPreference() == DOC_VALUES && hasDocValues()) { + return new BlockDocValuesReader.LongsBlockLoader(name()); + } + return blockLoaderFromSource(blContext); + } } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index 6f561bdb2dc4c..ea6bc2b73a208 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -640,6 +640,17 @@ public BlockLoader blockLoader(BlockLoaderContext blContext) { return null; } + public enum FieldExtractPreference { + /** + * Load the field from doc-values into a BlockLoader supporting doc-values. + */ + DOC_VALUES, + /** + * No preference. Leave the choice of where to load the field from up to the FieldType. + */ + NONE + } + /** * Arguments for {@link #blockLoader}. */ @@ -649,6 +660,13 @@ public interface BlockLoaderContext { */ String indexName(); + /** + * How the field should be extracted into the BlockLoader. The default is {@link FieldExtractPreference#NONE}, which means + * that the field type can choose where to load the field from. However, in some cases, the caller may have a preference. + * For example, when loading a spatial field for usage in STATS, it is preferable to load from doc-values. + */ + FieldExtractPreference fieldExtractPreference(); + /** * {@link SearchLookup} used for building scripts. */ diff --git a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java index 6d80be5167e52..70e2fee7a003a 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldMapperTests.java @@ -264,7 +264,7 @@ public List invalidExample() throws IOException { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { // Just assert that we expect a boolean. Otherwise no munging. return v -> (Boolean) v; } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java index 41628dac2faba..9e9437aa6b9db 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldMapperTests.java @@ -706,7 +706,7 @@ public void execute() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> ((Number) v).longValue(); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java index 511ce94a09eec..3798129ccff29 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FloatFieldMapperTests.java @@ -56,7 +56,7 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> { // The test converts the float into a string so we do do Number n = (Number) v; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java index 3da059803014f..a5d705076561b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java @@ -598,6 +598,11 @@ public void testScriptAndPrecludedParameters() { @Override protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) { + return syntheticSourceSupport(ignoreMalformed, false); + } + + @Override + protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed, boolean columnReader) { assumeFalse("synthetic _source for geo_point doesn't support ignore_malformed", ignoreMalformed); return new SyntheticSourceSupport() { private final boolean ignoreZValue = usually(); @@ -607,6 +612,9 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) public SyntheticSourceExample example(int maxVals) { if (randomBoolean()) { Tuple v = generateValue(); + if (columnReader) { + return new SyntheticSourceExample(v.v1(), decode(encode(v.v2())), encode(v.v2()), this::mapping); + } return new SyntheticSourceExample(v.v1(), v.v2(), v.v2().toWKT(), this::mapping); } List> values = randomList(1, maxVals, this::generateValue); @@ -616,12 +624,20 @@ public SyntheticSourceExample example(int maxVals) { .sorted((a, b) -> Long.compare(encode(a.v2()), encode(b.v2()))) .toList(); List in = sorted.stream().map(Tuple::v1).toList(); - List outList = sorted.stream().map(v -> encode(v.v2())).sorted().map(this::decode).toList(); + List outList = sorted.stream().map(Tuple::v2).toList(); Object out = outList.size() == 1 ? outList.get(0) : outList; - List outBlockList = outList.stream().map(GeoPoint::toWKT).toList(); - Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; - return new SyntheticSourceExample(in, out, outBlock, this::mapping); + if (columnReader) { + // When reading doc-values, the block is a list of encoded longs + List outBlockList = outList.stream().map(this::encode).toList(); + Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; + return new SyntheticSourceExample(in, out, outBlock, this::mapping); + } else { + // When reading row-stride, the block is a list of WKT encoded BytesRefs + List outBlockList = outList.stream().map(GeoPoint::toWKT).toList(); + Object outBlock = outBlockList.size() == 1 ? outBlockList.get(0) : outBlockList; + return new SyntheticSourceExample(in, out, outBlock, this::mapping); + } } private Tuple generateValue() { @@ -705,13 +721,18 @@ protected IngestScriptSupport ingestScriptSupport() { throw new AssumptionViolatedException("not supported"); } - private boolean useDocValues = false; + @Override + protected Function loadBlockExpected() { + throw new IllegalStateException("Should never reach here, call loadBlockExpected(BlockReaderSupport, boolean) instead"); + } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { - if (useDocValues) { + protected Function loadBlockExpected(BlockReaderSupport blockReaderSupport, boolean columnReader) { + if (columnReader) { + // When using column reader, we expect the output to be doc-values (which means encoded longs) return v -> asJacksonNumberOutput(((Number) v).longValue()); } else { + // When using row-stride reader, we expect the output to be WKT encoded BytesRef return v -> asWKT((BytesRef) v); } } @@ -732,13 +753,8 @@ protected static Object asWKT(BytesRef value) { } @Override - protected boolean supportsColumnAtATimeReader(MapperService mapper, MappedFieldType ft) { - // Currently ESQL support for geo_point is limited to source values - return false; - } - - @Override - public void testBlockLoaderFromRowStrideReaderWithSyntheticSource() { - assumeTrue("Synthetic source not completed supported for geo_point", false); + protected BlockReaderSupport getSupportedReaders(MapperService mapper, String loaderFieldName) { + MappedFieldType ft = mapper.fieldType(loaderFieldName); + return new BlockReaderSupport(ft.hasDocValues(), false, mapper, loaderFieldName); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java index a0545308c3928..cc024efb5f307 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/HalfFloatFieldMapperTests.java @@ -56,7 +56,7 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> { // The test converts the float into a string so we do do Number n = (Number) v; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java index 7aa68a6949b7e..ba9c2e6c4a299 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldMapperTests.java @@ -423,7 +423,7 @@ public void execute() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> InetAddresses.toAddrString(InetAddressPoint.decode(BytesRef.deepCopyOf((BytesRef) v).bytes)); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java index bf426f7e7e41b..892dbcb185bdb 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldMapperTests.java @@ -650,7 +650,7 @@ protected boolean supportsIgnoreMalformed() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> ((BytesRef) v).utf8ToString(); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java index 0e4502a813c15..f2d4431e5c79f 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldMapperTests.java @@ -122,7 +122,7 @@ public void testFetchCoerced() throws IOException { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return n -> { Number number = ((Number) n); if (Integer.MIN_VALUE <= number.longValue() && number.longValue() <= Integer.MAX_VALUE) { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java index 2db0203cb9383..7b91c84a05c53 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java @@ -382,7 +382,7 @@ public void testAllowMultipleValuesField() throws IOException { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return n -> ((Number) n); // Just assert it's a number } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java index 0181389bf8f8b..f92867d1ce461 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java @@ -1203,8 +1203,8 @@ public List invalidExample() throws IOException { } @Override - protected Function loadBlockExpected(MapperService mapper, String fieldName) { - if (nullLoaderExpected(mapper, fieldName)) { + protected Function loadBlockExpected(BlockReaderSupport blockReaderSupport, boolean columnReader) { + if (nullLoaderExpected(blockReaderSupport.mapper(), blockReaderSupport.loaderFieldName())) { return null; } return v -> ((BytesRef) v).utf8ToString(); @@ -1360,20 +1360,22 @@ public void testEmpty() throws Exception { } @Override - protected boolean supportsColumnAtATimeReader(MapperService mapper, MappedFieldType ft) { + protected BlockReaderSupport getSupportedReaders(MapperService mapper, String loaderFieldName) { + MappedFieldType ft = mapper.fieldType(loaderFieldName); String parentName = mapper.mappingLookup().parentField(ft.name()); if (parentName == null) { TextFieldMapper.TextFieldType text = (TextFieldType) ft; - return text.syntheticSourceDelegate() != null + boolean supportsColumnAtATimeReader = text.syntheticSourceDelegate() != null && text.syntheticSourceDelegate().hasDocValues() && text.canUseSyntheticSourceDelegateForQuerying(); + return new BlockReaderSupport(supportsColumnAtATimeReader, mapper, loaderFieldName); } MappedFieldType parent = mapper.fieldType(parentName); if (false == parent.typeName().equals(KeywordFieldMapper.CONTENT_TYPE)) { throw new UnsupportedOperationException(); } KeywordFieldMapper.KeywordFieldType kwd = (KeywordFieldMapper.KeywordFieldType) parent; - return kwd.hasDocValues(); + return new BlockReaderSupport(kwd.hasDocValues(), mapper, loaderFieldName); } public void testBlockLoaderFromParentColumnReader() throws IOException { @@ -1407,6 +1409,7 @@ private void testBlockLoaderFromParent(boolean columnReader, boolean syntheticSo b.endObject(); }; MapperService mapper = createMapperService(syntheticSource ? syntheticSourceMapping(buildFields) : mapping(buildFields)); - testBlockLoader(columnReader, example, mapper, "field.sub"); + BlockReaderSupport blockReaderSupport = getSupportedReaders(mapper, "field.sub"); + testBlockLoader(columnReader, example, blockReaderSupport); } } diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java index 643beda11939c..7693b58bff595 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldTypeTestCase.java @@ -422,6 +422,11 @@ public String indexName() { throw new UnsupportedOperationException(); } + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return MappedFieldType.FieldExtractPreference.NONE; + } + @Override public SearchLookup lookup() { return mockContext().lookup(); diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java index 4a8e6eb1b758e..05aee30799de2 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MapperTestCase.java @@ -72,6 +72,8 @@ import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE; import static org.elasticsearch.test.MapMatcher.assertMap; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; @@ -1092,6 +1094,10 @@ public interface SyntheticSourceSupport { protected abstract SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed); + protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed, boolean columnReader) { + return syntheticSourceSupport(ignoreMalformed); + } + public final void testSyntheticSource() throws IOException { boolean ignoreMalformed = supportsIgnoreMalformed() ? rarely() : false; assertSyntheticSource(syntheticSourceSupport(ignoreMalformed).example(5)); @@ -1244,60 +1250,97 @@ public final void testBlockLoaderFromColumnReaderWithSyntheticSource() throws IO testBlockLoader(true, true); } - // Removed 'final' to silence this test in GeoPointFieldMapperTests, which does not support synthetic source completely - public void testBlockLoaderFromRowStrideReaderWithSyntheticSource() throws IOException { + public final void testBlockLoaderFromRowStrideReaderWithSyntheticSource() throws IOException { testBlockLoader(true, false); } - protected boolean supportsColumnAtATimeReader(MapperService mapper, MappedFieldType ft) { - return ft.hasDocValues(); + /** + * Get the configuration for testing block loaders with this field. In particular, not all fields can be loaded from doc-values. + * For most ESQL types the preference is to read from doc-values if they exist, so that is the default behaviour here. + * However, for spatial types, the doc-values involve precision loss, and therefor it is preferable to read from source. + * And for text fields, doc values are not easily convertable to original values either, so special cases exist. + */ + protected BlockReaderSupport getSupportedReaders(MapperService mapper, String loaderFieldName) { + MappedFieldType ft = mapper.fieldType(loaderFieldName); + return new BlockReaderSupport(ft.hasDocValues(), true, mapper, loaderFieldName); + } + + /** + * This record encapsulates the test configuration for testing block loaders (used in ES|QL). + * + * @param columnAtATimeReader true if the field supports column at a time readers (doc-values) + * @param syntheticSource true if the field supports synthetic source + * @param mapper the mapper service to use for testing + * @param loaderFieldName the field name to use for loading the field + */ + public record BlockReaderSupport(boolean columnAtATimeReader, boolean syntheticSource, MapperService mapper, String loaderFieldName) { + BlockReaderSupport(boolean columnAtATimeReader, MapperService mapper, String loaderFieldName) { + this(columnAtATimeReader, true, mapper, loaderFieldName); + } + + private BlockLoader getBlockLoader(boolean columnReader) { + SearchLookup searchLookup = new SearchLookup(mapper.mappingLookup().fieldTypesLookup()::get, null, null); + return mapper.fieldType(loaderFieldName).blockLoader(new MappedFieldType.BlockLoaderContext() { + @Override + public String indexName() { + throw new UnsupportedOperationException(); + } + + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return columnReader ? DOC_VALUES : NONE; + } + + @Override + public SearchLookup lookup() { + return searchLookup; + } + + @Override + public Set sourcePaths(String name) { + return mapper.mappingLookup().sourcePaths(name); + } + + @Override + public String parentField(String field) { + return mapper.mappingLookup().parentField(field); + } + + @Override + public FieldNamesFieldMapper.FieldNamesFieldType fieldNames() { + return (FieldNamesFieldMapper.FieldNamesFieldType) mapper.fieldType(FieldNamesFieldMapper.NAME); + } + }); + } } private void testBlockLoader(boolean syntheticSource, boolean columnReader) throws IOException { // TODO if we're not using synthetic source use a different sort of example. Or something. - SyntheticSourceExample example = syntheticSourceSupport(false).example(5); + SyntheticSourceExample example = syntheticSourceSupport(false, columnReader).example(5); XContentBuilder mapping = syntheticSource ? syntheticSourceFieldMapping(example.mapping) : fieldMapping(example.mapping); MapperService mapper = createMapperService(mapping); - testBlockLoader(columnReader, example, mapper, "field"); + BlockReaderSupport blockReaderSupport = getSupportedReaders(mapper, "field"); + if (syntheticSource) { + // geo_point and point do not yet support synthetic source + assumeTrue( + "Synthetic source not completely supported for " + this.getClass().getSimpleName(), + blockReaderSupport.syntheticSource + ); + } + testBlockLoader(columnReader, example, blockReaderSupport); } - protected final void testBlockLoader(boolean columnReader, SyntheticSourceExample example, MapperService mapper, String loaderFieldName) + protected final void testBlockLoader(boolean columnReader, SyntheticSourceExample example, BlockReaderSupport blockReaderSupport) throws IOException { - SearchLookup searchLookup = new SearchLookup(mapper.mappingLookup().fieldTypesLookup()::get, null, null); - BlockLoader loader = mapper.fieldType(loaderFieldName).blockLoader(new MappedFieldType.BlockLoaderContext() { - @Override - public String indexName() { - throw new UnsupportedOperationException(); - } - - @Override - public SearchLookup lookup() { - return searchLookup; - } - - @Override - public Set sourcePaths(String name) { - return mapper.mappingLookup().sourcePaths(name); - } - - @Override - public String parentField(String field) { - return mapper.mappingLookup().parentField(field); - } - - @Override - public FieldNamesFieldMapper.FieldNamesFieldType fieldNames() { - return (FieldNamesFieldMapper.FieldNamesFieldType) mapper.fieldType(FieldNamesFieldMapper.NAME); - } - }); - Function valuesConvert = loadBlockExpected(mapper, loaderFieldName); + BlockLoader loader = blockReaderSupport.getBlockLoader(columnReader); + Function valuesConvert = loadBlockExpected(blockReaderSupport, columnReader); if (valuesConvert == null) { assertNull(loader); return; } try (Directory directory = newDirectory()) { RandomIndexWriter iw = new RandomIndexWriter(random(), directory); - LuceneDocument doc = mapper.documentMapper().parse(source(b -> { + LuceneDocument doc = blockReaderSupport.mapper.documentMapper().parse(source(b -> { b.field("field"); example.inputValue.accept(b); })).rootDoc(); @@ -1307,7 +1350,7 @@ public FieldNamesFieldMapper.FieldNamesFieldType fieldNames() { LeafReaderContext ctx = reader.leaves().get(0); TestBlock block; if (columnReader) { - if (supportsColumnAtATimeReader(mapper, mapper.fieldType(loaderFieldName))) { + if (blockReaderSupport.columnAtATimeReader) { block = (TestBlock) loader.columnAtATimeReader(ctx) .read(TestBlock.factory(ctx.reader().numDocs()), TestBlock.docs(0)); } else { @@ -1363,10 +1406,20 @@ protected Matcher blockItemMatcher(Object expected) { * How {@link MappedFieldType#blockLoader} should load values or {@code null} * if that method isn't supported by field being tested. */ - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return null; } + /** + * How {@link MappedFieldType#blockLoader} should load values or {@code null} + * if that method isn't supported by field being tested. + * This method should be overridden by fields that support different Block types + * when loading from doc values vs source. + */ + protected Function loadBlockExpected(BlockReaderSupport blockReaderSupport, boolean columnReader) { + return loadBlockExpected(); + } + public final void testEmptyDocumentNoDocValueLoader() throws IOException { assumeFalse("Field will add values even if no fields are supplied", addsValueWhenNotSupplied()); assertNoDocValueLoader(b -> {}); diff --git a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Methods.java b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Methods.java index 768ca0bd16201..741a1294e6fb5 100644 --- a/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Methods.java +++ b/x-pack/plugin/esql/compute/gen/src/main/java/org/elasticsearch/compute/gen/Methods.java @@ -12,10 +12,13 @@ import java.util.Arrays; import java.util.function.Predicate; +import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import static org.elasticsearch.compute.gen.Types.BOOLEAN_BLOCK; @@ -47,7 +50,7 @@ */ public class Methods { static ExecutableElement findRequiredMethod(TypeElement declarationType, String[] names, Predicate filter) { - ExecutableElement result = findMethod(declarationType, names, filter); + ExecutableElement result = findMethod(names, filter, declarationType, superClassOf(declarationType)); if (result == null) { if (names.length == 1) { throw new IllegalArgumentException(declarationType + "#" + names[0] + " is required"); @@ -58,18 +61,34 @@ static ExecutableElement findRequiredMethod(TypeElement declarationType, String[ } static ExecutableElement findMethod(TypeElement declarationType, String name) { - return findMethod(declarationType, new String[] { name }, e -> true); + return findMethod(new String[] { name }, e -> true, declarationType, superClassOf(declarationType)); } - static ExecutableElement findMethod(TypeElement declarationType, String[] names, Predicate filter) { - for (ExecutableElement e : ElementFilter.methodsIn(declarationType.getEnclosedElements())) { - if (e.getModifiers().contains(Modifier.STATIC) == false) { - continue; + private static TypeElement superClassOf(TypeElement declarationType) { + TypeMirror superclass = declarationType.getSuperclass(); + if (superclass instanceof DeclaredType declaredType) { + Element superclassElement = declaredType.asElement(); + if (superclassElement instanceof TypeElement) { + return (TypeElement) superclassElement; } - String name = e.getSimpleName().toString(); - for (String n : names) { - if (n.equals(name) && filter.test(e)) { - return e; + } + return null; + } + + static ExecutableElement findMethod(TypeElement declarationType, String[] names, Predicate filter) { + return findMethod(names, filter, declarationType); + } + + static ExecutableElement findMethod(String[] names, Predicate filter, TypeElement... declarationTypes) { + for (TypeElement declarationType : declarationTypes) { + for (ExecutableElement e : ElementFilter.methodsIn(declarationType.getEnclosedElements())) { + if (e.getModifiers().contains(Modifier.STATIC)) { + String name = e.getSimpleName().toString(); + for (String n : names) { + if (n.equals(name) && filter.test(e)) { + return e; + } + } } } } diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunction.java new file mode 100644 index 0000000000000..eef094ce2ecfa --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunction.java @@ -0,0 +1,152 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunction} implementation for {@link SpatialCentroidCartesianPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointDocValuesAggregatorFunction implements AggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final DriverContext driverContext; + + private final CentroidPointAggregator.CentroidState state; + + private final List channels; + + public SpatialCentroidCartesianPointDocValuesAggregatorFunction(DriverContext driverContext, + List channels, CentroidPointAggregator.CentroidState state) { + this.driverContext = driverContext; + this.channels = channels; + this.state = state; + } + + public static SpatialCentroidCartesianPointDocValuesAggregatorFunction create( + DriverContext driverContext, List channels) { + return new SpatialCentroidCartesianPointDocValuesAggregatorFunction(driverContext, channels, SpatialCentroidCartesianPointDocValuesAggregator.initSingle()); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public void addRawInput(Page page) { + LongBlock block = page.getBlock(channels.get(0)); + LongVector vector = block.asVector(); + if (vector != null) { + addRawVector(vector); + } else { + addRawBlock(block); + } + } + + private void addRawVector(LongVector vector) { + for (int i = 0; i < vector.getPositionCount(); i++) { + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, vector.getLong(i)); + } + } + + private void addRawBlock(LongBlock block) { + for (int p = 0; p < block.getPositionCount(); p++) { + if (block.isNull(p)) { + continue; + } + int start = block.getFirstValueIndex(p); + int end = start + block.getValueCount(p); + for (int i = start; i < end; i++) { + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, block.getLong(i)); + } + } + } + + @Override + public void addIntermediateInput(Page page) { + assert channels.size() == intermediateBlockCount(); + assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size(); + Block xValUncast = page.getBlock(channels.get(0)); + if (xValUncast.areAllValuesNull()) { + return; + } + DoubleVector xVal = ((DoubleBlock) xValUncast).asVector(); + assert xVal.getPositionCount() == 1; + Block xDelUncast = page.getBlock(channels.get(1)); + if (xDelUncast.areAllValuesNull()) { + return; + } + DoubleVector xDel = ((DoubleBlock) xDelUncast).asVector(); + assert xDel.getPositionCount() == 1; + Block yValUncast = page.getBlock(channels.get(2)); + if (yValUncast.areAllValuesNull()) { + return; + } + DoubleVector yVal = ((DoubleBlock) yValUncast).asVector(); + assert yVal.getPositionCount() == 1; + Block yDelUncast = page.getBlock(channels.get(3)); + if (yDelUncast.areAllValuesNull()) { + return; + } + DoubleVector yDel = ((DoubleBlock) yDelUncast).asVector(); + assert yDel.getPositionCount() == 1; + Block countUncast = page.getBlock(channels.get(4)); + if (countUncast.areAllValuesNull()) { + return; + } + LongVector count = ((LongBlock) countUncast).asVector(); + assert count.getPositionCount() == 1; + SpatialCentroidCartesianPointDocValuesAggregator.combineIntermediate(state, xVal.getDouble(0), xDel.getDouble(0), yVal.getDouble(0), yDel.getDouble(0), count.getLong(0)); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) { + state.toIntermediate(blocks, offset, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) { + blocks[offset] = SpatialCentroidCartesianPointDocValuesAggregator.evaluateFinal(state, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier.java new file mode 100644 index 0000000000000..3228340beeb43 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier.java @@ -0,0 +1,41 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunctionSupplier} implementation for {@link SpatialCentroidCartesianPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier { + private final List channels; + + public SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier(List channels) { + this.channels = channels; + } + + @Override + public SpatialCentroidCartesianPointDocValuesAggregatorFunction aggregator( + DriverContext driverContext) { + return SpatialCentroidCartesianPointDocValuesAggregatorFunction.create(driverContext, channels); + } + + @Override + public SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction groupingAggregator( + DriverContext driverContext) { + return SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction.create(channels, driverContext); + } + + @Override + public String describe() { + return "spatial_centroid_cartesian_point_doc of valuess"; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction.java new file mode 100644 index 0000000000000..de35965f52575 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction.java @@ -0,0 +1,205 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.aggregation.SeenGroupIds; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link GroupingAggregatorFunction} implementation for {@link SpatialCentroidCartesianPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final CentroidPointAggregator.GroupingCentroidState state; + + private final List channels; + + private final DriverContext driverContext; + + public SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction(List channels, + CentroidPointAggregator.GroupingCentroidState state, DriverContext driverContext) { + this.channels = channels; + this.state = state; + this.driverContext = driverContext; + } + + public static SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction create( + List channels, DriverContext driverContext) { + return new SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction(channels, SpatialCentroidCartesianPointDocValuesAggregator.initGrouping(driverContext.bigArrays()), driverContext); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, + Page page) { + LongBlock valuesBlock = page.getBlock(channels.get(0)); + LongVector valuesVector = valuesBlock.asVector(); + if (valuesVector == null) { + if (valuesBlock.mayHaveNulls()) { + state.enableGroupIdTracking(seenGroupIds); + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + }; + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + }; + } + + private void addRawInput(int positionOffset, IntVector groups, LongBlock values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, groupId, values.getLong(v)); + } + } + } + + private void addRawInput(int positionOffset, IntVector groups, LongVector values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, groupId, values.getLong(groupPosition + positionOffset)); + } + } + + private void addRawInput(int positionOffset, IntBlock groups, LongBlock values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, groupId, values.getLong(v)); + } + } + } + } + + private void addRawInput(int positionOffset, IntBlock groups, LongVector values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + SpatialCentroidCartesianPointDocValuesAggregator.combine(state, groupId, values.getLong(groupPosition + positionOffset)); + } + } + } + + @Override + public void addIntermediateInput(int positionOffset, IntVector groups, Page page) { + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + assert channels.size() == intermediateBlockCount(); + DoubleVector xVal = page.getBlock(channels.get(0)).asVector(); + DoubleVector xDel = page.getBlock(channels.get(1)).asVector(); + DoubleVector yVal = page.getBlock(channels.get(2)).asVector(); + DoubleVector yDel = page.getBlock(channels.get(3)).asVector(); + LongVector count = page.getBlock(channels.get(4)).asVector(); + assert xVal.getPositionCount() == xDel.getPositionCount() && xVal.getPositionCount() == yVal.getPositionCount() && xVal.getPositionCount() == yDel.getPositionCount() && xVal.getPositionCount() == count.getPositionCount(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidCartesianPointDocValuesAggregator.combineIntermediate(state, groupId, xVal.getDouble(groupPosition + positionOffset), xDel.getDouble(groupPosition + positionOffset), yVal.getDouble(groupPosition + positionOffset), yDel.getDouble(groupPosition + positionOffset), count.getLong(groupPosition + positionOffset)); + } + } + + @Override + public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) { + if (input.getClass() != getClass()) { + throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass()); + } + CentroidPointAggregator.GroupingCentroidState inState = ((SpatialCentroidCartesianPointDocValuesGroupingAggregatorFunction) input).state; + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + SpatialCentroidCartesianPointDocValuesAggregator.combineStates(state, groupId, inState, position); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) { + state.toIntermediate(blocks, offset, selected, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, IntVector selected, + DriverContext driverContext) { + blocks[offset] = SpatialCentroidCartesianPointDocValuesAggregator.evaluateFinal(state, selected, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunction.java new file mode 100644 index 0000000000000..bdc7c58a6c963 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunction.java @@ -0,0 +1,157 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.aggregation.AggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunction} implementation for {@link SpatialCentroidCartesianPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointSourceValuesAggregatorFunction implements AggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final DriverContext driverContext; + + private final CentroidPointAggregator.CentroidState state; + + private final List channels; + + public SpatialCentroidCartesianPointSourceValuesAggregatorFunction(DriverContext driverContext, + List channels, CentroidPointAggregator.CentroidState state) { + this.driverContext = driverContext; + this.channels = channels; + this.state = state; + } + + public static SpatialCentroidCartesianPointSourceValuesAggregatorFunction create( + DriverContext driverContext, List channels) { + return new SpatialCentroidCartesianPointSourceValuesAggregatorFunction(driverContext, channels, SpatialCentroidCartesianPointSourceValuesAggregator.initSingle()); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public void addRawInput(Page page) { + BytesRefBlock block = page.getBlock(channels.get(0)); + BytesRefVector vector = block.asVector(); + if (vector != null) { + addRawVector(vector); + } else { + addRawBlock(block); + } + } + + private void addRawVector(BytesRefVector vector) { + BytesRef scratch = new BytesRef(); + for (int i = 0; i < vector.getPositionCount(); i++) { + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch)); + } + } + + private void addRawBlock(BytesRefBlock block) { + BytesRef scratch = new BytesRef(); + for (int p = 0; p < block.getPositionCount(); p++) { + if (block.isNull(p)) { + continue; + } + int start = block.getFirstValueIndex(p); + int end = start + block.getValueCount(p); + for (int i = start; i < end; i++) { + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch)); + } + } + } + + @Override + public void addIntermediateInput(Page page) { + assert channels.size() == intermediateBlockCount(); + assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size(); + Block xValUncast = page.getBlock(channels.get(0)); + if (xValUncast.areAllValuesNull()) { + return; + } + DoubleVector xVal = ((DoubleBlock) xValUncast).asVector(); + assert xVal.getPositionCount() == 1; + Block xDelUncast = page.getBlock(channels.get(1)); + if (xDelUncast.areAllValuesNull()) { + return; + } + DoubleVector xDel = ((DoubleBlock) xDelUncast).asVector(); + assert xDel.getPositionCount() == 1; + Block yValUncast = page.getBlock(channels.get(2)); + if (yValUncast.areAllValuesNull()) { + return; + } + DoubleVector yVal = ((DoubleBlock) yValUncast).asVector(); + assert yVal.getPositionCount() == 1; + Block yDelUncast = page.getBlock(channels.get(3)); + if (yDelUncast.areAllValuesNull()) { + return; + } + DoubleVector yDel = ((DoubleBlock) yDelUncast).asVector(); + assert yDel.getPositionCount() == 1; + Block countUncast = page.getBlock(channels.get(4)); + if (countUncast.areAllValuesNull()) { + return; + } + LongVector count = ((LongBlock) countUncast).asVector(); + assert count.getPositionCount() == 1; + SpatialCentroidCartesianPointSourceValuesAggregator.combineIntermediate(state, xVal.getDouble(0), xDel.getDouble(0), yVal.getDouble(0), yDel.getDouble(0), count.getLong(0)); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) { + state.toIntermediate(blocks, offset, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) { + blocks[offset] = SpatialCentroidCartesianPointSourceValuesAggregator.evaluateFinal(state, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier.java new file mode 100644 index 0000000000000..9b0d7c5f64cd7 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier.java @@ -0,0 +1,42 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunctionSupplier} implementation for {@link SpatialCentroidCartesianPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier { + private final List channels; + + public SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier( + List channels) { + this.channels = channels; + } + + @Override + public SpatialCentroidCartesianPointSourceValuesAggregatorFunction aggregator( + DriverContext driverContext) { + return SpatialCentroidCartesianPointSourceValuesAggregatorFunction.create(driverContext, channels); + } + + @Override + public SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction groupingAggregator( + DriverContext driverContext) { + return SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction.create(channels, driverContext); + } + + @Override + public String describe() { + return "spatial_centroid_cartesian_point_source of valuess"; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction.java new file mode 100644 index 0000000000000..86b2f15187af6 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction.java @@ -0,0 +1,212 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.aggregation.SeenGroupIds; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link GroupingAggregatorFunction} implementation for {@link SpatialCentroidCartesianPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final CentroidPointAggregator.GroupingCentroidState state; + + private final List channels; + + private final DriverContext driverContext; + + public SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction(List channels, + CentroidPointAggregator.GroupingCentroidState state, DriverContext driverContext) { + this.channels = channels; + this.state = state; + this.driverContext = driverContext; + } + + public static SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction create( + List channels, DriverContext driverContext) { + return new SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction(channels, SpatialCentroidCartesianPointSourceValuesAggregator.initGrouping(driverContext.bigArrays()), driverContext); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, + Page page) { + BytesRefBlock valuesBlock = page.getBlock(channels.get(0)); + BytesRefVector valuesVector = valuesBlock.asVector(); + if (valuesVector == null) { + if (valuesBlock.mayHaveNulls()) { + state.enableGroupIdTracking(seenGroupIds); + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + }; + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + }; + } + + private void addRawInput(int positionOffset, IntVector groups, BytesRefBlock values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch)); + } + } + } + + private void addRawInput(int positionOffset, IntVector groups, BytesRefVector values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch)); + } + } + + private void addRawInput(int positionOffset, IntBlock groups, BytesRefBlock values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch)); + } + } + } + } + + private void addRawInput(int positionOffset, IntBlock groups, BytesRefVector values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + SpatialCentroidCartesianPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch)); + } + } + } + + @Override + public void addIntermediateInput(int positionOffset, IntVector groups, Page page) { + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + assert channels.size() == intermediateBlockCount(); + DoubleVector xVal = page.getBlock(channels.get(0)).asVector(); + DoubleVector xDel = page.getBlock(channels.get(1)).asVector(); + DoubleVector yVal = page.getBlock(channels.get(2)).asVector(); + DoubleVector yDel = page.getBlock(channels.get(3)).asVector(); + LongVector count = page.getBlock(channels.get(4)).asVector(); + assert xVal.getPositionCount() == xDel.getPositionCount() && xVal.getPositionCount() == yVal.getPositionCount() && xVal.getPositionCount() == yDel.getPositionCount() && xVal.getPositionCount() == count.getPositionCount(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidCartesianPointSourceValuesAggregator.combineIntermediate(state, groupId, xVal.getDouble(groupPosition + positionOffset), xDel.getDouble(groupPosition + positionOffset), yVal.getDouble(groupPosition + positionOffset), yDel.getDouble(groupPosition + positionOffset), count.getLong(groupPosition + positionOffset)); + } + } + + @Override + public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) { + if (input.getClass() != getClass()) { + throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass()); + } + CentroidPointAggregator.GroupingCentroidState inState = ((SpatialCentroidCartesianPointSourceValuesGroupingAggregatorFunction) input).state; + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + SpatialCentroidCartesianPointSourceValuesAggregator.combineStates(state, groupId, inState, position); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) { + state.toIntermediate(blocks, offset, selected, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, IntVector selected, + DriverContext driverContext) { + blocks[offset] = SpatialCentroidCartesianPointSourceValuesAggregator.evaluateFinal(state, selected, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunction.java new file mode 100644 index 0000000000000..fcd17d4c5cd86 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunction.java @@ -0,0 +1,152 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunction} implementation for {@link SpatialCentroidGeoPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointDocValuesAggregatorFunction implements AggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final DriverContext driverContext; + + private final CentroidPointAggregator.CentroidState state; + + private final List channels; + + public SpatialCentroidGeoPointDocValuesAggregatorFunction(DriverContext driverContext, + List channels, CentroidPointAggregator.CentroidState state) { + this.driverContext = driverContext; + this.channels = channels; + this.state = state; + } + + public static SpatialCentroidGeoPointDocValuesAggregatorFunction create( + DriverContext driverContext, List channels) { + return new SpatialCentroidGeoPointDocValuesAggregatorFunction(driverContext, channels, SpatialCentroidGeoPointDocValuesAggregator.initSingle()); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public void addRawInput(Page page) { + LongBlock block = page.getBlock(channels.get(0)); + LongVector vector = block.asVector(); + if (vector != null) { + addRawVector(vector); + } else { + addRawBlock(block); + } + } + + private void addRawVector(LongVector vector) { + for (int i = 0; i < vector.getPositionCount(); i++) { + SpatialCentroidGeoPointDocValuesAggregator.combine(state, vector.getLong(i)); + } + } + + private void addRawBlock(LongBlock block) { + for (int p = 0; p < block.getPositionCount(); p++) { + if (block.isNull(p)) { + continue; + } + int start = block.getFirstValueIndex(p); + int end = start + block.getValueCount(p); + for (int i = start; i < end; i++) { + SpatialCentroidGeoPointDocValuesAggregator.combine(state, block.getLong(i)); + } + } + } + + @Override + public void addIntermediateInput(Page page) { + assert channels.size() == intermediateBlockCount(); + assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size(); + Block xValUncast = page.getBlock(channels.get(0)); + if (xValUncast.areAllValuesNull()) { + return; + } + DoubleVector xVal = ((DoubleBlock) xValUncast).asVector(); + assert xVal.getPositionCount() == 1; + Block xDelUncast = page.getBlock(channels.get(1)); + if (xDelUncast.areAllValuesNull()) { + return; + } + DoubleVector xDel = ((DoubleBlock) xDelUncast).asVector(); + assert xDel.getPositionCount() == 1; + Block yValUncast = page.getBlock(channels.get(2)); + if (yValUncast.areAllValuesNull()) { + return; + } + DoubleVector yVal = ((DoubleBlock) yValUncast).asVector(); + assert yVal.getPositionCount() == 1; + Block yDelUncast = page.getBlock(channels.get(3)); + if (yDelUncast.areAllValuesNull()) { + return; + } + DoubleVector yDel = ((DoubleBlock) yDelUncast).asVector(); + assert yDel.getPositionCount() == 1; + Block countUncast = page.getBlock(channels.get(4)); + if (countUncast.areAllValuesNull()) { + return; + } + LongVector count = ((LongBlock) countUncast).asVector(); + assert count.getPositionCount() == 1; + SpatialCentroidGeoPointDocValuesAggregator.combineIntermediate(state, xVal.getDouble(0), xDel.getDouble(0), yVal.getDouble(0), yDel.getDouble(0), count.getLong(0)); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) { + state.toIntermediate(blocks, offset, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) { + blocks[offset] = SpatialCentroidGeoPointDocValuesAggregator.evaluateFinal(state, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier.java new file mode 100644 index 0000000000000..46c2777e8c77a --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier.java @@ -0,0 +1,41 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunctionSupplier} implementation for {@link SpatialCentroidGeoPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier { + private final List channels; + + public SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier(List channels) { + this.channels = channels; + } + + @Override + public SpatialCentroidGeoPointDocValuesAggregatorFunction aggregator( + DriverContext driverContext) { + return SpatialCentroidGeoPointDocValuesAggregatorFunction.create(driverContext, channels); + } + + @Override + public SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction groupingAggregator( + DriverContext driverContext) { + return SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction.create(channels, driverContext); + } + + @Override + public String describe() { + return "spatial_centroid_geo_point_doc of valuess"; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction.java new file mode 100644 index 0000000000000..0ccff1a1463ac --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction.java @@ -0,0 +1,205 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.aggregation.SeenGroupIds; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link GroupingAggregatorFunction} implementation for {@link SpatialCentroidGeoPointDocValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final CentroidPointAggregator.GroupingCentroidState state; + + private final List channels; + + private final DriverContext driverContext; + + public SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction(List channels, + CentroidPointAggregator.GroupingCentroidState state, DriverContext driverContext) { + this.channels = channels; + this.state = state; + this.driverContext = driverContext; + } + + public static SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction create( + List channels, DriverContext driverContext) { + return new SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction(channels, SpatialCentroidGeoPointDocValuesAggregator.initGrouping(driverContext.bigArrays()), driverContext); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, + Page page) { + LongBlock valuesBlock = page.getBlock(channels.get(0)); + LongVector valuesVector = valuesBlock.asVector(); + if (valuesVector == null) { + if (valuesBlock.mayHaveNulls()) { + state.enableGroupIdTracking(seenGroupIds); + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + }; + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + }; + } + + private void addRawInput(int positionOffset, IntVector groups, LongBlock values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidGeoPointDocValuesAggregator.combine(state, groupId, values.getLong(v)); + } + } + } + + private void addRawInput(int positionOffset, IntVector groups, LongVector values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidGeoPointDocValuesAggregator.combine(state, groupId, values.getLong(groupPosition + positionOffset)); + } + } + + private void addRawInput(int positionOffset, IntBlock groups, LongBlock values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidGeoPointDocValuesAggregator.combine(state, groupId, values.getLong(v)); + } + } + } + } + + private void addRawInput(int positionOffset, IntBlock groups, LongVector values) { + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + SpatialCentroidGeoPointDocValuesAggregator.combine(state, groupId, values.getLong(groupPosition + positionOffset)); + } + } + } + + @Override + public void addIntermediateInput(int positionOffset, IntVector groups, Page page) { + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + assert channels.size() == intermediateBlockCount(); + DoubleVector xVal = page.getBlock(channels.get(0)).asVector(); + DoubleVector xDel = page.getBlock(channels.get(1)).asVector(); + DoubleVector yVal = page.getBlock(channels.get(2)).asVector(); + DoubleVector yDel = page.getBlock(channels.get(3)).asVector(); + LongVector count = page.getBlock(channels.get(4)).asVector(); + assert xVal.getPositionCount() == xDel.getPositionCount() && xVal.getPositionCount() == yVal.getPositionCount() && xVal.getPositionCount() == yDel.getPositionCount() && xVal.getPositionCount() == count.getPositionCount(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidGeoPointDocValuesAggregator.combineIntermediate(state, groupId, xVal.getDouble(groupPosition + positionOffset), xDel.getDouble(groupPosition + positionOffset), yVal.getDouble(groupPosition + positionOffset), yDel.getDouble(groupPosition + positionOffset), count.getLong(groupPosition + positionOffset)); + } + } + + @Override + public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) { + if (input.getClass() != getClass()) { + throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass()); + } + CentroidPointAggregator.GroupingCentroidState inState = ((SpatialCentroidGeoPointDocValuesGroupingAggregatorFunction) input).state; + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + SpatialCentroidGeoPointDocValuesAggregator.combineStates(state, groupId, inState, position); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) { + state.toIntermediate(blocks, offset, selected, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, IntVector selected, + DriverContext driverContext) { + blocks[offset] = SpatialCentroidGeoPointDocValuesAggregator.evaluateFinal(state, selected, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunction.java new file mode 100644 index 0000000000000..be7b8d9758d1c --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunction.java @@ -0,0 +1,157 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.aggregation.AggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunction} implementation for {@link SpatialCentroidGeoPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointSourceValuesAggregatorFunction implements AggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final DriverContext driverContext; + + private final CentroidPointAggregator.CentroidState state; + + private final List channels; + + public SpatialCentroidGeoPointSourceValuesAggregatorFunction(DriverContext driverContext, + List channels, CentroidPointAggregator.CentroidState state) { + this.driverContext = driverContext; + this.channels = channels; + this.state = state; + } + + public static SpatialCentroidGeoPointSourceValuesAggregatorFunction create( + DriverContext driverContext, List channels) { + return new SpatialCentroidGeoPointSourceValuesAggregatorFunction(driverContext, channels, SpatialCentroidGeoPointSourceValuesAggregator.initSingle()); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public void addRawInput(Page page) { + BytesRefBlock block = page.getBlock(channels.get(0)); + BytesRefVector vector = block.asVector(); + if (vector != null) { + addRawVector(vector); + } else { + addRawBlock(block); + } + } + + private void addRawVector(BytesRefVector vector) { + BytesRef scratch = new BytesRef(); + for (int i = 0; i < vector.getPositionCount(); i++) { + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch)); + } + } + + private void addRawBlock(BytesRefBlock block) { + BytesRef scratch = new BytesRef(); + for (int p = 0; p < block.getPositionCount(); p++) { + if (block.isNull(p)) { + continue; + } + int start = block.getFirstValueIndex(p); + int end = start + block.getValueCount(p); + for (int i = start; i < end; i++) { + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch)); + } + } + } + + @Override + public void addIntermediateInput(Page page) { + assert channels.size() == intermediateBlockCount(); + assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size(); + Block xValUncast = page.getBlock(channels.get(0)); + if (xValUncast.areAllValuesNull()) { + return; + } + DoubleVector xVal = ((DoubleBlock) xValUncast).asVector(); + assert xVal.getPositionCount() == 1; + Block xDelUncast = page.getBlock(channels.get(1)); + if (xDelUncast.areAllValuesNull()) { + return; + } + DoubleVector xDel = ((DoubleBlock) xDelUncast).asVector(); + assert xDel.getPositionCount() == 1; + Block yValUncast = page.getBlock(channels.get(2)); + if (yValUncast.areAllValuesNull()) { + return; + } + DoubleVector yVal = ((DoubleBlock) yValUncast).asVector(); + assert yVal.getPositionCount() == 1; + Block yDelUncast = page.getBlock(channels.get(3)); + if (yDelUncast.areAllValuesNull()) { + return; + } + DoubleVector yDel = ((DoubleBlock) yDelUncast).asVector(); + assert yDel.getPositionCount() == 1; + Block countUncast = page.getBlock(channels.get(4)); + if (countUncast.areAllValuesNull()) { + return; + } + LongVector count = ((LongBlock) countUncast).asVector(); + assert count.getPositionCount() == 1; + SpatialCentroidGeoPointSourceValuesAggregator.combineIntermediate(state, xVal.getDouble(0), xDel.getDouble(0), yVal.getDouble(0), yDel.getDouble(0), count.getLong(0)); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) { + state.toIntermediate(blocks, offset, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) { + blocks[offset] = SpatialCentroidGeoPointSourceValuesAggregator.evaluateFinal(state, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier.java new file mode 100644 index 0000000000000..02b975f03890f --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier.java @@ -0,0 +1,41 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.util.List; +import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link AggregatorFunctionSupplier} implementation for {@link SpatialCentroidGeoPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier { + private final List channels; + + public SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier(List channels) { + this.channels = channels; + } + + @Override + public SpatialCentroidGeoPointSourceValuesAggregatorFunction aggregator( + DriverContext driverContext) { + return SpatialCentroidGeoPointSourceValuesAggregatorFunction.create(driverContext, channels); + } + + @Override + public SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction groupingAggregator( + DriverContext driverContext) { + return SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction.create(channels, driverContext); + } + + @Override + public String describe() { + return "spatial_centroid_geo_point_source of valuess"; + } +} diff --git a/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction.java b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction.java new file mode 100644 index 0000000000000..30ef738669914 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/generated/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction.java @@ -0,0 +1,212 @@ +// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +// or more contributor license agreements. Licensed under the Elastic License +// 2.0; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.compute.aggregation.spatial; + +import java.lang.Integer; +import java.lang.Override; +import java.lang.String; +import java.lang.StringBuilder; +import java.util.List; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction; +import org.elasticsearch.compute.aggregation.IntermediateStateDesc; +import org.elasticsearch.compute.aggregation.SeenGroupIds; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.ElementType; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; + +/** + * {@link GroupingAggregatorFunction} implementation for {@link SpatialCentroidGeoPointSourceValuesAggregator}. + * This class is generated. Do not edit it. + */ +public final class SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction { + private static final List INTERMEDIATE_STATE_DESC = List.of( + new IntermediateStateDesc("xVal", ElementType.DOUBLE), + new IntermediateStateDesc("xDel", ElementType.DOUBLE), + new IntermediateStateDesc("yVal", ElementType.DOUBLE), + new IntermediateStateDesc("yDel", ElementType.DOUBLE), + new IntermediateStateDesc("count", ElementType.LONG) ); + + private final CentroidPointAggregator.GroupingCentroidState state; + + private final List channels; + + private final DriverContext driverContext; + + public SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction(List channels, + CentroidPointAggregator.GroupingCentroidState state, DriverContext driverContext) { + this.channels = channels; + this.state = state; + this.driverContext = driverContext; + } + + public static SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction create( + List channels, DriverContext driverContext) { + return new SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction(channels, SpatialCentroidGeoPointSourceValuesAggregator.initGrouping(driverContext.bigArrays()), driverContext); + } + + public static List intermediateStateDesc() { + return INTERMEDIATE_STATE_DESC; + } + + @Override + public int intermediateBlockCount() { + return INTERMEDIATE_STATE_DESC.size(); + } + + @Override + public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds, + Page page) { + BytesRefBlock valuesBlock = page.getBlock(channels.get(0)); + BytesRefVector valuesVector = valuesBlock.asVector(); + if (valuesVector == null) { + if (valuesBlock.mayHaveNulls()) { + state.enableGroupIdTracking(seenGroupIds); + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesBlock); + } + }; + } + return new GroupingAggregatorFunction.AddInput() { + @Override + public void add(int positionOffset, IntBlock groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + + @Override + public void add(int positionOffset, IntVector groupIds) { + addRawInput(positionOffset, groupIds, valuesVector); + } + }; + } + + private void addRawInput(int positionOffset, IntVector groups, BytesRefBlock values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch)); + } + } + } + + private void addRawInput(int positionOffset, IntVector groups, BytesRefVector values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch)); + } + } + + private void addRawInput(int positionOffset, IntBlock groups, BytesRefBlock values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + if (values.isNull(groupPosition + positionOffset)) { + continue; + } + int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset); + int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset); + for (int v = valuesStart; v < valuesEnd; v++) { + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch)); + } + } + } + } + + private void addRawInput(int positionOffset, IntBlock groups, BytesRefVector values) { + BytesRef scratch = new BytesRef(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + if (groups.isNull(groupPosition)) { + continue; + } + int groupStart = groups.getFirstValueIndex(groupPosition); + int groupEnd = groupStart + groups.getValueCount(groupPosition); + for (int g = groupStart; g < groupEnd; g++) { + int groupId = Math.toIntExact(groups.getInt(g)); + SpatialCentroidGeoPointSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch)); + } + } + } + + @Override + public void addIntermediateInput(int positionOffset, IntVector groups, Page page) { + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + assert channels.size() == intermediateBlockCount(); + DoubleVector xVal = page.getBlock(channels.get(0)).asVector(); + DoubleVector xDel = page.getBlock(channels.get(1)).asVector(); + DoubleVector yVal = page.getBlock(channels.get(2)).asVector(); + DoubleVector yDel = page.getBlock(channels.get(3)).asVector(); + LongVector count = page.getBlock(channels.get(4)).asVector(); + assert xVal.getPositionCount() == xDel.getPositionCount() && xVal.getPositionCount() == yVal.getPositionCount() && xVal.getPositionCount() == yDel.getPositionCount() && xVal.getPositionCount() == count.getPositionCount(); + for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) { + int groupId = Math.toIntExact(groups.getInt(groupPosition)); + SpatialCentroidGeoPointSourceValuesAggregator.combineIntermediate(state, groupId, xVal.getDouble(groupPosition + positionOffset), xDel.getDouble(groupPosition + positionOffset), yVal.getDouble(groupPosition + positionOffset), yDel.getDouble(groupPosition + positionOffset), count.getLong(groupPosition + positionOffset)); + } + } + + @Override + public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) { + if (input.getClass() != getClass()) { + throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass()); + } + CentroidPointAggregator.GroupingCentroidState inState = ((SpatialCentroidGeoPointSourceValuesGroupingAggregatorFunction) input).state; + state.enableGroupIdTracking(new SeenGroupIds.Empty()); + SpatialCentroidGeoPointSourceValuesAggregator.combineStates(state, groupId, inState, position); + } + + @Override + public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) { + state.toIntermediate(blocks, offset, selected, driverContext); + } + + @Override + public void evaluateFinal(Block[] blocks, int offset, IntVector selected, + DriverContext driverContext) { + blocks[offset] = SpatialCentroidGeoPointSourceValuesAggregator.evaluateFinal(state, selected, driverContext); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("["); + sb.append("channels=").append(channels); + sb.append("]"); + return sb.toString(); + } + + @Override + public void close() { + state.close(); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/module-info.java b/x-pack/plugin/esql/compute/src/main/java/module-info.java index 37c91dfd836a7..df6c883e952e5 100644 --- a/x-pack/plugin/esql/compute/src/main/java/module-info.java +++ b/x-pack/plugin/esql/compute/src/main/java/module-info.java @@ -25,5 +25,6 @@ exports org.elasticsearch.compute.operator; exports org.elasticsearch.compute.operator.exchange; exports org.elasticsearch.compute.aggregation.blockhash; + exports org.elasticsearch.compute.aggregation.spatial; exports org.elasticsearch.compute.operator.topn; } diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/CentroidPointAggregator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/CentroidPointAggregator.java new file mode 100644 index 0000000000000..1fc2430393c98 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/CentroidPointAggregator.java @@ -0,0 +1,281 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.aggregation.spatial; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.common.util.DoubleArray; +import org.elasticsearch.common.util.LongArray; +import org.elasticsearch.compute.aggregation.AggregatorState; +import org.elasticsearch.compute.aggregation.GroupingAggregatorState; +import org.elasticsearch.compute.aggregation.SeenGroupIds; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BlockFactory; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.geometry.Point; +import org.elasticsearch.geometry.utils.WellKnownBinary; +import org.elasticsearch.search.aggregations.metrics.CompensatedSum; + +import java.nio.ByteOrder; + +/** + * This aggregator calculates the centroid of a set of geo points or cartesian_points. + * It is assumes that the points are encoded as longs. + * This requires that the planner has planned that points are loaded from the index as doc-values. + */ +abstract class CentroidPointAggregator { + + public static void combine(CentroidState current, double xVal, double xDel, double yVal, double yDel, long count) { + current.add(xVal, xDel, yVal, yDel, count); + } + + public static void combineStates(CentroidState current, CentroidState state) { + current.add(state); + } + + public static void combineIntermediate(CentroidState state, double xIn, double dx, double yIn, double dy, long count) { + if (count > 0) { + combine(state, xIn, dx, yIn, dy, count); + } + } + + public static void evaluateIntermediate(CentroidState state, DriverContext driverContext, Block[] blocks, int offset) { + assert blocks.length >= offset + 5; + BlockFactory blockFactory = driverContext.blockFactory(); + blocks[offset + 0] = blockFactory.newConstantDoubleBlockWith(state.xSum.value(), 1); + blocks[offset + 1] = blockFactory.newConstantDoubleBlockWith(state.xSum.delta(), 1); + blocks[offset + 2] = blockFactory.newConstantDoubleBlockWith(state.ySum.value(), 1); + blocks[offset + 3] = blockFactory.newConstantDoubleBlockWith(state.ySum.delta(), 1); + blocks[offset + 4] = blockFactory.newConstantLongBlockWith(state.count, 1); + } + + public static Block evaluateFinal(CentroidState state, DriverContext driverContext) { + return driverContext.blockFactory().newConstantBytesRefBlockWith(state.encodeCentroidResult(), 1); + } + + public static void combineStates(GroupingCentroidState current, int groupId, GroupingCentroidState state, int statePosition) { + if (state.hasValue(statePosition)) { + current.add( + state.xValues.get(statePosition), + state.xDeltas.get(statePosition), + state.yValues.get(statePosition), + state.yDeltas.get(statePosition), + state.counts.get(statePosition), + groupId + ); + } + } + + public static void combineIntermediate( + GroupingCentroidState current, + int groupId, + double xValue, + double xDelta, + double yValue, + double yDelta, + long count + ) { + if (count > 0) { + current.add(xValue, xDelta, yValue, yDelta, count, groupId); + } + } + + public static void evaluateIntermediate( + GroupingCentroidState state, + Block[] blocks, + int offset, + IntVector selected, + DriverContext driverContext + ) { + assert blocks.length >= offset + 5; + try ( + var xValuesBuilder = driverContext.blockFactory().newDoubleBlockBuilder(selected.getPositionCount()); + var xDeltaBuilder = driverContext.blockFactory().newDoubleBlockBuilder(selected.getPositionCount()); + var yValuesBuilder = driverContext.blockFactory().newDoubleBlockBuilder(selected.getPositionCount()); + var yDeltaBuilder = driverContext.blockFactory().newDoubleBlockBuilder(selected.getPositionCount()); + var countsBuilder = driverContext.blockFactory().newLongBlockBuilder(selected.getPositionCount()); + ) { + for (int i = 0; i < selected.getPositionCount(); i++) { + int group = selected.getInt(i); + if (group < state.xValues.size()) { + xValuesBuilder.appendDouble(state.xValues.get(group)); + xDeltaBuilder.appendDouble(state.xDeltas.get(group)); + yValuesBuilder.appendDouble(state.yValues.get(group)); + yDeltaBuilder.appendDouble(state.yDeltas.get(group)); + countsBuilder.appendLong(state.counts.get(group)); + } else { + xValuesBuilder.appendDouble(0); + xDeltaBuilder.appendDouble(0); + yValuesBuilder.appendDouble(0); + yDeltaBuilder.appendDouble(0); + countsBuilder.appendLong(0); + } + } + blocks[offset + 0] = xValuesBuilder.build(); + blocks[offset + 1] = xDeltaBuilder.build(); + blocks[offset + 2] = yValuesBuilder.build(); + blocks[offset + 3] = yDeltaBuilder.build(); + blocks[offset + 4] = countsBuilder.build(); + } + } + + public static Block evaluateFinal(GroupingCentroidState state, IntVector selected, DriverContext driverContext) { + try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(selected.getPositionCount())) { + for (int i = 0; i < selected.getPositionCount(); i++) { + int si = selected.getInt(i); + if (state.hasValue(si) && si < state.xValues.size()) { + BytesRef result = state.encodeCentroidResult(si); + builder.appendBytesRef(result); + } else { + builder.appendNull(); + } + } + return builder.build(); + } + } + + private static BytesRef encode(double x, double y) { + return new BytesRef(WellKnownBinary.toWKB(new Point(x, y), ByteOrder.LITTLE_ENDIAN)); + } + + static class CentroidState implements AggregatorState { + protected final CompensatedSum xSum = new CompensatedSum(0, 0); + protected final CompensatedSum ySum = new CompensatedSum(0, 0); + protected long count = 0; + + @Override + public void toIntermediate(Block[] blocks, int offset, DriverContext driverContext) { + CentroidPointAggregator.evaluateIntermediate(this, driverContext, blocks, offset); + } + + @Override + public void close() {} + + public void count(long count) { + this.count = count; + } + + public void add(CentroidState other) { + xSum.add(other.xSum.value(), other.xSum.delta()); + ySum.add(other.ySum.value(), other.ySum.delta()); + count += other.count; + } + + public void add(double x, double y) { + xSum.add(x); + ySum.add(y); + count++; + } + + public void add(double x, double dx, double y, double dy, long count) { + xSum.add(x, dx); + ySum.add(y, dy); + this.count += count; + } + + protected BytesRef encodeCentroidResult() { + double x = xSum.value() / count; + double y = ySum.value() / count; + return encode(x, y); + } + } + + static class GroupingCentroidState implements GroupingAggregatorState { + private final BigArrays bigArrays; + + DoubleArray xValues; + DoubleArray xDeltas; + DoubleArray yValues; + DoubleArray yDeltas; + + LongArray counts; + + GroupingCentroidState(BigArrays bigArrays) { + this.bigArrays = bigArrays; + boolean success = false; + try { + this.xValues = bigArrays.newDoubleArray(1); + this.xDeltas = bigArrays.newDoubleArray(1); + this.yValues = bigArrays.newDoubleArray(1); + this.yDeltas = bigArrays.newDoubleArray(1); + this.counts = bigArrays.newLongArray(1); + success = true; + } finally { + if (success == false) { + close(); + } + } + } + + void add(double x, double dx, double y, double dy, long count, int groupId) { + ensureCapacity(groupId); + + // If the value is Inf or NaN, just add it to the running tally to "convert" to + // Inf/NaN. This keeps the behavior bwc from before kahan summing + if (Double.isFinite(x) == false || Double.isFinite(y) == false) { + xValues.increment(groupId, x); + yValues.increment(groupId, y); + return; + } + + addTo(xValues, xDeltas, groupId, x, dx); + addTo(yValues, yDeltas, groupId, y, dy); + counts.increment(groupId, count); + } + + private static void addTo(DoubleArray values, DoubleArray deltas, int groupId, double valueToAdd, double deltaToAdd) { + double value = values.get(groupId); + if (Double.isFinite(value) == false) { + // It isn't going to get any more infinite. + return; + } + double delta = deltas.get(groupId); + double correctedSum = valueToAdd + (delta + deltaToAdd); + double updatedValue = value + correctedSum; + deltas.set(groupId, correctedSum - (updatedValue - value)); + values.set(groupId, updatedValue); + } + + boolean hasValue(int index) { + return counts.get(index) > 0; + } + + /** Needed for generated code that does null tracking, which we do not need because we use count */ + final void enableGroupIdTracking(SeenGroupIds ignore) {} + + private void ensureCapacity(int groupId) { + if (groupId >= xValues.size()) { + xValues = bigArrays.grow(xValues, groupId + 1); + xDeltas = bigArrays.grow(xDeltas, groupId + 1); + yValues = bigArrays.grow(yValues, groupId + 1); + yDeltas = bigArrays.grow(yDeltas, groupId + 1); + counts = bigArrays.grow(counts, groupId + 1); + } + } + + protected BytesRef encodeCentroidResult(int si) { + long count = counts.get(si); + double x = xValues.get(si) / count; + double y = yValues.get(si) / count; + return encode(x, y); + } + + @Override + public void toIntermediate(Block[] blocks, int offset, IntVector selected, DriverContext driverContext) { + CentroidPointAggregator.evaluateIntermediate(this, blocks, offset, selected, driverContext); + } + + @Override + public void close() { + Releasables.close(xValues, xDeltas, yValues, yDeltas, counts); + } + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregator.java new file mode 100644 index 0000000000000..0bafb6f8112de --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointDocValuesAggregator.java @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.aggregation.spatial; + +import org.apache.lucene.geo.XYEncodingUtils; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.ann.Aggregator; +import org.elasticsearch.compute.ann.GroupingAggregator; +import org.elasticsearch.compute.ann.IntermediateState; + +/** + * This aggregator calculates the centroid of a set of cartesian points. + * It is assumes that the cartesian points are encoded as longs. + * This requires that the planner has planned that points are loaded from the index as doc-values. + */ +@Aggregator( + { + @IntermediateState(name = "xVal", type = "DOUBLE"), + @IntermediateState(name = "xDel", type = "DOUBLE"), + @IntermediateState(name = "yVal", type = "DOUBLE"), + @IntermediateState(name = "yDel", type = "DOUBLE"), + @IntermediateState(name = "count", type = "LONG") } +) +@GroupingAggregator +class SpatialCentroidCartesianPointDocValuesAggregator extends CentroidPointAggregator { + + public static CentroidState initSingle() { + return new CentroidState(); + } + + public static GroupingCentroidState initGrouping(BigArrays bigArrays) { + return new GroupingCentroidState(bigArrays); + } + + public static void combine(CentroidState current, long v) { + current.add(decodeX(v), decodeY(v)); + } + + public static void combine(GroupingCentroidState current, int groupId, long encoded) { + current.add(decodeX(encoded), 0d, decodeY(encoded), 0d, 1, groupId); + } + + private static double decodeX(long encoded) { + return XYEncodingUtils.decode((int) (encoded >>> 32)); + } + + private static double decodeY(long encoded) { + return XYEncodingUtils.decode((int) (encoded & 0xFFFFFFFFL)); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregator.java new file mode 100644 index 0000000000000..5673892be4bf0 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidCartesianPointSourceValuesAggregator.java @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.aggregation.spatial; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.ann.Aggregator; +import org.elasticsearch.compute.ann.GroupingAggregator; +import org.elasticsearch.compute.ann.IntermediateState; +import org.elasticsearch.geometry.Point; +import org.elasticsearch.geometry.utils.GeometryValidator; +import org.elasticsearch.geometry.utils.WellKnownBinary; + +/** + * This aggregator calculates the centroid of a set of cartesian points. + * It is assumes that the cartesian points are encoded as WKB BytesRef. + * This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead. + * This is also used for final aggregations and aggregations in the coordinator node, + * even if the local node partial aggregation is done with {@link SpatialCentroidCartesianPointSourceValuesAggregator}. + */ +@Aggregator( + { + @IntermediateState(name = "xVal", type = "DOUBLE"), + @IntermediateState(name = "xDel", type = "DOUBLE"), + @IntermediateState(name = "yVal", type = "DOUBLE"), + @IntermediateState(name = "yDel", type = "DOUBLE"), + @IntermediateState(name = "count", type = "LONG") } +) +@GroupingAggregator +class SpatialCentroidCartesianPointSourceValuesAggregator extends CentroidPointAggregator { + + public static CentroidState initSingle() { + return new CentroidState(); + } + + public static GroupingCentroidState initGrouping(BigArrays bigArrays) { + return new GroupingCentroidState(bigArrays); + } + + public static void combine(CentroidState current, BytesRef wkb) { + Point point = decode(wkb); + current.add(point.getX(), point.getY()); + } + + public static void combine(GroupingCentroidState current, int groupId, BytesRef wkb) { + Point point = decode(wkb); + current.add(point.getX(), 0d, point.getY(), 0d, 1, groupId); + } + + private static Point decode(BytesRef wkb) { + return (Point) WellKnownBinary.fromWKB(GeometryValidator.NOOP, false, wkb.bytes, wkb.offset, wkb.length); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregator.java new file mode 100644 index 0000000000000..ee5ab0e292547 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointDocValuesAggregator.java @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.aggregation.spatial; + +import org.apache.lucene.geo.GeoEncodingUtils; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.ann.Aggregator; +import org.elasticsearch.compute.ann.GroupingAggregator; +import org.elasticsearch.compute.ann.IntermediateState; + +/** + * This aggregator calculates the centroid of a set of geo points. It is assumes that the geo points are encoded as longs. + * This requires that the planner has planned that points are loaded from the index as doc-values. + */ +@Aggregator( + { + @IntermediateState(name = "xVal", type = "DOUBLE"), + @IntermediateState(name = "xDel", type = "DOUBLE"), + @IntermediateState(name = "yVal", type = "DOUBLE"), + @IntermediateState(name = "yDel", type = "DOUBLE"), + @IntermediateState(name = "count", type = "LONG") } +) +@GroupingAggregator +class SpatialCentroidGeoPointDocValuesAggregator extends CentroidPointAggregator { + + public static CentroidState initSingle() { + return new CentroidState(); + } + + public static GroupingCentroidState initGrouping(BigArrays bigArrays) { + return new GroupingCentroidState(bigArrays); + } + + public static void combine(CentroidState current, long v) { + current.add(decodeX(v), decodeY(v)); + } + + public static void combine(GroupingCentroidState current, int groupId, long encoded) { + current.add(decodeX(encoded), 0d, decodeY(encoded), 0d, 1, groupId); + } + + private static double decodeX(long encoded) { + return GeoEncodingUtils.decodeLongitude((int) (encoded & 0xFFFFFFFFL)); + } + + private static double decodeY(long encoded) { + return GeoEncodingUtils.decodeLatitude((int) (encoded >>> 32)); + } +} diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregator.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregator.java new file mode 100644 index 0000000000000..caf55dcc2f4e1 --- /dev/null +++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/aggregation/spatial/SpatialCentroidGeoPointSourceValuesAggregator.java @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.compute.aggregation.spatial; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.util.BigArrays; +import org.elasticsearch.compute.ann.Aggregator; +import org.elasticsearch.compute.ann.GroupingAggregator; +import org.elasticsearch.compute.ann.IntermediateState; +import org.elasticsearch.geometry.Point; +import org.elasticsearch.geometry.utils.GeometryValidator; +import org.elasticsearch.geometry.utils.WellKnownBinary; + +/** + * This aggregator calculates the centroid of a set of geo points. + * It is assumes that the geo points are encoded as WKB BytesRef. + * This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead. + * This is also used for final aggregations and aggregations in the coordinator node, + * even if the local node partial aggregation is done with {@link SpatialCentroidGeoPointDocValuesAggregator}. + */ +@Aggregator( + { + @IntermediateState(name = "xVal", type = "DOUBLE"), + @IntermediateState(name = "xDel", type = "DOUBLE"), + @IntermediateState(name = "yVal", type = "DOUBLE"), + @IntermediateState(name = "yDel", type = "DOUBLE"), + @IntermediateState(name = "count", type = "LONG") } +) +@GroupingAggregator +class SpatialCentroidGeoPointSourceValuesAggregator extends CentroidPointAggregator { + + public static CentroidState initSingle() { + return new CentroidState(); + } + + public static GroupingCentroidState initGrouping(BigArrays bigArrays) { + return new GroupingCentroidState(bigArrays); + } + + public static void combine(CentroidState current, BytesRef wkb) { + Point point = decode(wkb); + current.add(point.getX(), point.getY()); + } + + public static void combine(GroupingCentroidState current, int groupId, BytesRef wkb) { + Point point = decode(wkb); + current.add(point.getX(), 0d, point.getY(), 0d, 1, groupId); + } + + private static Point decode(BytesRef wkb) { + return (Point) WellKnownBinary.fromWKB(GeometryValidator.NOOP, false, wkb.bytes, wkb.offset, wkb.length); + } +} diff --git a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java index 330a7293a9a67..1ba9fa5d1d354 100644 --- a/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java +++ b/x-pack/plugin/esql/compute/src/test/java/org/elasticsearch/compute/lucene/ValuesSourceReaderOperatorTests.java @@ -494,6 +494,11 @@ public String indexName() { return "test_index"; } + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return MappedFieldType.FieldExtractPreference.NONE; + } + @Override public SearchLookup lookup() { throw new UnsupportedOperationException(); diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java index fd686ec48bb79..63f3e27830fa8 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java @@ -12,11 +12,17 @@ import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.geometry.Geometry; +import org.elasticsearch.geometry.Point; +import org.elasticsearch.geometry.utils.GeometryValidator; +import org.elasticsearch.geometry.utils.WellKnownText; import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.Logger; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.xcontent.XContentType; +import org.elasticsearch.xpack.esql.CsvTestUtils; import org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.RequestObjectBuilder; import org.elasticsearch.xpack.ql.CsvSpecReader.CsvTestCase; import org.elasticsearch.xpack.ql.SpecReader; @@ -28,8 +34,13 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Map; +import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude; +import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude; +import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude; +import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude; import static org.elasticsearch.test.MapMatcher.assertMap; import static org.elasticsearch.test.MapMatcher.matchesMap; import static org.elasticsearch.xpack.esql.CsvAssert.assertData; @@ -161,7 +172,34 @@ protected void assertResults( Logger logger ) { assertMetadata(expected, actualColumns, logger); - assertData(expected, actualValues, testCase.ignoreOrder, logger, value -> value == null ? "null" : value.toString()); + assertData(expected, actualValues, testCase.ignoreOrder, logger, EsqlSpecTestCase::valueMapper); + } + + private static Object valueMapper(CsvTestUtils.Type type, Object value) { + if (value == null) { + return "null"; + } + if (type == CsvTestUtils.Type.GEO_POINT) { + // GeoPoint tests are failing in clustered integration tests because of tiny precision differences at very small scales + if (value instanceof GeoPoint point) { + return normalizedGeoPoint(point.getX(), point.getY()); + } + if (value instanceof String wkt) { + try { + Geometry geometry = WellKnownText.fromWKT(GeometryValidator.NOOP, false, wkt); + if (geometry instanceof Point point) { + return normalizedGeoPoint(point.getX(), point.getY()); + } + } catch (Throwable ignored) {} + } + } + return value.toString(); + } + + private static String normalizedGeoPoint(double x, double y) { + x = decodeLongitude(encodeLongitude(x)); + y = decodeLatitude(encodeLatitude(y)); + return String.format(Locale.ROOT, "POINT (%f %f)", x, y); } private Throwable reworkException(Throwable th) { diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java index 8886951030c07..38bd05d57d768 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Function; import static org.elasticsearch.common.logging.LoggerMessageFormat.format; @@ -155,7 +156,7 @@ private static void assertMetadata( } static void assertData(ExpectedResults expected, ActualResults actual, boolean ignoreOrder, Logger logger) { - assertData(expected, actual.values(), ignoreOrder, logger, v -> v); + assertData(expected, actual.values(), ignoreOrder, logger, (t, v) -> v); } public static void assertData( @@ -163,7 +164,7 @@ public static void assertData( Iterator> actualValuesIterator, boolean ignoreOrder, Logger logger, - Function valueTransformer + BiFunction valueTransformer ) { assertData(expected, EsqlTestUtils.getValuesList(actualValuesIterator), ignoreOrder, logger, valueTransformer); } @@ -173,7 +174,7 @@ public static void assertData( List> actualValues, boolean ignoreOrder, Logger logger, - Function valueTransformer + BiFunction valueTransformer ) { if (ignoreOrder) { expected.values().sort(resultRowComparator(expected.columnTypes())); @@ -195,9 +196,9 @@ public static void assertData( for (int column = 0; column < expectedRow.size(); column++) { var expectedValue = expectedRow.get(column); var actualValue = actualRow.get(column); + var expectedType = expected.columnTypes().get(column); if (expectedValue != null) { - var expectedType = expected.columnTypes().get(column); // convert the long from CSV back to its STRING form if (expectedType == Type.DATETIME) { expectedValue = rebuildExpected(expectedValue, Long.class, x -> UTC_DATE_TIME_FORMATTER.formatMillis((long) x)); @@ -221,8 +222,8 @@ public static void assertData( } assertEquals( "Row[" + row + "] Column[" + column + "]", - valueTransformer.apply(expectedValue), - valueTransformer.apply(actualValue) + valueTransformer.apply(expectedType, expectedValue), + valueTransformer.apply(expectedType, actualValue) ); } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/airports.csv b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/airports.csv index 8c20e876385a5..1594c6b8f54f0 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/airports.csv +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/airports.csv @@ -1,892 +1,892 @@ -abbrev:keyword,name:text, scalerank:integer,type:keyword, location:geo_point -LUH, Sahnewal, 9, small, POINT(75.9570722403652 30.8503598561702) -SSE, Solapur, 9, mid, POINT(75.9330597710755 17.625415183635) -IXR, Birsa Munda, 9, mid, POINT(85.3235970368767 23.3177245989962) -AWZ, Ahwaz, 9, mid, POINT(48.7471065435931 31.3431585560757) -GWL, Gwalior, 9, [mid,military], POINT(78.2172186546348 26.285487697937) -HOD, Hodeidah Int'l, 9, mid, POINT(42.97109630194 14.7552534413725) -IDR, Devi Ahilyabai Holkar Int'l, 9, mid, POINT(75.8092915005895 22.727749187571) -ISK, Gandhinagar, 9, mid, POINT(73.8105674924689 19.9660205672806) -IXC, Chandigarh Int'l, 9, [major,military], POINT(76.8017261105242 30.6707248949667) -IXU, Aurangabad, 9, mid, POINT(75.3958432922005 19.8672969621082) -LYP, Faisalabad Int'l, 9, [mid,military], POINT(72.9878190922305 31.3627435480862) -OMS, Omsk Tsentralny, 9, mid, POINT(73.3163595376585 54.9576482934059) -OVB, Novosibirsk Tolmachev, 9, mid, POINT(82.6671524525865 55.0095847136264) -OZH, Zaporozhye Int'l, 9, [mid,military], POINT(35.3018728575279 47.8732635579023) -PKU, Simpang Tiga, 9, mid, POINT(101.446569298441 0.464600872998505) -ROP, Rota Int'l, 9, mid, POINT(145.243980298582 14.1717712971216) -SGC, Surgut, 9, mid, POINT(73.4084964764375 61.3401672194481) -TRZ, Tiruchirappalli, 9, mid, POINT(78.7089578747476 10.7603571306554) -TUK, Turbat Int'l, 9, mid, POINT(63.0279333519181 25.988794590011) -UET, Quetta Int'l, 9, mid, POINT(66.9487311480949 30.249043186181) -ZAH, Zahedan Int'l, 9, mid, POINT(60.900708564915 29.4752941956573) -MLG, Abdul Rachman Saleh, 9, [mid,military], POINT(112.711418617258 -7.92998002840567) -BAX, Barnaul, 9, mid, POINT(83.5504532124038 53.3633850813046) -VIAX, Adampur, 9, [military,mid], POINT(75.7584828456005 31.4329422397715) -VIBY, Bareilly, 9, military, POINT(79.452002687657 28.4218087161144) -OPQS, Dhamial, 9, small, POINT(73.0320498392002 33.5614146278861) -CJJ, Cheongju Int'l, 9, major, POINT(127.495916124681 36.7220227766673) -KWJ, Gwangju, 9, [mid,military], POINT(126.810839481226 35.1400051390198) -TAE, Daegu Int'l, 9, mid, POINT(128.637537699933 35.8999277969087) -USN, Ulsan, 9, mid, POINT(129.355731047528 35.5928957527107) -WIIT, Radin Inten II, 9, mid, POINT(105.176060419161 -5.242566777132) -IXD, Allahabad, 9, military, POINT(81.7317271462187 25.443522027821) -CEK, Chelyabinsk, 9, mid, POINT(61.5122589740201 55.2977919496055) -TNN, Tainan, 8, [military,mid], POINT(120.209733318093 22.950667918347) -RMQ, Taichung, 8, [military,mid], POINT(120.630703547584 24.2666555567115) -RTM, Rotterdam The Hague, 8, mid, POINT(4.43384434962876 51.9491301899382) -VOZ, Voronezh-Chertovitskoye, 8, mid, POINT(39.2254496447973 51.8126171268344) -LPL, Liverpool John Lennon, 8, major, POINT(-2.85862065784938 53.3363751054422) -VTZ, Vishakapatnam, 8, mid, POINT(83.2235216387465 17.7279577384364) -UPG, Sultan Hasanuddin Int'l, 8, major, POINT(119.545691342151 -5.05893689455779) -VAV, Vava'u Int'l, 8, mid, POINT(-173.968093944159 -18.5860058550654) -NCL, Newcastle Int'l, 8, major, POINT(-1.71034578407216 55.037084860802) -LCE, Goloson Int'l, 8, mid, POINT(-86.8514685020011 15.7451596659126) -MED, Madinah Int'l, 8, major, POINT(39.6991359560417 24.5442339605661) -YMX, Mirabel Int'l, 8, mid, POINT(-74.0287382984814 45.6832250979267) -PLQ, Palanga Int'l, 8, mid, POINT(21.0974463986251 55.9713426235358) -JAI, Jaipur Int'l, 8, mid, POINT(75.8010104192668 26.8211798100605) -IXW, Sonari, 8, mid, POINT(86.1724662363776 22.8154145110242) -YEI, Yenisehir, 8, mid, POINT(29.54492 40.2555395007473) -ADA, Şakirpaşa, 8, major, POINT(35.2969614268338 36.9852090641795) -ADQ, Kodiak, 8, mid, POINT(-152.485638515235 57.7485921070483) -AMA, Amarillo Int'l, 8, major, POINT(-101.705352772697 35.2184031919398) -ASP, Alice Springs, 8, mid, POINT(133.902918 -23.801968) -ATQ, Raja Sansi Int'l, 8, [mid,military], POINT(74.8071559719824 31.7068220258888) -BBI, Biju Patnaik, 8, mid, POINT(85.8168899544429 20.2526659754734) -BET, Bethel, 8, mid, POINT(-161.83898695944 60.7787379834088) -BGA, Palonegro, 8, mid, POINT(-73.1809207725361 7.12770915402685) -BHM, Birmingham Int'l, 8, major, POINT(-86.7523773615462 33.5618672828058) -BHQ, Broken Hill, 8, mid, POINT(141.470407303097 -31.998996737463) -BIL, Logan Int'l, 8, major, POINT(-108.536929388125 45.8036855715278) -BIS, Bismarck Muni., 8, mid, POINT(-100.757471303717 46.7751066661614) -BJX, Del Bajio Int'l, 8, mid, POINT(-101.478753382467 20.9858871211938) -BNI, Benin, 8, mid, POINT(5.603682560067 6.31716689207818) -BOJ, Bourgas, 8, major, POINT(27.5164093662953 42.5670835487702) -BRE, Bremen, 8, major, POINT(8.7858617703132 53.052287104156) -BRM, Jacinto Lara Int'l, 8, mid, POINT(-69.356102 10.0453) -BRO, Brownsville-South Padre Island Int'l, 8, mid, POINT(-97.431765340232 25.9062743545347) -BRS, Bristol Int'l, 8, major, POINT(-2.71086469134308 51.3862934189148) -BTR, Baton Rouge Metro, 8, major, POINT(-91.1567544048105 30.5326138040586) -BTS, Bratislava-M.R. Štefánik, 8, major, POINT(17.1999850022208 48.1698379062535) -BTV, Burlington Int'l, 8, mid, POINT(-73.1550787790668 44.4692066040732) -CAE, Columbia Metro, 8, major, POINT(-81.1093352429377 33.9342054584275) -CCJ, Calicut Int'l, 8, major, POINT(75.950993063051 11.1395520526064) -CCK, Cocos (Keeling) Islands, 8, mid, POINT(96.8287472144207 -12.1851585953293) -CFU, Corfu Int'l (Ioannis Kapodistrias), 8, mid, POINT(19.9147561641662 39.6067554505259) -CGQ, Changchun Longjia Int'l, 8, major, POINT(125.690456812998 43.993011479577) -CHS, Charleston Int'l, 8, [major,military], POINT(-80.0369337438262 32.8845301562965) -CJB, Coimbatore, 8, mid, POINT(77.038893772262 11.0301415125983) -CLJ, Someseni, 8, mid, POINT(23.6869812680486 46.7826626340243) -CMW, Ignacio Agramonte, 8, mid, POINT(-77.8451039935167 21.4247037281961) -CPR, Casper/Natrona County Int'l, 8, major, POINT(-106.464444809692 42.8971900483006) -CRK, Clark Int'l, 8, major, POINT(120.550770223914 15.1876422423888) -CRW, Yeager, 8, [major,military], POINT(-81.5964164667526 38.3705914372865) -CTA, Catania Fontanarossa, 8, major, POINT(15.0674605007053 37.470072800341) -CTM, Chetumal Int'l, 8, mid, POINT(-88.3242600415365 18.506434233376) -CWL, Cardiff, 8, major, POINT(-3.33956876429118 51.3986220911017) -CYB, Gerrard Smith, 8, mid, POINT(-79.879461638003 19.6898653962844) -CZM, Cozumel Int'l, 8, mid, POINT(-86.9304064070436 20.5115543771647) -DAY, James M. Cox Dayton Int'l, 8, major, POINT(-84.2204594238102 39.8990402865362) -DBO, Dubbo, 8, mid, POINT(148.569717 -32.218681) -DCA, Washington Nat'l, 8, major, POINT(-77.0433373925631 38.8537162012123) -DGO, Durango Int'l, 8, mid, POINT(-104.533846024964 24.1261948326182) -DNK, Voloskoye, 8, mid, POINT(35.0939060224975 48.3675718021117) -DOK, Donetsk, 8, major, POINT(37.7448085572103 48.0691671285582) -DZO, Santa Bernardina Int'l, 8, mid, POINT(-56.4992636213744 -33.3591084475501) -EDI, Edinburgh Int'l, 8, major, POINT(-3.36428468513554 55.9485540113639) -EIS, Terrance B. Lettsome Int'l, 8, mid, POINT(-64.5371514365794 18.4443618557983) -EKO, Elko Reg., 8, mid, POINT(-115.786479232249 40.8276058815225) -ESE, Ensenada, 8, mid, POINT(-116.595724400418 31.7977139760569) -FAE, Vágar, 8, mid, POINT(-7.2708 62.0625) -FAR, Hector Int'l, 8, [mid,military], POINT(-96.8254561269675 46.9198178811323) -FAT, Fresno Yosemite Int'l, 8, mid, POINT(-119.720001323576 36.7698128373959) -FLG, Flagstaff Pulliam, 8, mid, POINT(-111.674656171675 35.1389116757976) -FRS, Mundo Maya Int'l, 8, mid, POINT(-89.8778404226508 16.9149741642226) -FSD, Sioux Falls Reg., 8, mid, POINT(-96.7313831017541 43.5801934972763) -GEG, Spokane Int'l, 8, major, POINT(-117.536836628585 47.6254781278368) -GGT, Exuma Int'l, 8, mid, POINT(-75.872613085304 23.5638829069259) -GIB, Gibraltar, 8, mid, POINT(-5.34677180033388 36.1512747504173) -GRR, Gerald R. Ford Int'l, 8, mid, POINT(-85.529573207274 42.8847776020908) -GSO, Triad Int'l, 8, major, POINT(-79.9364867577484 36.1053781998932) -GTF, Great Falls Int'l, 8, mid, POINT(-111.35668472784 47.482270729263) -GZT, Gaziantep Oğuzeli Int'l, 8, major, POINT(37.47380325219 36.9453633446875) -HBX, Hubli, 8, mid, POINT(75.0863155680281 15.3591833386229) -HDY, Hat Yai Int'l, 8, mid, POINT(100.393751274671 6.93634231940664) -HFE, Hefei-Luogang, 8, mid, POINT(117.304197015888 31.7798576795778) -HRG, Hurghada Int'l, 8, major, POINT(33.8071606414118 27.1804260918186) -HRK, Kharkov Int'l, 8, major, POINT(36.2822010773503 49.9215360631551) -HSV, Huntsville Int'l, 8, major, POINT(-86.7749430563373 34.6483344609319) -IBA, Ibadan, 8, mid, POINT(3.9738133433229 7.36034397269393) -ICT, Kansas City Int'l, 8, major, POINT(-97.4287387683976 37.6529279603903) -ILM, Wilmington Int'l, 8, mid, POINT(-77.9103756560469 34.2667840671996) -ILR, Ilorin Int'l, 8, mid, POINT(4.49484038819934 8.43537651935241) -INL, Falls Int'l, 8, mid, POINT(-93.3980027552794 48.5659930848414) -INV, Inverness, 8, mid, POINT(-4.06359175587141 57.5395002923424) -IPL, Imperial Cty., 8, mid, POINT(-115.57199556658 32.8339586685524) -IXJ, Jammu, 8, mid, POINT(74.8423077638915 32.6810428886225) -IXM, Madurai, 8, mid, POINT(78.0911394937194 9.83718627877566) -JDH, Jodhpur, 8, [major,military], POINT(73.0505491895671 26.2637623458351) -JLR, Jabalpur, 8, mid, POINT(80.0587438885277 23.1845328746465) -JRO, Kilimanjaro Int'l, 8, mid, POINT(37.0651896067748 -3.42444495998178) -KAD, Kaduna, 8, mid, POINT(7.32525347407434 10.6946192862391) -KGA, Kananga, 8, mid, POINT(22.4783332482689 -5.90016656227041) -KMS, Kumasi, 8, mid, POINT(-1.59257526582361 6.71460638750365) -KNA, Viña del Mar, 8, mid, POINT(-71.4806025354969 -32.948391765136) -KNU, Kanpur, 8, mid, POINT(80.3675338772002 26.4388334467042) -KOA, Kona Int'l at Keahole, 8, mid, POINT(-156.040889471058 19.7370991399442) -KOI, Kirkwall, 8, mid, POINT(-2.90137849524508 58.9544288788303) -KTU, Kota, 8, mid, POINT(75.8504977944552 25.1634187166743) -KYA, Konya, 8, [major,military], POINT(32.5756732669687 37.9839945531695) -LEX, Blue Grass, 8, major, POINT(-84.5982681918786 38.0374273181372) -LIH, Lihue, 8, mid, POINT(-159.349084290522 21.9781243162088) -LIT, Clinton National, 8, major, POINT(-92.2205881319289 34.7284300415179) -LMM, Los Mochis, 8, mid, POINT(-109.082694645261 25.688508826099) -LOV, Venustiano Carranza Int'l, 8, mid, POINT(-101.464960031751 26.9553927160699) -LRD, Laredo Int'l, 8, mid, POINT(-99.4556603976513 27.5436657175825) -LSI, Sumburgh, 8, mid, POINT(-1.28806068838753 59.8766899598999) -LTK, Bassel Al-Assad Int'l, 8, major, POINT(35.9442407096663 35.4073114596744) -LTN, London Luton, 8, major, POINT(-0.376227267397439 51.8802952570969) -LYR, Svalbard Longyear, 8, mid, POINT(15.495229 78.246717) -MBJ, Sangster Int'l, 8, mid, POINT(-77.9183907635752 18.5011549298249) -MDL, Mandalay Int'l, 8, mid, POINT(95.9706535950217 21.7055490680274) -MDW, Chicago Midway Int'l, 8, major, POINT(-87.7421266885612 41.7883492597409) -MEC, Eloy Alfaro Int'l, 8, [mid,military], POINT(-80.6833845995774 -0.949557002112883) -MGM, Montgomery Reg., 8, major, POINT(-86.3903074602686 32.3045879909631) -MHT, Manchester-Boston Reg., 8, major, POINT(-71.4375239091857 42.9279139945886) -DNMA, Maiduguri Int'l, 8, mid, POINT(13.0851390162471 11.8534713188527) -MJM, Mbuji Mayi, 8, mid, POINT(23.5721091989052 -6.12484541348812) -MOT, Minot Int'l, 8, mid, POINT(-101.2913855313 48.2556049212839) -MSO, Missoula Int'l, 8, mid, POINT(-114.083694923651 46.9187604768831) -MXL, Gen R.S. Taboada Int'l, 8, mid, POINT(-115.247874047841 32.6285643324607) -MXP, Malpensa, 8, major, POINT(8.71295953502437 45.6274405140381) -NLK, Norfolk Island, 8, mid, POINT(167.943394116205 -29.0351592555275) -NUE, Nurnberg, 8, major, POINT(11.0774179739096 49.4945052170345) -ODS, Odessa Int'l, 8, major, POINT(30.6768308310206 46.4406268759106) -OOL, Gold Coast, 8, mid, POINT(153.512876264303 -28.1665168540202) -ORN, Oran Es Senia, 8, mid, POINT(-0.60679696443112 35.6202747312734) -PAT, Lok Nayak Jaiprakash, 8, mid, POINT(85.0909021314663 25.5944434295605) -PDU, Paysandu, 8, mid, POINT(-58.0685346825257 -32.3614545292723) -PFO, Paphos Int'l, 8, major, POINT(32.4832322064926 34.7134012817335) -PLM, Sultan Mahmud Badaruddin II, 8, mid, POINT(104.699128326762 -2.89999345005997) -PTG, Polokwane Int'l, 8, mid, POINT(29.4533403645644 -23.858986270166) -PUJ, Punta Cana, 8, mid, POINT(-68.3632351074649 18.563039033987) -QRO, Queretaro Int'l, 8, mid, POINT(-100.18735943003 20.622466071278) -RAJ, Rajkot, 8, mid, POINT(70.7799548311565 22.3092816988361) -RIC, Richmond Int'l, 8, major, POINT(-77.333119638113 37.5082899750901) -RJH, Shah Makhdum, 8, mid, POINT(88.6138045704431 24.4448068623035) -ROC, Greater Rochester Int'l, 8, major, POINT(-77.6652445062197 43.1275519826482) -ROK, Rockhampton, 8, mid, POINT(150.478897 -23.378599) -ROV, Rostov-on-Don, 8, mid, POINT(39.8035144445391 47.2551119519754) -RTW, Saratov, 8, mid, POINT(46.035023249891 51.5606456508842) -SAP, Ramón Villeda Morales Int'l, 8, mid, POINT(-87.9272365125409 15.4558630524883) -SBA, Santa Barbara Muni., 8, mid, POINT(-119.8366015808 34.4257312978783) -SCC, Deadhorse, 8, mid, POINT(-148.457855 70.19751) -SFJ, Kangerlussuaq, 8, mid, POINT(-50.694199 67.018097) -SGF, Springfield Reg., 8, major, POINT(-93.3826379012003 37.2421444903024) -SHV, Shreveport Reg., 8, major, POINT(-93.8285222229503 32.4545798866513) -SIP, Simferopol Int'l, 8, major, POINT(33.9960529244537 45.0202173978165) -SIT, Sitka Rocky Gutierrez, 8, mid, POINT(-135.365692 57.05349) -SJD, Los Cabos Int'l, 8, major, POINT(-109.717858386909 23.1626574483597) -SLE, McNary Field, 8, major, POINT(-123.007871479404 44.9105138452142) -SLW, Plan de Guadalupe, 8, mid, POINT(-100.932260548587 25.5479976419974) -SNN, Shannon, 8, major, POINT(-8.92242885557686 52.6934537102532) -SON, Santo Pekoa Int'l, 8, mid, POINT(167.220894919375 -15.5055387370858) -SRG, Achmad Yani, 8, mid, POINT(110.378556255666 -6.97873484956982) -SXR, Srinagar, 8, [military,mid], POINT(74.7826243672311 33.9830909431623) -TAP, Tapachula Int'l, 8, mid, POINT(-92.370003 14.7911281338773) -TGD, Podgorica, 8, major, POINT(19.2466868618873 42.3679335195428) -TLH, Tallahassee Reg., 8, major, POINT(-84.3449953984858 30.3955576176938) -TRN, Turin Int'l, 8, major, POINT(7.64416230362133 45.1916600734642) -TYN, Taiyuan Wusu Int'l, 8, major, POINT(112.625891539315 37.7545117791512) -UAK, Narsarsuaq, 8, mid, POINT(-45.4164008923108 61.1625968337328) -UTP, U-Tapao, 8, [military,mid], POINT(101.00020929048 12.6852930912664) -VFA, Victoria Falls, 8, mid, POINT(25.8467677208826 -18.0990155983682) -VGA, Vijaywada, 8, mid, POINT(80.7973080000675 16.528642778235) -VNS, Varanasi, 8, mid, POINT(82.8538741913527 25.4499077329822) -VRA, Juan Gualberto Gomez, 8, major, POINT(-81.4367103850623 23.0395422339631) -VSA, Villahermosa, 8, mid, POINT(-92.8190675836262 17.9930660113111) -YBR, Brandon, 8, mid, POINT(-99.9458959002463 49.9047279410277) -YED, CFB Edmonton, 8, [military,major], POINT(-113.478839054497 53.6749156618668) -YFB, Iqaluit, 8, mid, POINT(-68.5367292441812 63.7511523537807) -YHM, John C. Munro Hamilton Int'l, 8, mid, POINT(-79.9264230959967 43.1633605305096) -YMM, Fort McMurray, 8, mid, POINT(-111.223840046617 56.6563171390962) -YNT, Yantai, 8, [major,military], POINT(121.372047417773 37.4077044726924) -YPE, Peace River, 8, mid, POINT(-117.443663208082 56.231924036745) -YQM, Greater Moncton Int'l, 8, mid, POINT(-64.6886696807361 46.1162059639259) -YQY, Sydney/J.A. Douglas McCurdy, 8, mid, POINT(-60.0469372117026 46.1673405890504) -YRB, Resolute Bay, 8, mid, POINT(-94.9708023244006 74.7181860987594) -YSM, Fort Smith, 8, mid, POINT(-111.961059938158 60.0198749602443) -YTH, Thompson, 8, mid, POINT(-97.860733 55.797482) -YTS, Timmins, 8, mid, POINT(-81.372047 48.566158) -YUT, Repulse Bay, 8, mid, POINT(-86.25 66.533302) -YVP, Kuujjuaq, 8, mid, POINT(-68.433342 58.101959) -YWK, Wabush, 8, mid, POINT(-66.873009 52.926071) -YXD, Edmonton City Centre, 8, mid, POINT(-113.522973688581 53.5709436582812) -YXJ, Fort St. John (N. Peace), 8, mid, POINT(-120.736439 56.246035) -YYB, North Bay/Jack Garland, 8, mid, POINT(-79.42491 46.358711) -ZAR, Zaria, 8, mid, POINT(7.68726764310577 11.1352958601071) -SKP, Skopje, 8, mid, POINT(21.6281971858229 41.9564546081544) -VE23, Burnpur, 8, mid, POINT(86.974546776573 23.6312179107764) -VIDX, Hindon Air Force Station, 8, mid, POINT(77.3507888779117 28.7077968601071) -, Sunchon, 8, major, POINT(125.890825057486 39.4119659710565) -EPLL, Łódź Władysław Reymont, 8, mid, POINT(19.4032148744037 51.72720704517) -BXJ, Alma Ata N.W., 8, [mid,military], POINT(76.8782640096648 43.3554190837919) -JMU, Jiamusi Dongjiao, 8, mid, POINT(130.456204704407 46.8430150223379) -MDG, Mudanjiang Hailang, 8, major, POINT(129.58015153222 44.5342936299935) -ULMM, Severomorsk-3 (Murmansk N.E.), 8, [military,major], POINT(33.2903527616285 69.0168711826804) -OSB, Mosul Int'l, 8, mid, POINT(43.145802 36.308601) -, Rostov N., 8, [military,mid], POINT(39.6353996343665 47.2774209202867) -, Rostov S.W., 8, mid, POINT(39.7972215345149 47.1158577255835) -OUL, Oulu, 8, mid, POINT(25.3728374704307 64.9287992358849) -BOD, Bordeaux, 8, major, POINT(-0.701793449075243 44.8321108662674) -CEQ, Mandelieu, 8, mid, POINT(6.95431612028937 43.546097987045) -DOL, St Gatien, 8, mid, POINT(0.158653528230218 49.3616609986609) -LIL, Lille-Lesquin, 8, mid, POINT(3.10596499799813 50.5716423929581) -TLS, Toulouse-Blagnac, 8, major, POINT(1.37350918551153 43.6304625661601) -FUK, Fukuoka, 8, major, POINT(130.444189541884 33.5848164332573) -HIW, Hiroshima-Nishi, 8, mid, POINT(132.419372741681 34.3713815628829) -NKM, Nagoya, 8, mid, POINT(136.91962838414 35.2540532052867) -SDJ, Sendai, 8, mid, POINT(140.930247381369 38.1382075615287) -KKN, Kirkenes Hoybuktmoen, 8, mid, POINT(29.8913489500406 69.7238318113692) -CGB, Marechal Rondon Int'l, 8, mid, POINT(-56.1201774754724 -15.6511470191955) -FLN, Hercilio Luz Int'l, 8, major, POINT(-48.5448122049599 -27.6646276941638) -JOI, Joinville-Lauro C. de Loyola, 8, mid, POINT(-48.8016498165616 -26.2242941374785) -JPA, Presidente Castro Pinto Int'l, 8, mid, POINT(-34.9488925911125 -7.14617462402047) -NAT, Augusto Severo Int'l, 8, major, POINT(-35.2488410165389 -5.89912054477116) -OPO, Francisco Sa Carneiro, 8, major, POINT(-8.67127240719647 41.2368708920452) -SLZ, Marechal Cunha Machado Int'l, 8, mid, POINT(-44.2362344700492 -2.58350921043019) -SSZ, Santos Air Force Base, 8, [military,mid], POINT(-46.3052704931003 -23.9237590410637) -THE, Teresina-Senador Petronio Portella, 8, mid, POINT(-42.8212402317845 -5.06346299167191) -VCP, Viracopos-Campinas Int'l, 8, mid, POINT(-47.1410791911014 -23.0096239085339) -VIX, Eurico de Aguiar Salles, 8, mid, POINT(-40.2885368759913 -20.2574162759418) -ALC, Alicante, 8, major, POINT(-0.557230440363588 38.2866408993929) -LEI, Almeria, 8, mid, POINT(-2.3716014405912 36.8477672709643) -VLC, Valencia, 8, mid, POINT(-0.473474930771676 39.4914597884489) -KRN, Kiruna_Airport, 8, mid, POINT(20.3351522954898 67.8256066056432) -NRK, Norrköping Airport, 8, major, POINT(16.2339407695814 58.5833805017541) -BDO, Husein Sastranegara Int'l, 8, mid, POINT(107.575611852209 -6.90042408353409) -ROS, Rosario – Islas Malvinas Int'l, 8, mid, POINT(-60.7800787216586 -32.9162269743812) -MCZ, Maceio/Zumbi dos Palmares Int'l, 8, mid, POINT(-35.7924951215833 -9.51494118540116) -SSH, Sharm el-Sheikh Int'l, 8, mid, POINT(34.3901189267288 27.9804044199168) -TCP, Taba Int'l, 8, mid, POINT(34.7758378996779 29.5944990568019) -AGR, Agra, 8, [major,military], POINT(77.960909176509 27.15772773475) -BDQ, Vadodara, 8, mid, POINT(73.2262889533239 22.3361640021171) -KSH, Shahid Ashrafi Esfahani, 8, mid, POINT(47.1565835165639 34.3464167739108) -BEN, Benina Int'l, 8, mid, POINT(20.2680398018516 32.0872774606553) -DHA, King Abdulaziz AB, 8, [military,major], POINT(50.1477245727844 26.2703680854768) -STY, Nueva Hespérides Int'l, 8, mid, POINT(-57.9840821176492 -31.4373883387798) -BAIK, Baikonur Cosmodrome, 8, spaceport, POINT(63.307354423875 45.9635739403124) -KSC, Kennedy Space Center, 8, spaceport, POINT(-80.6369680911892 28.5163704772027) -CSG, Centre Spatial Guyanais, 8, spaceport, POINT(-52.7684296893452 5.23941001258035) -AUA, Queen Beatrix Int'l, 7, mid, POINT(-70.0076228563496 12.5034643630297) -JIB, Djibouti-Ambouli Int'l, 7, mid, POINT(43.1497127859956 11.5521018230172) -IQQ, Diego Aracena Int'l, 7, [mid,military], POINT(-70.178635395533 -20.5478400878309) -SAW, Sabiha Gökçen Havaalani, 7, major, POINT(29.3095991423889 40.9043003553957) -KSA, Kosrae Island, 7, mid, POINT(162.957041225076 5.3520098571828) -FUN, Funafuti Int'l, 7, mid, POINT(179.19544202302 -8.52485415059424) -NAG, Dr. Babasaheb Ambedkar Int'l, 7, mid, POINT(79.0537976421986 21.0899317630087) -HKT, Phuket Int'l, 7, mid, POINT(98.3060384900559 8.10768475952735) -NAN, Nadi Int'l, 7, mid, POINT(177.451151198059 -17.7529129479792) -AGU, Lic. Jesús Terán Peredo Int'l, 7, mid, POINT(-102.314093740058 21.7013390329207) -ALL, Albenga, 7, mid, POINT(8.12314535436409 44.0458773598158) -AMM, Queen Alia Int'l, 7, major, POINT(35.989707162193 31.7226621600432) -ARI, Chacalluta Int'l, 7, mid, POINT(-70.3357301410959 -18.3492061639579) -ATR, Atar Int'l, 7, mid, POINT(-13.0511704323315 20.4982706101565) -BAQ, Ernesto Cortissoz Int'l, 7, mid, POINT(-74.776555978265 10.8866775959414) -BRC, Teniente Luis Candelaria Int'l, 7, mid, POINT(-71.1614300869763 -41.1459976958105) -BYK, Bouaké, 7, mid, POINT(-5.06894222275311 7.73610495555032) -BZE, Philip S. W. Goldson Int'l, 7, major, POINT(-88.3082064033075 17.5360686575521) -CRP, Corpus Christi Int'l, 7, major, POINT(-97.5022678710298 27.7744560700823) -CUR, Hato Int'l, 7, mid, POINT(-68.9568788072761 12.1848346052019) -CUZ, Velazco Astete Int'l, 7, major, POINT(-71.9436641449722 -13.5382186992639) -DAR, Julius Nyerere Int'l, 7, mid, POINT(39.2074715039165 -6.86672004249119) -DET, Detroit City, 7, mid, POINT(-83.0039681417733 42.4090938431907) -DIL, Presidente Nicolau Lobato Int'l, 7, mid, POINT(125.524854209182 -8.54931157414564) -DME, Moscow Domodedovo Int'l, 7, major, POINT(37.9002531289452 55.4141528223023) -DUD, Dunedin Int'l, 7, mid, POINT(170.200027 -45.923431) -DZA, Dzaoudzi Pamanzi Int'l, 7, mid, POINT(45.2817864197899 -12.8049474381643) -ELP, El Paso Int'l, 7, mid, POINT(-106.395714679366 31.7990860272589) -EVN, Zvartnots Int'l, 7, major, POINT(44.4000630536938 40.1523679451884) -FTW, Fort Worth Meacham Field, 7, major, POINT(-97.3551348561587 32.8207529047972) -GDT, JAGS McCartney Int'l, 7, mid, POINT(-71.1461337448876 21.4421237439063) -GLS, Scholes Int'l, 7, mid, POINT(-94.8554013876264 29.2671239212096) -GOM, Goma Int'l, 7, mid, POINT(29.2400534952228 -1.6583179500207) -GOU, Garoua Int'l, 7, mid, POINT(13.3724309377878 9.33068867678854) -GUM, Antonio B. Won Pat Int'l, 7, major, POINT(144.805850357093 13.4926462359465) -GYY, Gary/Chicago Int'l, 7, mid, POINT(-87.4083596247406 41.6177930015166) -HAH, Prince Said Ibrahim Int'l, 7, mid, POINT(43.2745612179616 -11.5366393829127) -HBA, Hobart Int'l, 7, mid, POINT(147.505996190408 -42.8376083694822) -HIR, Honiara Int'l, 7, mid, POINT(160.045855129925 -9.42757566400146) -IEV, Kiev Zhuliany Int'l, 7, mid, POINT(30.4451305182104 50.412808165985) -IKT, Irkutsk S.E., 7, [mid,military], POINT(104.355859748002 52.2728893882244) -IND, Indianapolis Int'l, 7, major, POINT(-86.2734003650885 39.7302043703969) -INU, Nauru Int'l, 7, mid, POINT(166.91613965882 -0.545037226856384) -IPC, Mataveri Int'l, 7, mid, POINT(-109.43006441001 -27.1587738388538) -JUJ, Gob. Horacio Guzman Int'l, 7, mid, POINT(-65.0937665458812 -24.3861010775846) -KHN, Nanchang Changbei Int'l, 7, mid, POINT(115.911979918602 28.8624891200666) -KMG, Kunming Wujiaba Int'l, 7, major, POINT(102.742117578823 24.999996110081) -LBA, Leeds Bradford, 7, major, POINT(-1.65983106734746 53.8690819474434) -LBV, Libreville Leon M'ba Int'l, 7, mid, POINT(9.41022337820712 0.457139229503759) -LFW, Lomé Tokoin, 7, mid, POINT(1.25093205640014 6.16687362722297) -LWO, Lviv Danylo Halytskyi Int'l, 7, [mid,military], POINT(23.9461269598944 49.8178506050005) -MAJ, Marshall Islands Int'l, 7, mid, POINT(171.281919370648 7.06811848557091) -MFM, Macau Int'l, 7, major, POINT(113.57451294862 22.1576572529634) -MGQ, Aden Adde Int'l, 7, mid, POINT(45.3036374186202 2.01635311214988) -MPM, Maputo Int'l, 7, mid, POINT(32.5741915194782 -25.924276711787) -MRU, Sir Seewoosagur Ramgoolam Int'l, 7, mid, POINT(57.6769860076636 -20.4317567793216) -NAP, Naples Int'l, 7, major, POINT(14.2828444340203 40.8780728843639) -NDB, Nouadhibou Int'l, 7, mid, POINT(-17.0334398691538 20.9290523064387) -NGB, Ningbo Lishe Int'l, 7, major, POINT(121.461819388484 29.8208231906861) -NKC, Nouakchott Int'l, 7, mid, POINT(-15.9519259252201 18.0979231718174) -NOU, La Tontouta Int'l, 7, mid, POINT(166.217232118699 -22.0136386248981) -OAK, Oakland Int'l, 7, major, POINT(-122.213261257863 37.7123036951691) -ONT, Ontario Int'l, 7, major, POINT(-117.592327651651 34.060191102066) -ORK, Cork, 7, major, POINT(-8.49014199983817 51.8485405419923) -PDG, Minangkabau Int'l, 7, mid, POINT(100.285455851791 -0.786045714026273) -PDL, João Paulo II, 7, mid, POINT(-25.6969882198711 37.7433316472933) -PEW, Bacha Khan Int'l, 7, mid, POINT(71.5188149912667 33.9914027889596) -PIK, Glasgow Prestwick, 7, mid, POINT(-4.61097163901068 55.5088918105142) -PMG, Ponta Porã Int'l, 7, mid, POINT(-55.7060793748573 -22.551786560876) -PMR, Palmerston N. Int'l, 7, mid, POINT(175.62128328196 -40.3233178852055) -PNI, Pohnpei Int'l, 7, mid, POINT(158.203304490964 6.98130676512123) -PPT, Tahiti Faa'a Int'l, 7, mid, POINT(-149.609757932429 -17.5594577659942) -PSA, Pisa Galileo Galilei Int'l, 7, [major,military], POINT(10.4001343718056 43.6983224157664) -PZU, Port Sudan, 7, [mid,military], POINT(37.216065757542 19.5760636531968) -RAI, Praia Int'l, 7, mid, POINT(-23.4862019883587 14.9449889352832) -RAK, Marrakech-Menara, 7, mid, POINT(-8.02460535907989 31.6022946597764) -RAR, Rarotonga Int'l, 7, mid, POINT(-159.798156308387 -21.2009821724632) -REP, Siem Reap Int'l, 7, major, POINT(103.815780528112 13.4087969693538) -RGA, Hermes Quijada Int'l, 7, mid, POINT(-67.7530268462675 -53.7814746058316) -RGL, Piloto Civil Norberto Fernandez Int'l, 7, mid, POINT(-69.3064711776731 -51.6116980855402) -RNO, Reno-Tahoe Int'l, 7, major, POINT(-119.775283308105 39.5058499014703) -ROR, Roman Tmetuchl Int'l, 7, mid, POINT(134.532953466159 7.3644955361292) -SID, Amilcar Cabral Int'l, 7, mid, POINT(-22.9440574079648 16.7347932693385) -SJJ, Sarajevo, 7, major, POINT(18.3366185457127 43.8258872246797) -SKB, Robert L. Bradshaw Int'l, 7, mid, POINT(-62.7142125047316 17.311125840442) -SLA, Martín Miguel de Güemes Int, 7, mid, POINT(-65.4784760437796 -24.8443742713315) -SPN, Saipan Int'l, 7, mid, POINT(145.723694658638 15.1215167197664) -SRE, Juana Azurduy de Padilla Int'l, 7, mid, POINT(-65.2928631387847 -19.0139157924657) -SXM, Princess Juliana Int'l, 7, major, POINT(-63.1122760858602 18.042244021474) -TAI, Ta'izz Int'l, 7, mid, POINT(44.134782731062 13.6854970025574) -TAO, Qingdao Liuting Int'l, 7, mid, POINT(120.380685949061 36.2677578081039) -TKK, Chuuk Int'l, 7, mid, POINT(151.842046037403 7.45761780288443) -TNG, Tangier Ibn Battouta, 7, mid, POINT(-5.91288087655914 35.7257656409274) -TRW, Bonriki Int'l, 7, mid, POINT(173.145990795301 1.3806686975383) -TSE, Astana Int'l, 7, major, POINT(71.4609441399936 51.0269352907712) -TSN, Tianjin Binhai Int'l, 7, major, POINT(117.352723159919 39.1294609909008) -TSV, Townsville, 7, [major,military], POINT(146.77067890477 -19.2561814376212) -TUC, Teniente Gen. Benjamin Matienzo Int'l, 7, mid, POINT(-65.1081246236248 -26.8357310050714) -TUN, Aeroport Tunis, 7, major, POINT(10.2176992447111 36.8474482177219) -TUS, Tucson Int'l, 7, major, POINT(-110.937713232132 32.1203523441898) -ULN, Chinggis Khaan Int'l, 7, mid, POINT(106.762873994929 47.8525260966684) -URC, Ürümqi Diwopu Int'l, 7, major, POINT(87.4671298487808 43.8983382193653) -VLI, Bauerfield Int'l, 7, mid, POINT(168.319622739662 -17.7016990681781) -WWK, Wewak Int'l, 7, mid, POINT(143.669102299698 -3.58022689444744) -XCR, Châlons Vatry, 7, [military,mid], POINT(4.19111982574289 48.7803946138566) -XMN, Xiamen Gaoqi Int'l, 7, major, POINT(118.12696884672 24.537192570557) -YAP, Yap Int'l, 7, mid, POINT(138.086430283619 9.49791733361348) -ZLO, Playa de Oro Int'l, 7, mid, POINT(-104.560095200097 19.1480860285854) -CAY, Cayenne – Rochambeau, 7, mid, POINT(-52.3638068572357 4.82126714308924) -UIII, Irkutsk N.W., 7, mid, POINT(104.197359284494 52.3616476700131) -SJW, Shijiazhuang Zhengding Int'l, 7, major, POINT(114.692266598902 38.278140913112) -GYD, Heydar Aliyev Int'l, 7, major, POINT(50.0498394867405 40.462746883908) -LAK, Lakatamia Airbase, 7, [military,mid], POINT(33.322201334899 35.1063448067362) -CFB, Cabo Frio Int'l, 7, mid, POINT(-42.0792517520184 -22.9256317091328) -HEM, Helsinki-Malmi, 7, mid, POINT(25.0455353698315 60.2493778499587) -LUX, Luxembourg-Findel, 7, major, POINT(6.21642121728731 49.6343040925102) -VCE, Venice Marco Polo, 7, major, POINT(12.3410673004369 45.5048477588455) -YNY, Yangyang Int'l, 7, mid, POINT(128.66298866884 38.0587824162585) -TBT, Tabatinga Int'l, 7, mid, POINT(-69.939473933909 -4.25032469493379) -BVB, Boa Vista Int'l, 7, mid, POINT(-60.6922206338682 2.84119534121157) -LPA, Gran Canaria, 7, major, POINT(-15.3899245158461 27.9368899716574) -ING, Com. Armando Tola Int'l, 7, mid, POINT(-72.0538569101296 -50.2839008690038) -NYO, Stockholm-Skavsta, 7, mid, POINT(16.9216055584254 58.7851041303448) -MES, Polonia Int'l, 7, mid, POINT(98.6761925714641 3.56659179990894) -BGF, Bangui M'Poko Int'l, 7, mid, POINT(18.524123630208 4.39885153695957) -HGH, Hangzhou Xiaoshan Int'l, 7, major, POINT(120.432097376313 30.2351862790414) -CXI, Cassidy Int'l, 7, mid, POINT(-157.34977789343 1.98616119792402) -SQQ, Šiauliai Int'l, 7, mid, POINT(23.3831885738691 55.90376945404) -IUE, Niue Int'l, 7, mid, POINT(-169.926129774217 -19.0767129354511) -AGT, Guaraní Int'l, 7, mid, POINT(-54.8393995296062 -25.4568570715812) -AQP, Rodríguez Ballón Int'l, 7, mid, POINT(-71.5679335385285 -16.344552065352) -VVO, Vladivostok Int'l, 7, [mid,military], POINT(132.139841720715 43.3776492533885) -PRN, Pristina, 7, major, POINT(21.0302690124746 42.5850331153448) -ANR, Deurne, 6, mid, POINT(4.45092277399909 51.1891285063806) -LAP, Gen. Márquez de León Int'l, 6, mid, POINT(-110.367197859809 24.0760903521803) -HRB, Harbin Taiping, 6, major, POINT(126.236983030863 45.6206011723245) -TRV, Trivandrum Int'l, 6, mid, POINT(76.9189025612913 8.47650993894514) -ADB, Adnan Menderes, 6, major, POINT(27.1492975952664 38.2912347645175) -NKG, Nanjing Lukou Int'l, 6, major, POINT(118.866102146906 31.7353249296177) -FPO, Freeport Int'l, 6, mid, POINT(-78.7039343114497 26.548246747189) -TIP, Tripoli Int'l, 6, major, POINT(13.1442589810713 32.6691695504993) -YQX, Gander Int'l, 6, mid, POINT(-54.5755719093578 48.9465980060736) -DOH, Doha Int'l, 6, [major,military], POINT(51.5585487876547 25.2682461310506) -ABQ, Albuquerque Int'l, 6, major, POINT(-106.6166851616 35.0491578018276) -ANU, V.C. Bird Int'l, 6, mid, POINT(-61.7923676698358 17.1403599371617) -APW, Faleolo, 6, mid, POINT(-171.99732221834 -13.8325013323956) -ATZ, Asyut, 6, mid, POINT(31.0162490438011 27.0508158406978) -BAH, Bahrain Int'l, 6, major, POINT(50.6260028757534 26.2696971499497) -BDL, Bradley Int'l, 6, major, POINT(-72.685394743339 41.9303160058352) -BGI, Grantley Adams Int'l, 6, mid, POINT(-59.4874188953158 13.079661104553) -BJL, Yundum Int'l, 6, mid, POINT(-16.6523132698075 13.3438604788942) -BJM, Bujumbura Int'l, 6, mid, POINT(29.3209840169939 -3.32204434913113) -BLZ, Chileka Int'l, 6, mid, POINT(34.9719441837933 -15.6813844793272) -BME, Broome Int'l, 6, mid, POINT(122.233850515022 -17.952576129268) -BND, Bandar Abbass Int'l, 6, mid, POINT(56.368886456411 27.2103258455145) -BSR, Basrah Int'l, 6, major, POINT(47.6683766633518 30.552799016106) -CJS, Ciudad Juarez Int'l, 6, mid, POINT(-106.435846631055 31.6357566201951) -CMB, Katunayake Int'l, 6, major, POINT(79.8852573421506 7.17807710544221) -CNS, Cairns Int'l, 6, mid, POINT(145.7535848444 -16.8767421554062) -CNX, Chiang Mai Int'l, 6, major, POINT(98.9681181241593 18.7688473919675) -COS, City of Colorado Springs, 6, major, POINT(-104.700880274111 38.7974248779125) -CPE, Ign. Alberto Ongay Int'l, 6, mid, POINT(-90.5036283734038 19.8142247992074) -CSX, Changsha Huanghua Int'l, 6, major, POINT(113.214054203252 28.1899218619451) -CVG, Greater Cincinnati Int'l, 6, major, POINT(-84.6561699153392 39.055418904783) -DAD, Da Nang, 6, major, POINT(108.202706257936 16.053144145167) -DAL, Dallas Love Field, 6, major, POINT(-96.84986377098 32.8444253732738) -DAM, Damascus Int'l, 6, major, POINT(36.5128954718126 33.4114366702732) -DAV, Enrique Malek Int'l, 6, mid, POINT(-82.4317583369387 8.39126106116917) -DIR, Aba Tenna D. Yilma Int'l, 6, mid, POINT(41.857756722253 9.61267784753569) -DPS, Bali Int'l, 6, major, POINT(115.162322961107 -8.74475731595652) -DSM, Des Moines Int'l, 6, major, POINT(-93.6484612563736 41.5327904242113) -EBB, Entebbe Int'l, 6, mid, POINT(32.4427573135214 0.044940949388672) -FKI, Kisangani Bangoka Int'l, 6, mid, POINT(25.3302714896212 0.492225136917501) -FOC, Fuzhou Changle Int'l, 6, mid, POINT(119.668043820999 25.9318233148143) -GAU, Lokpriya G. Bordoloi Int'l, 6, mid, POINT(91.588229058187 26.1052475924255) -GDN, Gdansk Lech Walesa, 6, major, POINT(18.4684422165911 54.3807025352925) -GND, Point Salines Int'l, 6, mid, POINT(-61.7858529909285 12.0072683054283) -GOJ, Nizhny Novgorod Int'l, 6, mid, POINT(43.7896337062935 56.2185525910656) -GYM, Gen. José M. Yáñez Int'l, 6, mid, POINT(-110.921651270402 27.9694553962829) -HET, Hohhot Baita Int'l, 6, mid, POINT(111.814681821626 40.8540600906552) -HLN, Helena Reg., 6, mid, POINT(-111.989896896008 46.6102043529) -HMO, Gen. Ignacio P. Garcia Int'l, 6, mid, POINT(-111.051901711819 29.0900772523445) -IAD, Dulles Int'l, 6, major, POINT(-77.4477925769206 38.952774037953) -ITO, Hilo Int'l, 6, mid, POINT(-155.039629733435 19.7147976868663) -JAN, Jackson Int'l, 6, major, POINT(-90.0750986276924 32.3100600273635) -JAX, Jacksonville Int'l, 6, major, POINT(-81.6835767278311 30.491352730948) -KCH, Kuching Int'l, 6, mid, POINT(110.341837054315 1.4872079377901) -KGL, Kigali Int'l, 6, mid, POINT(30.1348768187856 -1.96365443664138) -KRK, Kraków-Balice, 6, major, POINT(19.8009772844504 50.0722630648331) -KUF, Kurumoch, 6, major, POINT(50.1472655210191 53.5083848190935) -KWL, Guilin Liangjiang Int'l, 6, major, POINT(110.04689349777 25.2176055252293) -LAO, Laoag Int'l, 6, mid, POINT(120.533876196127 18.1824180866379) -LGA, LaGuardia, 6, major, POINT(-73.8719858204814 40.7745539398858) -LGW, London Gatwick, 6, major, POINT(-0.162961639139456 51.1557567519275) -LJU, Ljubljana, 6, major, POINT(14.4548126283266 46.2305445554486) -LKO, Amausi Int'l, 6, mid, POINT(80.8841719732472 26.7639328700916) -LPG, La Plata, 6, mid, POINT(-57.895382063651 -34.9655441559234) -MAM, Gen. Sevando Canales, 6, mid, POINT(-97.5308217121187 25.7708412640619) -MAN, Manchester Int'l, 6, major, POINT(-2.27337159069427 53.3624896066518) -MCI, Kansas City Int'l, 6, major, POINT(-94.7159148579154 39.2978958263659) -MCT, Seeb Int'l, 6, major, POINT(58.2904804753493 23.5885704175856) -MIR, Habib Bourguiba Int'l, 6, mid, POINT(10.753368185054 35.760710442178) -MRS, Marseille Provence Airport, 6, major, POINT(5.22137917720337 43.4410600016468) -NLD, Quetzalcoatl Int'l, 6, mid, POINT(-99.5680081930063 27.4496896508316) -NNG, Nanning Wuwu Int'l, 6, major, POINT(108.168012273331 22.6120370541785) -OAX, Xoxocotlán Int'l, 6, mid, POINT(-96.7217959384975 17.0005592569745) -OGG, Kahului, 6, mid, POINT(-156.437429581353 20.8932885151112) -OKC, Will Rogers, 6, major, POINT(-97.5961177542092 35.3952774911744) -ORF, Norfolk Int'l, 6, major, POINT(-76.2044231712327 36.8982394673674) -PBI, Palm Beach Int'l, 6, major, POINT(-80.0901893383387 26.688441666433) -PBM, Pengel Int'l, 6, mid, POINT(-55.1999113892902 5.45599967797439) -PEE, Bolshesavino, 6, mid, POINT(56.0195602820297 57.9197711231691) -PEN, Penang Int'l, 6, mid, POINT(100.265786380955 5.29265627790489) -PHC, Port Harcourt Int'l, 6, mid, POINT(6.94989742723191 5.00700347673943) -PHE, Port Hedland Int'l, 6, mid, POINT(118.631797815615 -20.3781272960723) -PIR, Pierre Regional, 6, mid, POINT(-100.292641981705 44.3801534668762) -PIT, Greater Pittsburgh Int'l, 6, major, POINT(-80.2561290571918 40.4960518915285) -PPG, Pago Pago Int'l, 6, mid, POINT(-170.713307053734 -14.3290641850306) -BHX, Birmingham Int'l, 6, major, POINT(-1.73373170434452 52.4529085542838) -ROB, Roberts Int'l, 6, mid, POINT(-10.3530851867934 6.24183456554525) -RPR, Raipur, 6, mid, POINT(81.7403775915201 21.1859868561447) -SAL, El Salvador Int'l, 6, mid, POINT(-89.0572035692743 13.4447481228616) -SAN, San Diego Int'l, 6, major, POINT(-117.197511025731 32.7322645570132) -SAT, San Antonio Int'l, 6, major, POINT(-98.4719699991559 29.5266203391315) -SAV, Savannah Int'l, 6, major, POINT(-81.2099647750913 32.1356415522902) -SCU, Antonio Maceo, 6, mid, POINT(-75.8398877639791 19.9724288717622) -SLP, Ponciano Arriaga Int'l, 6, mid, POINT(-100.936477816267 22.2557130495903) -SMF, Sacramento Int'l, 6, major, POINT(-121.587894877723 38.6927238925554) -STI, Cibao Int'l, 6, mid, POINT(-70.6941783224468 19.4659219152888) -SVX, Koltsovo, 6, major, POINT(60.8058033432174 56.732245612046) -SYR, Syracuse Hancock Int'l, 6, major, POINT(-76.1130789991049 43.1317844943741) -TBZ, Tabriz, 6, mid, POINT(46.244713373574 38.1311107688175) -TRC, Torreon Int'l, 6, mid, POINT(-103.398787828579 25.5632164399896) -TUL, Tulsa Int'l, 6, major, POINT(-95.889882271542 36.190127565195) -TYS, Mcghee Tyson, 6, major, POINT(-83.9899378327585 35.8057448027088) -UFA, Ufa Int'l, 6, major, POINT(55.8840773411837 54.5651323578972) -UVF, Hewanorra Int'l, 6, mid, POINT(-60.9499737723461 13.7365238050489) -WDH, Windhoek Hosea Kutako Int'l, 6, mid, POINT(17.4632259028133 -22.4869531202041) -YAM, Sault Ste Marie, 6, mid, POINT(-84.5006089999717 46.4854175101926) -YDQ, Dawson Cr., 6, mid, POINT(-120.185595619101 55.7394117074557) -YEG, Edmonton Int'l, 6, major, POINT(-113.584492564406 53.3072001619183) -YHZ, Halifax Int'l, 6, major, POINT(-63.5149652501673 44.886545450101) -YKA, Kamloops, 6, mid, POINT(-120.441734763962 50.7051955184591) -YSB, Sudbury, 6, mid, POINT(-80.7957747817105 46.6227508204893) -YSJ, Saint John, 6, mid, POINT(-65.8905573681168 45.3292305955017) -YXS, Prince George, 6, mid, POINT(-122.674014743986 53.8842485751138) -YYJ, Victoria Int'l, 6, major, POINT(-123.430624539528 48.640529482179) -ZAM, Zamboanga Int'l, 6, mid, POINT(122.062432321637 6.9197577480583) -ZGC, Lanzhou Zhongchuan, 6, mid, POINT(103.615415363043 36.5078842461237) -ALB, Albany Int'l, 6, mid, POINT(-73.8093518843173 42.7456619801729) -MKE, General Mitchell Int'l, 6, major, POINT(-87.9021056250744 42.9479198729586) -ZHHH, Wang-Chia Tun Airbase, 6, [military,mid], POINT(114.24694737615 30.6017141196702) -SYX, Sanya Phoenix Int'l, 6, major, POINT(109.40823949108 18.3090959908593) -LXA, Lhasa Gonggar, 6, mid, POINT(90.9005610194027 29.2936936123184) -HTN, Hotan, 6, mid, POINT(79.8723005212191 37.0400363509765) -DRS, Dresden, 6, major, POINT(13.7649671440047 51.1250912428871) -NNA, Kenitra Air Base, 6, [military,major], POINT(-6.597753628116 34.2986673638223) -QNJ, Annemasse, 6, mid, POINT(6.26491085364159 46.1957283286261) -NOG, Nogales Int'l, 6, mid, POINT(-110.972721301675 31.2255371741159) -SXB, Strasbourg, 6, mid, POINT(7.62784196688924 48.5446961721759) -CGN, Cologne/Bonn, 6, major, POINT(7.12235975524539 50.8782596629471) -PUS, Kimhae Int'l, 6, major, POINT(128.948801379039 35.1702840636829) -CJU, Jeju Int'l, 6, major, POINT(126.491629401972 33.5247173150399) -SVG, Stavanger Sola, 6, major, POINT(5.6298103297218 58.8821564842185) -TRD, Trondheim Vaernes, 6, major, POINT(10.9168095241445 63.472029381717) -CMG, Corumbá Int'l, 6, mid, POINT(-57.6636078925543 -19.0141662885534) -FNC, Madeira, 6, mid, POINT(-16.7756374531213 32.6933642847489) -IGU, Foz do Iguaçu Int'l, 6, mid, POINT(-54.4885922735633 -25.5976832162102) -PVH, Gov. Jorge Teixeira de Oliveira Int'l, 6, mid, POINT(-63.8984625004213 -8.71442482859288) -BIO, Bilbao, 6, mid, POINT(-2.90609011679805 43.3050829811195) -PMI, Palma de Mallorca, 6, major, POINT(2.72997660200647 39.5657758586254) -TFN, Tenerife N., 6, major, POINT(-16.3463175679264 28.4875770267731) -GOT, Gothenburg, 6, major, POINT(12.2938269092573 57.6857493534879) -LLA, Lulea, 6, major, POINT(22.1230271243945 65.5490362477616) -AUH, Abu Dhabi Int'l, 6, major, POINT(54.6463293225558 24.4272271529764) -CZL, Mohamed Boudiaf Int'l, 6, mid, POINT(6.62194665181219 36.2834409441601) -ASW, Aswan Int'l, 6, mid, POINT(32.8244372462973 23.9682765441778) -RVN, Rovaniemi, 6, mid, POINT(25.8294409760452 66.5595564168509) -GEO, Cheddi Jagan Int'l, 6, mid, POINT(-58.2541191925889 6.49855290813572) -COK, Cochin Int'l, 6, major, POINT(76.3905198502024 10.1551187628118) -EDL, Eldoret Int'l, 6, mid, POINT(35.2236930658301 0.40507147546036) -ICN, Incheon Int'l, 6, major, POINT(126.450875980796 37.4492088624346) -CUL, Federal de Bachigualato Int'l, 6, mid, POINT(-107.469863792896 24.7668040390461) -ISB, Benazir Bhutto Int'l, 6, [major,military], POINT(73.1007936471882 33.6074457507526) -BRU, Brussels, 5, major, POINT(4.48464032408272 50.8972949641511) -ABV, Abuja Int'l, 5, major, POINT(7.27025993974356 9.00437659781094) -ACV, Arcata-Eureka, 5, mid, POINT(-124.107065520139 40.9719245381314) -AUS, Austin-Bergstrom Int'l, 5, major, POINT(-97.6668367646054 30.2021081920749) -AYT, Antalya, 5, major, POINT(30.8025526439415 36.9153233051868) -BFS, Belfast Int'l, 5, major, POINT(-6.21616943734958 54.6615575470103) -BGY, Orio Al Serio, 5, major, POINT(9.6989176939974 45.6654980560695) -BKI, Kota Kinabalu Int'l, 5, mid, POINT(116.051087873369 5.92289445474807) -BLR, Bengaluru Int'l, 5, major, POINT(77.7095579889575 13.2006108069609) -CBR, Canberra Int'l, 5, major, POINT(149.190760539671 -35.3071855902909) -CMH, Port Columbus Int'l, 5, major, POINT(-82.8840306426634 39.9981181922432) -CMN, Mohamed V Int'l, 5, major, POINT(-7.5814559902572 33.3747274815396) -DUS, Düsseldorf Int'l, 5, major, POINT(6.76494446612174 51.2781820420774) -ESB, Esenboğa Int'l, 5, major, POINT(32.9930100772014 40.1151278273234) -HLZ, Hamilton Int'l, 5, mid, POINT(175.336221432708 -37.8658411484827) -HYD, Rajiv Gandhi Int'l, 5, major, POINT(78.42953613452 17.2359831507471) -JFK, John F Kennedy Int'l, 5, major, POINT(-73.7863268609295 40.6459595584081) -KBP, Boryspil Int'l, 5, major, POINT(30.8951621615528 50.340902338877) -KRT, Khartoum, 5, major, POINT(32.550153296633 15.5922226530858) -MSN, Dane Cty. Reg. (Truax Field), 5, major, POINT(-89.3457847894487 43.1363082385868) -MSQ, Minsk Int'l, 5, major, POINT(28.0341933346378 53.8893792398005) -PMO, Palermo, 5, major, POINT(13.1055309888638 38.1863351084895) -PVD, T.F. Green, 5, mid, POINT(-71.4357841445789 41.7260019847189) -RSW, Southwest Florida Int'l, 5, major, POINT(-81.7551231409306 26.5279288067651) -SHE, Shenyang Taoxian Int'l, 5, major, POINT(123.487974430338 41.6347891339582) -SHJ, Sharjah Int'l, 5, major, POINT(55.5205071948853 25.3211964019068) -SJC, San Jose Int'l, 5, major, POINT(-121.929428983532 37.3694905908965) -SNA, John Wayne, 5, major, POINT(-117.861489220393 33.6794857329549) -STR, Stuttgart, 5, major, POINT(9.19395108945536 48.6901051358913) -SYQ, Nacional Tobías Bolaños, 5, mid, POINT(-84.1386091971594 9.95827851919623) -SZX, Shenzhen Bao'an Int'l, 5, major, POINT(113.815852751085 22.6465077147868) -SDF, Louisville Int'l, 5, major, POINT(-85.7417027597367 38.1860207152699) -GVA, Geneva, 5, major, POINT(6.10794577423603 46.231009510158) -LYS, Lyon-Saint Exupery, 5, mid, POINT(5.07594431813459 45.7210186834669) -KIX, Kansai Int'l, 5, major, POINT(135.244459772476 34.4347941629269) -LIS, Lisbon Portela, 5, major, POINT(-9.13069440931071 38.7707623427514) -CNF, Tancredo Neves Int'l, 5, major, POINT(-43.9635815209949 -19.6327821218747) -BMA, Bromma, 5, mid, POINT(17.9456175406145 59.3555902065112) -SUB, Juanda Int'l, 5, major, POINT(112.777034594933 -7.383578985276) -MDQ, Astor Piazzolla Int'l, 5, mid, POINT(-57.5816150932392 -37.9332161204482) -GCM, Owen Roberts Int'l, 5, major, POINT(-81.3576706162289 19.2959107437122) -CGO, Zhengzhou Xinzheng Int'l, 5, major, POINT(113.841831302845 34.5263027198957) -DLC, Dalian Zhoushuizi Int'l, 5, major, POINT(121.538913780101 38.9615702300222) -HER, Heraklion Int'l, 5, major, POINT(25.1740558243272 35.3369024101045) -TBS, Tbilisi Int'l, 5, major, POINT(44.9646146141664 41.6694420187261) -XXC, Cascais, 5, mid, POINT(-9.35458240263928 38.7235353208323) -KHH, Kaohsiung Int'l, 4, major, POINT(120.345156342151 22.5717061054422) -SKO, Sadiq Abubakar III, 4, mid, POINT(5.20022616032651 12.9174824166181) -UIO, Mariscal Sucre Int'l, 4, mid, POINT(-78.4899925545701 -0.145552408466882) -KHI, Karachi Civil, 4, mid, POINT(67.1521283592947 24.8985243689595) -KIV, Kishinev S.E., 4, mid, POINT(28.9360487562255 46.9341619900391) -LIM, Jorge Chávez, 4, major, POINT(-77.1075656931342 -12.0237161502221) -YQT, Thunder Bay Int'l, 4, mid, POINT(-89.3121421238136 48.3718811492508) -VNO, Vilnius, 4, major, POINT(25.2807164497285 54.6430549307542) -XIY, Hsien Yang, 4, major, POINT(108.755811342151 34.4429391054422) -NTR, Del Norte Int'l, 4, mid, POINT(-100.238394186577 25.859873767729) -TBU, Fua'amotu Int'l, 4, mid, POINT(-175.135635 -21.24861) -IFN, Esfahan Int'l, 4, mid, POINT(51.8763916812681 32.7460805344321) -HRE, Harare Int'l, 4, mid, POINT(31.1014 -17.9228) -KWI, Kuwait Int'l, 4, major, POINT(47.9714825593316 29.2396800581583) -YOW, Macdonald-Cartier Int'l, 4, major, POINT(-75.6648933870205 45.3201348196531) -KBL, Kabul Int'l, 4, mid, POINT(69.2100736270874 34.5633978864149) -ABJ, Abidjan Port Bouet, 4, mid, POINT(-3.93221929167636 5.2543984451492) -ACA, General Juan N Alvarez Int'l, 4, major, POINT(-99.7545085619681 16.76196735278) -ACC, Kotoka Int'l, 4, major, POINT(-0.171402855660817 5.60698152381193) -ADD, Bole Int'l, 4, mid, POINT(38.7931904366343 8.98173027581099) -ADE, Aden Int'l, 4, mid, POINT(45.030602 12.8278) -ADL, Adelaide Int'l, 4, mid, POINT(138.532101457699 -34.9405860275154) -ALA, Almaty Int'l, 4, major, POINT(77.0120458771175 43.3464943144793) -ALG, Houari Boumediene, 4, major, POINT(3.21207353516506 36.6997206663535) -ALP, Aleppo Int'l, 4, major, POINT(37.2273414057828 36.1846237314314) -AMD, Sardar Vallabhbhai Patel Int'l, 4, mid, POINT(72.6209000884332 23.0707454635881) -ANF, Cerro Moreno Int'l, 4, mid, POINT(-70.4409908509407 -23.4489545248317) -ASB, Ashkhabad Northwest, 4, mid, POINT(58.3639659208246 37.984853438957) -ASM, Yohannes Iv Int'l, 4, mid, POINT(38.9063540136321 15.2936159696499) -ASU, Silvio Pettirossi Int'l, 4, mid, POINT(-57.5139078247136 -25.2416592533816) -BDA, Bermuda Int'l, 4, mid, POINT(-64.7027740686514 32.3591739601581) -BEG, Surcin, 4, major, POINT(20.2912845946621 44.8190766654433) -BEY, Beirut Int'l, 4, major, POINT(35.4930853618161 33.8254400618668) -BHO, Bairagarh, 4, mid, POINT(77.3408714713579 23.2855684869809) -BKO, Bamako Sénou, 4, mid, POINT(-7.94727226970801 12.5393363425867) -BNA, Nashville Int'l, 4, major, POINT(-86.6692867356375 36.1314876361697) -BNE, Brisbane Int'l, 4, major, POINT(153.120256418844 -27.3853965939099) -BOI, Boise Air Terminal, 4, major, POINT(-116.221841070549 43.5689592234704) -BRW, Wiley Post Will Rogers Mem., 4, mid, POINT(-156.771835 71.289299) -BUF, Greater Buffalo Int'l, 4, major, POINT(-78.7319965523308 42.9340337493526) -BUQ, Bulawayo, 4, mid, POINT(28.622552042904 -20.0155684094908) -BWN, Brunei Int'l, 4, major, POINT(114.933119029209 4.94547528227685) -CAN, Guangzhou Baiyun Int'l, 4, major, POINT(113.297516552171 23.3891511573243) -CCP, Carriel Sur Int'l, 4, mid, POINT(-73.0621061746214 -36.7763727437881) -CCU, Netaji Subhash Chandra Bose Int'l, 4, major, POINT(88.4400010130197 22.6453893785064) -CGP, Chittagong, 4, mid, POINT(91.8147107162383 22.2455658585738) -CHC, Christchurch Int'l, 4, major, POINT(172.538675565223 -43.4885486784104) -CKY, Conakry, 4, mid, POINT(-13.6210656251671 9.57418115850082) -CLE, Hopkins Int'l, 4, major, POINT(-81.8384406064046 41.4111916124966) -CLO, Alfonso Bonilla Aragón Int'l, 4, mid, POINT(-76.3850714728091 3.54328635123219) -COO, Cotonou Cadjehon, 4, mid, POINT(2.3838000724352 6.3582465034691) -COR, Ingeniero Ambrosio L.V. Taravella Int'l, 4, mid, POINT(-64.2123157670801 -31.3156811684889) -CTG, Rafael Nunez, 4, mid, POINT(-75.5123349559682 10.4449381764915) -CUN, Cancún, 4, major, POINT(-86.8744172506694 21.04019667144) -CUU, General R F Villalobos Int'l, 4, mid, POINT(-105.969204692629 28.7039984997679) -DAC, Zia Int'l Dhaka, 4, mid, POINT(90.4049241599237 23.8481243218127) -DRW, Darwin Int'l, 4, [major,military], POINT(130.877501436774 -12.4080559966556) -DUR, Louis Botha, 4, mid, POINT(30.9457081940881 -29.965914250828) -FBM, Lubumbashi Luano Int'l, 4, mid, POINT(27.5292 -11.5908) -FEZ, Saiss, 4, mid, POINT(-4.98214637678303 33.9305251844673) -FIH, Kinshasa N Djili Int'l, 4, mid, POINT(15.4465162074561 -4.38916882197582) -FNA, Freetown Lungi, 4, mid, POINT(-13.2002296786483 8.61542361726369) -FNJ, Sunan, 4, mid, POINT(125.675321571201 39.2001771667656) -FRU, Vasilyevka, 4, major, POINT(74.468800339909 43.0554527233303) -GBE, Sir Seretse Khama Int'l, 4, mid, POINT(25.9243808264147 -24.5580718089441) -GDL, Don Miguel Hidalgo Int'l, 4, major, POINT(-103.300766222752 20.5246863485173) -GLA, Glasgow Int'l, 4, major, POINT(-4.43167796995107 55.8641828570355) -GUA, La Aurora, 4, mid, POINT(-90.530181111378 14.5881608290051) -GYE, Simon Bolivar Int'l, 4, mid, POINT(-79.887009643933 -2.15833790699136) -HAN, Noi Bai, 4, major, POINT(105.803759436806 21.2145596707245) -HAV, José Martí Int'l, 4, major, POINT(-82.4074206289499 22.9973533364428) -HBE, Borg El Arab Int'l, 4, mid, POINT(29.69266601523 30.9183712786239) -JED, King Abdul Aziz Int'l, 4, major, POINT(39.1504996780448 21.6706857878128) -KAN, Kano Mallam Aminu Int'l, 4, mid, POINT(8.52213718395767 12.0457071601746) -KHG, Kashi, 4, mid, POINT(76.0130148060075 39.5379686306258) -KIN, Norman Manley Int'l, 4, major, POINT(-76.7786897616576 17.9375751552752) -KTM, Tribhuvan Int'l, 4, mid, POINT(85.357139531668 27.7002816751609) -LAD, Luanda 4 de Fevereiro, 4, mid, POINT(13.2347957502699 -8.84831327917379) -LED, Pulkovo 2, 4, major, POINT(30.3070976454648 59.8054061601897) -LHE, Allama Iqbal Int'l, 4, mid, POINT(74.4108810181748 31.5206296518206) -LLW, Kamuzu Int'l, 4, mid, POINT(33.7827885019788 -13.788622823746) -LOS, Lagos Murtala Muhammed, 4, major, POINT(3.32112435281334 6.57825944540467) -LPB, El Alto Int'l, 4, mid, POINT(-68.1780055277945 -16.5098792213977) -LUN, Lusaka Int'l, 4, mid, POINT(28.4455443211019 -15.3268522509447) -LXR, Luxor, 4, mid, POINT(32.7032970848623 25.6730347786023) -MAA, Chennai Int'l, 4, major, POINT(80.1637759731545 12.9825301669154) -MAR, La Chinita Int'l, 4, mid, POINT(-71.7237688094687 10.5557594684972) -MDE, José María Córdova, 4, mid, POINT(-75.4269557399772 6.171001614358) -MEM, Memphis Int'l, 4, major, POINT(-89.9816280353237 35.0444101240089) -MGA, Augusto Cesar Sandino Int'l, 4, mid, POINT(-86.1712846229543 12.144635873435) -MHD, Mashhad, 4, major, POINT(59.6421943574029 36.2275503134984) -MIA, Miami Int'l, 4, major, POINT(-80.2789718277441 25.7949407212406) -MID, Lic M Crecencio Rejon Int'l, 4, mid, POINT(-89.6630235736434 20.9338603864296) -MLA, Luqa, 4, major, POINT(14.4952644555055 35.8489307943501) -MBA, Moi Int'l, 4, major, POINT(39.6026631870383 -4.03265262579657) -MSU, Moshoeshoe I Int'l, 4, mid, POINT(27.5592160333614 -29.4555740046101) -MSY, New Orleans Int'l, 4, major, POINT(-90.2566939480594 29.9851141460622) -MUX, Multan, 4, [major,military], POINT(71.418995432932 30.1950780904965) -MVD, Carrasco Int'l, 4, major, POINT(-56.026636146282 -34.8410485988569) -MZT, General Rafael Buelna Int'l, 4, mid, POINT(-106.270016617885 23.1665960971344) -NAS, Nassau Int'l, 4, major, POINT(-77.4648472290944 25.0486910600866) -NDJ, Ndjamena, 4, mid, POINT(15.0330446385559 12.1295400184115) -NIM, Niamey, 4, mid, POINT(2.17730671184125 13.4767572807942) -CEB, Mactan-Cebu Int'l, 4, major, POINT(123.979134508664 10.3158756727292) -NOV, Nova Lisboa, 4, mid, POINT(15.7497618459595 -12.8025414575915) -OMA, Eppley Airfield, 4, mid, POINT(-95.8994157953121 41.2997111453012) -OME, Nome, 4, mid, POINT(-165.441641712281 64.5072207026631) -OUA, Ouagadougou, 4, mid, POINT(-1.51380536165114 12.3535800260473) -PAP, Mais Gate Int'l, 4, mid, POINT(-72.2944780260473 18.5756829054286) -PBC, Puebla, 4, mid, POINT(-98.375759790423 19.163793546584) -PDX, Portland Int'l, 4, major, POINT(-122.592738881254 45.5889569315305) -PER, Perth Int'l, 4, major, POINT(115.974224942233 -31.9411297945783) -PLZ, H F Verwoerd, 4, mid, POINT(25.6117777567602 -33.9840877431374) -PMC, El Tepual Int'l, 4, mid, POINT(-73.0983841336424 -41.4333820702269) -PNH, Pochentong, 4, major, POINT(104.845027612457 11.5526449176513) -PNQ, Pune, 4, [major,military], POINT(73.9089838110016 18.5791766115328) -POM, Port Moresby Int'l, 4, major, POINT(147.211250855977 -9.43865269316142) -PTY, Tocumen Int'l, 4, major, POINT(-79.3871348215438 9.06687242265839) -PUQ, Carlos Ibáñez de Campo Int'l, 4, mid, POINT(-70.8431237851324 -53.0050698255177) -RDU, Durham Int'l, 4, major, POINT(-78.7913814006751 35.8752323452255) -RGN, Mingaladon, 4, major, POINT(96.1341946114947 16.9011542818251) -RIX, Riga, 4, major, POINT(23.9793791116995 56.9220038786097) -SAH, Sanaa Int'l, 4, mid, POINT(44.2246467902561 15.4739027755737) -SDA, Baghdad Int'l, 4, major, POINT(44.2289125352942 33.268162986377) -SDQ, De Las Américas Int'l, 4, major, POINT(-69.6764726754667 18.4302196948173) -SGN, Tan Son Nhat, 4, major, POINT(106.664246141375 10.8163005571879) -SKG, Thessaloniki, 4, major, POINT(22.9764353610613 40.5238736887775) -SOF, Vrazhdebna, 4, major, POINT(23.4024521357708 42.6891841273195) -STV, Surat, 4, major, POINT(72.7424384372589 21.1204503297172) -SUV, Nausori Int'l, 4, mid, POINT(178.560048369959 -18.0458996922854) -SYZ, Shiraz Int'l, 4, major, POINT(52.5897712745211 29.5458013842874) -TAM, Gen Francisco J Mina Int'l, 4, mid, POINT(-97.8698137568394 22.2893319525064) -TGU, Toncontin Int'l, 4, mid, POINT(-87.2192116348986 14.0599852192071) -THR, Mehrabad Int'l, 4, major, POINT(51.3208069717572 35.6913743304946) -TIA, Tirane Rinas, 4, major, POINT(19.7150324049722 41.4208514680567) -TIJ, General Abelardo L Rodriguez Int'l, 4, major, POINT(-116.975476095598 32.5460499135013) -TLC, Jose Maria Morelos Y Pavon, 4, mid, POINT(-99.5706494463542 19.3386880423032) -TLL, Ulemiste, 4, major, POINT(24.798964869983 59.4165014697451) -TLV, Ben Gurion, 4, major, POINT(34.8708499180995 32.0007468501844) -TMS, São Tomé Salazar, 4, mid, POINT(6.71282193005667 0.374744213699427) -TNR, Antananarivo Ivato, 4, mid, POINT(47.4753540009579 -18.7993348763082) -TPA, Tampa Int'l, 4, major, POINT(-82.534824252055 27.9800400852184) -VLN, Zim Valencia, 4, mid, POINT(-67.9223617121873 10.1540056883979) -VOG, Gumrak, 4, mid, POINT(44.354767968489 48.7916764657611) -VTE, Vientiane, 4, mid, POINT(102.568238195728 17.9754595948321) -VVI, Viru Viru Int'l, 4, mid, POINT(-63.1403888218213 -17.6479468257839) -WLG, Wellington Int'l, 4, major, POINT(174.811665268238 -41.3289891844659) -YPR, Prince Rupert, 4, mid, POINT(-130.445587 54.292) -YQG, Windsor, 4, mid, POINT(-82.9600877389448 42.2658784727198) -YQR, Regina, 4, mid, POINT(-104.655433975371 50.4332192867183) -YVR, Vancouver Int'l, 4, major, POINT(-123.180867003812 49.1935590395715) -YWG, Winnipeg Int'l, 4, major, POINT(-97.2267694809585 49.9033302471671) -YXE, John G Diefenbaker Int'l, 4, mid, POINT(-106.690181967554 52.1701439447381) -YXY, Whitehorse Int'l, 4, mid, POINT(-135.076210089402 60.7141521481397) -YYC, Calgary Int'l, 4, major, POINT(-114.010560500236 51.1308572567549) -YYG, Charlottetown, 4, mid, POINT(-63.1312341333234 46.2858131367525) -YYQ, Churchill, 4, mid, POINT(-94.0813639506318 58.7497237849788) -YYT, St John's Int'l, 4, mid, POINT(-52.7433337428638 47.6131179007955) -YZF, Yellowknife, 4, mid, POINT(-114.437846335049 62.4707373610202) -ZAG, Zagreb, 4, major, POINT(16.0615138009014 45.7333266730984) -ZNZ, Zanzibar, 4, mid, POINT(39.2223319841558 -6.21857034620282) -REK, Reykjavik Air Terminal, 4, mid, POINT(-21.9466344031327 64.1318728609901) -ARH, Arkhangelsk-Talagi, 4, mid, POINT(40.7133465694594 64.5967437730455) -KZN, Kazan Int'l, 4, major, POINT(49.2984458036407 55.6080601429764) -ORY, Paris Orly, 4, major, POINT(2.36737912783773 48.7313030458052) -YQB, Québec, 4, major, POINT(-71.3839280711731 46.7915684363308) -YUL, Montréal-Trudeau, 4, major, POINT(-73.7493162650417 45.4583512294531) -NRT, Narita Int'l, 4, major, POINT(140.384401709179 35.7640560727828) -NGO, Chubu Centrair Int'l, 4, major, POINT(136.814771286824 34.8590296958162) -OKD, Okadama, 4, mid, POINT(141.382100450075 43.1106495990978) -BGO, Bergen Flesland, 4, major, POINT(5.22725311562336 60.2890610502966) -TOS, Tromsø Langnes, 4, major, POINT(18.9072624292132 69.6796790473478) -BEL, Val de Caes Int'l, 4, mid, POINT(-48.4795602893793 -1.38974628795546) -CGR, Campo Grande Int'l, 4, mid, POINT(-54.6689498781305 -20.4572717360311) -CWB, Afonso Pena Int'l, 4, mid, POINT(-49.1737093663469 -25.5360001430558) -FOR, Pinto Martins Int'l, 4, mid, POINT(-38.5407472498334 -3.77859496233091) -GRU, São Paulo-Guarulhos Int'l, 4, major, POINT(-46.481753608842 -23.4261155770421) -GYN, Santa Genoveva, 4, mid, POINT(-49.2266464905994 -16.6323665721637) -POA, Salgado Filho Int'l, 4, mid, POINT(-51.1770409488172 -29.9901930170609) -REC, Gilberto Freyre Int'l, 4, mid, POINT(-34.9182667174851 -8.13162553076239) -SSA, Deputado Luis Eduardo Magalhaes Int'l, 4, mid, POINT(-38.3347989911732 -12.9143614970326) -MDZ, El Plumerillo, 4, mid, POINT(-68.7984838394473 -32.8278001692719) -MAO, Eduardo Gomes Int'l, 4, mid, POINT(-60.0460645898854 -3.0321390062591) -NSI, Yaoundé Nsimalen Int'l, 4, mid, POINT(11.5479941396807 3.71484520708126) -PVG, Shanghai Pudong Int'l, 4, major, POINT(121.801518760578 31.1523090295533) -ADJ, Marka Int'l, 4, mid, POINT(35.9841052362449 31.9741994015442) -MLE, Male Int'l, 4, major, POINT(73.5273902836844 4.18870090323372) -VER, Gen. Heriberto Jara Int'l, 4, mid, POINT(-96.1835702143695 19.1424237025017) -OXB, Osvaldo Vieira Int'l, 4, mid, POINT(-15.651185561666 11.8889231454855) -DVO, Francisco Bangoy Int'l, 4, major, POINT(125.645066609434 7.13053746163073) -SEZ, Seychelles Int'l, 4, mid, POINT(55.5115519246793 -4.67106914178521) -DKR, Léopold Sedar Senghor Int'l, 4, major, POINT(-17.490407907719 14.7456306146748) -PZU, Port Sudan New Int'l, 4, mid, POINT(37.2387475981025 19.4341052385231) -TAS, Tashkent Int'l, 4, major, POINT(69.2666137241129 41.2622338767383) -CPH, Copenhagen, 3, major, POINT(12.6493508684508 55.6285017221528) -BBU, Aeroportul National Bucuresti-Baneasa, 3, mid, POINT(26.0857251587764 44.497041455972) -BUD, Ferihegy, 3, major, POINT(19.2622301677881 47.433274269248) -CKG, Chongqing Jiangbei Int'l, 3, major, POINT(106.638019704811 29.7240422241688) -CLT, Douglas Int'l, 3, major, POINT(-80.9439277342763 35.2204281685597) -DTW, Detroit Metro, 3, major, POINT(-83.3478935065615 42.2257204508004) -DUB, Dublin, 3, major, POINT(-6.24388491037139 53.42700828497) -FAI, Fairbanks Int'l, 3, major, POINT(-147.865721120795 64.8180981117369) -HAM, Hamburg, 3, major, POINT(10.005647830925 53.6320011640866) -KUL, Kuala Lumpur Int'l, 3, major, POINT(101.713886325743 2.74751295791811) -LAS, Mccarran Int'l, 3, major, POINT(-115.151323951283 36.0849602383367) -MCO, Orlando Int'l, 3, major, POINT(-81.3073713307985 28.4311506791138) -MSP, Minneapolis St. Paul Int'l, 3, major, POINT(-93.2081003718301 44.8820263631968) -MUC, Franz-Josef-Strauss, 3, major, POINT(11.7880627192437 48.3538373961608) -PHL, Philadelphia Int'l, 3, major, POINT(-75.2429857676998 39.876087236427) -PHX, Sky Harbor Int'l, 3, major, POINT(-112.01363529773 33.4358607639498) -SLC, Salt Lake City Int'l, 3, major, POINT(-111.981984879993 40.7867290053708) -STL, Lambert St Louis Int'l, 3, major, POINT(-90.3659545350675 38.7427163155204) -WAW, Okecie Int'l, 3, major, POINT(20.9727263383587 52.171026749259) -ZRH, Zurich Int'l, 3, major, POINT(8.56221279534765 47.4523895064915) -CRL, Gosselies, 3, mid, POINT(4.4543736298165 50.4571296549567) -MUCf, Munich Freight Terminal, 3, major, POINT(11.7694828593654 48.3497964078377) -BCN, Barcelona, 3, major, POINT(2.07800334981292 41.3031552797463) -PRG, Ruzyn, 3, major, POINT(14.2674849854076 50.1076511703671) -HKG, Hong Kong Int'l, 2, major, POINT(113.935016387376 22.3153328280868) -TPE, Taoyuan, 2, major, POINT(121.231370453323 25.0767411043346) -AMS, Schiphol, 2, major, POINT(4.76437693232812 52.3089323889822) -SIN, Singapore Changi, 2, major, POINT(103.986413880993 1.35616083528126) -LHR, London Heathrow, 2, major, POINT(-0.453156652063309 51.4709958799938) -AKL, Auckland Int'l, 2, major, POINT(174.791719433715 -37.0063551142815) -ANC, Anchorage Int'l, 2, major, POINT(-149.981725100633 61.1728936745367) -ATL, Hartsfield-Jackson Atlanta Int'l, 2, major, POINT(-84.4253974336047 33.6405290807352) -PEK, Beijing Capital, 2, major, POINT(116.588174004661 40.078766336331) -BOG, Eldorado Int'l, 2, major, POINT(-74.1433718001028 4.69883276192097) -BOM, Chhatrapati Shivaji Int'l, 2, major, POINT(72.8745639500051 19.0951019488402) -BOS, Gen E L Logan Int'l, 2, major, POINT(-71.0164066172958 42.3665658198506) -BWI, Baltimore-Washington Int'l Thurgood Marshall,2, major, POINT(-76.6686428352448 39.1793943583568) -CAI, Cairo Int'l, 2, major, POINT(31.3997430067114 30.1119904385575) -CAS, Casablanca-Anfa, 2, mid, POINT(-7.66321880771143 33.5627883851079) -CCS, Simón Bolivar Int'l, 2, mid, POINT(-67.0057488076316 10.5973549146064) -CPT, Cape Town Int'l, 2, major, POINT(18.5976565083138 -33.9704466120395) -CTU, Chengdushuang Liu, 2, major, POINT(103.956136481695 30.5810712647464) -DEL, Indira Gandhi Int'l, 2, major, POINT(77.0878362565332 28.5592039760586) -DEN, Denver Int'l, 2, major, POINT(-104.673797338542 39.8494613881509) -DFW, Dallas-Ft. Worth Int'l, 2, major, POINT(-97.0403710741144 32.9001505594816) -DMK, Don Muang Int'l, 2, major, POINT(100.602578626505 13.9202766010347) -DXB, Dubai Int'l, 2, major, POINT(55.3540769172243 25.2525655938182) -EWR, Newark Int'l, 2, major, POINT(-74.1771472796706 40.6904798278929) -EZE, Ministro Pistarini Int'l, 2, major, POINT(-58.5412456939382 -34.8136469380323) -FLL, Fort Lauderdale Hollywood Int'l, 2, major, POINT(-80.1452588465189 26.0717095746827) -IAH, George Bush Intercontinental, 2, major, POINT(-95.3337047912947 29.9865909034907) -IST, Atatürk Hava Limani, 2, major, POINT(28.8195493087893 40.9778388177797) -JNB, OR Tambo Int'l, 2, major, POINT(28.2319885648741 -26.1320953994887) -JNU, Juneau Int'l, 2, mid, POINT(-134.583573037872 58.3589441045951) -LAX, Los Angeles Int'l, 2, major, POINT(-118.402468548522 33.9441742543586) -LIN, Linate, 2, major, POINT(9.27996629691061 45.4603938456252) -MEL, Melbourne Int'l, 2, major, POINT(144.848998091131 -37.6699411967893) -MEX, Lic Benito Juarez Int'l, 2, major, POINT(-99.0826079514239 19.4354695720494) -MNL, Ninoy Aquino Int'l, 2, major, POINT(121.004122083437 14.5068323762967) -NBO, Jomo Kenyatta Int'l, 2, major, POINT(36.9250887490365 -1.33052964350634) -HNL, Honolulu Int'l, 2, major, POINT(-157.919783173755 21.332022315024) -ORD, Chicago O'Hare Int'l, 2, major, POINT(-87.90513439065 41.9765291023803) -RUH, King Khalid Int'l, 2, major, POINT(46.701829023464 24.9590317436512) -SCL, Arturo Merino Benitez Int'l, 2, major, POINT(-70.7936860162974 -33.3968336342597) -SEA, Tacoma Int'l, 2, major, POINT(-122.302289722924 47.4435819127259) -SFO, San Francisco Int'l, 2, major, POINT(-122.383470344449 37.6170250868053) -SHA, Hongqiao, 2, major, POINT(121.341183788567 31.1872574314078) -SVO, Sheremtyevo, 2, major, POINT(37.4159690348414 55.966447172512) -YYZ, Toronto-Pearson Int'l, 2, major, POINT(-79.6114193247449 43.6809595186356) -SYD, Kingsford Smith, 2, major, POINT(151.166067305601 -33.9365832057717) -HEL, Helsinki Vantaa, 2, major, POINT(24.9682078665914 60.3187158912982) -CDG, Charles de Gaulle Int'l, 2, major, POINT(2.54186776739457 49.0144200969386) -TXL, Berlin-Tegel Int'l, 2, major, POINT(13.2903090925074 52.5544287044101) -VIE, Vienna Schwechat Int'l, 2, major, POINT(16.5607679642129 48.1197563052538) -FRA, Frankfurt Int'l, 2, major, POINT(8.57182286907608 50.0506770895207) -FCO, Leonardo da Vinci Int'l, 2, major, POINT(12.2501008973638 41.7950786307394) -ITM, Osaka Int'l, 2, major, POINT(135.442475256249 34.7901980848749) -GMP, Gimpo Int'l, 2, major, POINT(126.802392860276 37.5573005399508) -OSL, Oslo Gardermoen, 2, major, POINT(11.0991032762581 60.1935783171386) -BSB, Juscelino Kubitschek Int'l, 2, major, POINT(-47.9207885133625 -15.8699985002824) -CGH, Congonhas Int'l, 2, major, POINT(-46.6591155302196 -23.62685882701) -GIG, Rio de Janeiro-Antonio Carlos Jobim Int'l, 2, major, POINT(-43.2483813790683 -22.8123437125006) -MAD, Madrid Barajas, 2, major, POINT(-3.56902665458863 40.4681282733923) -SJU, Luis Muñoz Marin, 2, major, POINT(-66.0042299757548 18.4380770734949) -ARN, Arlanda, 2, major, POINT(17.9307299016916 59.6511203397372) -CGK, Soekarno-Hatta Int'l, 2, major, POINT(106.654296151172 -6.1266029559729) -ATH, Eleftherios Venizelos Int'l, 2, major, POINT(23.9471160554073 37.9362331299254) +abbrev:keyword,name:text, scalerank:integer,type:keyword, location:geo_point, country:keyword, city:keyword, city_location:geo_point +LUH, Sahnewal, 9, small, POINT(75.9570722403652 30.8503598561702), India, Ludhiāna, POINT(75.85 30.91) +SSE, Solapur, 9, mid, POINT(75.9330597710755 17.625415183635), India, Solāpur, POINT(75.92 17.68) +IXR, Birsa Munda, 9, mid, POINT(85.3235970368767 23.3177245989962), India, Rānchi, POINT(85.33 23.36) +AWZ, Ahwaz, 9, mid, POINT(48.7471065435931 31.3431585560757), Iran, Ahvāz, POINT(48.6692 31.3203) +GWL, Gwalior, 9, [mid,military], POINT(78.2172186546348 26.285487697937), India, Gwalior, POINT(78.178 26.2215) +HOD, Hodeidah Int'l, 9, mid, POINT(42.97109630194 14.7552534413725), Yemen, Al Ḩudaydah, POINT(42.9511 14.8022) +IDR, Devi Ahilyabai Holkar Int'l, 9, mid, POINT(75.8092915005895 22.727749187571), India, Indore, POINT(75.8472 22.7167) +ISK, Gandhinagar, 9, mid, POINT(73.8105674924689 19.9660205672806), India, Nāsik, POINT(73.78 20.0) +IXC, Chandigarh Int'l, 9, [major,military], POINT(76.8017261105242 30.6707248949667), India, Chandīgarh, POINT(76.78 30.75) +IXU, Aurangabad, 9, mid, POINT(75.3958432922005 19.8672969621082), India, Jālna, POINT(75.8864 19.841) +LYP, Faisalabad Int'l, 9, [mid,military], POINT(72.9878190922305 31.3627435480862), Pakistan, Faisalabad, POINT(73.0911 31.4167) +OMS, Omsk Tsentralny, 9, mid, POINT(73.3163595376585 54.9576482934059), Russia, Omsk, POINT(73.3833 54.9667) +OVB, Novosibirsk Tolmachev, 9, mid, POINT(82.6671524525865 55.0095847136264), Russia, Novosibirsk, POINT(82.9167 55.0333) +OZH, Zaporozhye Int'l, 9, [mid,military], POINT(35.3018728575279 47.8732635579023), Ukraine, Zaporizhzhia, POINT(35.1175 47.85) +PKU, Simpang Tiga, 9, mid, POINT(101.446569298441 0.464600872998505), Indonesia, Pekanbaru, POINT(101.4453 0.5092) +ROP, Rota Int'l, 9, mid, POINT(145.243980298582 14.1717712971216), Northern Mariana Islands, Capitol Hill, POINT(145.7546 15.2137) +SGC, Surgut, 9, mid, POINT(73.4084964764375 61.3401672194481), Russia, Surgut, POINT(73.4333 61.25) +TRZ, Tiruchirappalli, 9, mid, POINT(78.7089578747476 10.7603571306554), India, Trichinopoly, POINT(78.7047 10.7903) +TUK, Turbat Int'l, 9, mid, POINT(63.0279333519181 25.988794590011), Pakistan, Turbat, POINT(63.0544 26.0031) +UET, Quetta Int'l, 9, mid, POINT(66.9487311480949 30.249043186181), Pakistan, Quetta, POINT(67.0 30.1833) +ZAH, Zahedan Int'l, 9, mid, POINT(60.900708564915 29.4752941956573), Iran, Zāhedān, POINT(60.8628 29.4964) +MLG, Abdul Rachman Saleh, 9, [mid,military], POINT(112.711418617258 -7.92998002840567), Indonesia, Malang, POINT(112.62 -7.98) +BAX, Barnaul, 9, mid, POINT(83.5504532124038 53.3633850813046), Russia, Barnaul, POINT(83.75 53.3333) +VIAX, Adampur, 9, [military,mid], POINT(75.7584828456005 31.4329422397715), India, Jalandhar, POINT(75.4432 31.2569) +VIBY, Bareilly, 9, military, POINT(79.452002687657 28.4218087161144), India, Bareilly, POINT(79.415 28.364) +OPQS, Dhamial, 9, small, POINT(73.0320498392002 33.5614146278861), Pakistan, Rawalpindi, POINT(73.0333 33.6) +CJJ, Cheongju Int'l, 9, major, POINT(127.495916124681 36.7220227766673), South Korea, Cheongju, POINT(127.4833 36.6333) +KWJ, Gwangju, 9, [mid,military], POINT(126.810839481226 35.1400051390198), South Korea, Naju, POINT(126.7167 35.0333) +TAE, Daegu Int'l, 9, mid, POINT(128.637537699933 35.8999277969087), South Korea, Daegu, POINT(128.6017 35.8717) +USN, Ulsan, 9, mid, POINT(129.355731047528 35.5928957527107), South Korea, Ulsan, POINT(129.3167 35.55) +WIIT, Radin Inten II, 9, mid, POINT(105.176060419161 -5.242566777132), Indonesia, Bandar Lampung, POINT(105.2667 -5.45) +IXD, Allahabad, 9, military, POINT(81.7317271462187 25.443522027821), India, Prayagraj, POINT(81.8464 25.4358) +CEK, Chelyabinsk, 9, mid, POINT(61.5122589740201 55.2977919496055), Russia, Chelyabinsk, POINT(61.4 55.15) +TNN, Tainan, 8, [military,mid], POINT(120.209733318093 22.950667918347), Taiwan, Tainan, POINT(120.1833 22.9833) +RMQ, Taichung, 8, [military,mid], POINT(120.630703547584 24.2666555567115), Taiwan, Taichung, POINT(120.6794 24.1439) +RTM, Rotterdam The Hague, 8, mid, POINT(4.43384434962876 51.9491301899382), Netherlands, The Hague, POINT(4.31 52.08) +VOZ, Voronezh-Chertovitskoye, 8, mid, POINT(39.2254496447973 51.8126171268344), Russia, Voronezh, POINT(39.2106 51.6717) +LPL, Liverpool John Lennon, 8, major, POINT(-2.85862065784938 53.3363751054422), United Kingdom, Liverpool, POINT(-2.9919 53.4075) +VTZ, Vishakapatnam, 8, mid, POINT(83.2235216387465 17.7279577384364), India, Vishākhapatnam, POINT(83.2978 17.7042) +UPG, Sultan Hasanuddin Int'l, 8, major, POINT(119.545691342151 -5.05893689455779), Indonesia, Makassar, POINT(119.4362 -5.1619) +VAV, Vava'u Int'l, 8, mid, POINT(-173.968093944159 -18.5860058550654), Tonga, Neiafu, POINT(-173.9831 -18.6508) +NCL, Newcastle Int'l, 8, major, POINT(-1.71034578407216 55.037084860802), United Kingdom, Gateshead, POINT(-1.6 54.95) +LCE, Goloson Int'l, 8, mid, POINT(-86.8514685020011 15.7451596659126), Honduras, La Ceiba, POINT(-86.7931 15.7792) +MED, Madinah Int'l, 8, major, POINT(39.6991359560417 24.5442339605661), Saudi Arabia, Badr Ḩunayn, POINT(38.7906 23.78) +YMX, Mirabel Int'l, 8, mid, POINT(-74.0287382984814 45.6832250979267), Canada, Montréal, POINT(-73.5617 45.5089) +PLQ, Palanga Int'l, 8, mid, POINT(21.0974463986251 55.9713426235358), Lithuania, Klaipėda, POINT(21.1667 55.75) +JAI, Jaipur Int'l, 8, mid, POINT(75.8010104192668 26.8211798100605), India, Nawai, POINT(75.924 26.3824) +IXW, Sonari, 8, mid, POINT(86.1724662363776 22.8154145110242), India, Jamshedpur, POINT(86.1842 22.7925) +YEI, Yenisehir, 8, mid, POINT(29.54492 40.2555395007473), Turkey, İnegöl, POINT(29.5097 40.0806) +ADA, Şakirpaşa, 8, major, POINT(35.2969614268338 36.9852090641795), Turkey, Adana, POINT(35.3213 37.0) +ADQ, Kodiak, 8, mid, POINT(-152.485638515235 57.7485921070483), , , +AMA, Amarillo Int'l, 8, major, POINT(-101.705352772697 35.2184031919398), United States, Amarillo, POINT(-101.8316 35.1984) +ASP, Alice Springs, 8, mid, POINT(133.902918 -23.801968), Australia, Alice Springs, POINT(133.8667 -23.7) +ATQ, Raja Sansi Int'l, 8, [mid,military], POINT(74.8071559719824 31.7068220258888), Pakistan, Lahore, POINT(74.3436 31.5497) +BBI, Biju Patnaik, 8, mid, POINT(85.8168899544429 20.2526659754734), India, Bhubaneshwar, POINT(85.84 20.27) +BET, Bethel, 8, mid, POINT(-161.83898695944 60.7787379834088), , , +BGA, Palonegro, 8, mid, POINT(-73.1809207725361 7.12770915402685), Colombia, Bucaramanga, POINT(-73.0 7.1333) +BHM, Birmingham Int'l, 8, major, POINT(-86.7523773615462 33.5618672828058), United States, Hoover, POINT(-86.8068 33.3763) +BHQ, Broken Hill, 8, mid, POINT(141.470407303097 -31.998996737463), Australia, Broken Hill, POINT(141.4667 -31.95) +BIL, Logan Int'l, 8, major, POINT(-108.536929388125 45.8036855715278), United States, Billings, POINT(-108.5526 45.7891) +BIS, Bismarck Muni., 8, mid, POINT(-100.757471303717 46.7751066661614), United States, Bismarck, POINT(-100.7694 46.8143) +BJX, Del Bajio Int'l, 8, mid, POINT(-101.478753382467 20.9858871211938), Mexico, León de los Aldama, POINT(-101.6833 21.1167) +BNI, Benin, 8, mid, POINT(5.603682560067 6.31716689207818), Nigeria, Benin City, POINT(5.6222 6.3333) +BOJ, Bourgas, 8, major, POINT(27.5164093662953 42.5670835487702), Bulgaria, Burgas, POINT(27.4702 42.503) +BRE, Bremen, 8, major, POINT(8.7858617703132 53.052287104156), Germany, Bremen, POINT(8.8 53.0833) +BRM, Jacinto Lara Int'l, 8, mid, POINT(-69.356102 10.0453), Venezuela, Barquisimeto, POINT(-69.3467 10.0678) +BRO, Brownsville-South Padre Island Int'l, 8, mid, POINT(-97.431765340232 25.9062743545347), Mexico, Heroica Matamoros, POINT(-97.5042 25.8797) +BRS, Bristol Int'l, 8, major, POINT(-2.71086469134308 51.3862934189148), United Kingdom, Caerdydd, POINT(-3.1792 51.4817) +BTR, Baton Rouge Metro, 8, major, POINT(-91.1567544048105 30.5326138040586), United States, Baton Rouge, POINT(-91.1311 30.442) +BTS, Bratislava-M.R. Štefánik, 8, major, POINT(17.1999850022208 48.1698379062535), Slovakia, Bratislava, POINT(17.1097 48.1439) +BTV, Burlington Int'l, 8, mid, POINT(-73.1550787790668 44.4692066040732), United States, South Burlington, POINT(-73.2202 44.4622) +CAE, Columbia Metro, 8, major, POINT(-81.1093352429377 33.9342054584275), United States, West Columbia, POINT(-81.0936 33.9932) +CCJ, Calicut Int'l, 8, major, POINT(75.950993063051 11.1395520526064), India, Calicut, POINT(75.77 11.25) +CCK, Cocos (Keeling) Islands, 8, mid, POINT(96.8287472144207 -12.1851585953293), , , +CFU, Corfu Int'l (Ioannis Kapodistrias), 8, mid, POINT(19.9147561641662 39.6067554505259), Greece, Kérkyra, POINT(19.9214 39.6239) +CGQ, Changchun Longjia Int'l, 8, major, POINT(125.690456812998 43.993011479577), China, Changchun, POINT(125.2 43.9) +CHS, Charleston Int'l, 8, [major,military], POINT(-80.0369337438262 32.8845301562965), United States, North Charleston, POINT(-80.0722 32.9067) +CJB, Coimbatore, 8, mid, POINT(77.038893772262 11.0301415125983), India, Coimbatore, POINT(76.9558 11.0168) +CLJ, Someseni, 8, mid, POINT(23.6869812680486 46.7826626340243), Romania, Cluj-Napoca, POINT(23.5833 46.7667) +CMW, Ignacio Agramonte, 8, mid, POINT(-77.8451039935167 21.4247037281961), Cuba, Camagüey, POINT(-77.9075 21.3839) +CPR, Casper/Natrona County Int'l, 8, major, POINT(-106.464444809692 42.8971900483006), United States, Casper, POINT(-106.3208 42.842) +CRK, Clark Int'l, 8, major, POINT(120.550770223914 15.1876422423888), Philippines, Angeles City, POINT(120.5847 15.1472) +CRW, Yeager, 8, [major,military], POINT(-81.5964164667526 38.3705914372865), United States, South Charleston, POINT(-81.7155 38.3426) +CTA, Catania Fontanarossa, 8, major, POINT(15.0674605007053 37.470072800341), Italy, Catania, POINT(15.0903 37.5) +CTM, Chetumal Int'l, 8, mid, POINT(-88.3242600415365 18.506434233376), Mexico, Chetumal, POINT(-88.3053 18.5036) +CWL, Cardiff, 8, major, POINT(-3.33956876429118 51.3986220911017), United Kingdom, Caerdydd, POINT(-3.1792 51.4817) +CYB, Gerrard Smith, 8, mid, POINT(-79.879461638003 19.6898653962844), Cuba, Ciego de Ávila, POINT(-78.7631 21.8481) +CZM, Cozumel Int'l, 8, mid, POINT(-86.9304064070436 20.5115543771647), Mexico, Cozumel, POINT(-86.9493 20.5104) +DAY, James M. Cox Dayton Int'l, 8, major, POINT(-84.2204594238102 39.8990402865362), United States, Vandalia, POINT(-84.193 39.8791) +DBO, Dubbo, 8, mid, POINT(148.569717 -32.218681), Australia, Dubbo, POINT(148.6011 -32.2569) +DCA, Washington Nat'l, 8, major, POINT(-77.0433373925631 38.8537162012123), United States, Waldorf, POINT(-76.9194 38.6085) +DGO, Durango Int'l, 8, mid, POINT(-104.533846024964 24.1261948326182), Mexico, Colonia General Felipe Ángeles, POINT(-104.6 23.9167) +DNK, Voloskoye, 8, mid, POINT(35.0939060224975 48.3675718021117), Ukraine, Dnipro, POINT(35.04 48.4675) +DOK, Donetsk, 8, major, POINT(37.7448085572103 48.0691671285582), Ukraine, Makiivka, POINT(37.9611 48.0556) +DZO, Santa Bernardina Int'l, 8, mid, POINT(-56.4992636213744 -33.3591084475501), Uruguay, Durazno, POINT(-56.5167 -33.3667) +EDI, Edinburgh Int'l, 8, major, POINT(-3.36428468513554 55.9485540113639), United Kingdom, Dunfermline, POINT(-3.4393 56.0719) +EIS, Terrance B. Lettsome Int'l, 8, mid, POINT(-64.5371514365794 18.4443618557983), British Virgin Islands, Road Town, POINT(-64.6167 18.4167) +EKO, Elko Reg., 8, mid, POINT(-115.786479232249 40.8276058815225), United States, Elko, POINT(-115.7678 40.8381) +ESE, Ensenada, 8, mid, POINT(-116.595724400418 31.7977139760569), Mexico, Rodolfo Sánchez Taboada, POINT(-116.5911 31.7958) +FAE, Vágar, 8, mid, POINT(-7.2708 62.0625), Faroe Islands, Tórshavn, POINT(-6.7833 62.0) +FAR, Hector Int'l, 8, [mid,military], POINT(-96.8254561269675 46.9198178811323), United States, Fargo, POINT(-96.8292 46.8651) +FAT, Fresno Yosemite Int'l, 8, mid, POINT(-119.720001323576 36.7698128373959), United States, Hanford, POINT(-119.6549 36.3274) +FLG, Flagstaff Pulliam, 8, mid, POINT(-111.674656171675 35.1389116757976), United States, Flagstaff, POINT(-111.6194 35.1872) +FRS, Mundo Maya Int'l, 8, mid, POINT(-89.8778404226508 16.9149741642226), Guatemala, El Chal, POINT(-89.65 16.6333) +FSD, Sioux Falls Reg., 8, mid, POINT(-96.7313831017541 43.5801934972763), United States, Sioux Falls, POINT(-96.7311 43.5396) +GEG, Spokane Int'l, 8, major, POINT(-117.536836628585 47.6254781278368), United States, Spokane, POINT(-117.433 47.6671) +GGT, Exuma Int'l, 8, mid, POINT(-75.872613085304 23.5638829069259), The Bahamas, Nassau, POINT(-77.3386 25.0781) +GIB, Gibraltar, 8, mid, POINT(-5.34677180033388 36.1512747504173), Gibraltar, Gibraltar, POINT(-5.35 36.1333) +GRR, Gerald R. Ford Int'l, 8, mid, POINT(-85.529573207274 42.8847776020908), United States, Kentwood, POINT(-85.5926 42.8852) +GSO, Triad Int'l, 8, major, POINT(-79.9364867577484 36.1053781998932), United States, Greensboro, POINT(-79.8271 36.0956) +GTF, Great Falls Int'l, 8, mid, POINT(-111.35668472784 47.482270729263), United States, Bozeman, POINT(-111.0558 45.6833) +GZT, Gaziantep Oğuzeli Int'l, 8, major, POINT(37.47380325219 36.9453633446875), Turkey, Gaziantep, POINT(37.3792 37.0628) +HBX, Hubli, 8, mid, POINT(75.0863155680281 15.3591833386229), India, Saundatti, POINT(75.1167 15.7833) +HDY, Hat Yai Int'l, 8, mid, POINT(100.393751274671 6.93634231940664), Thailand, Hat Yai, POINT(100.4667 7.0167) +HFE, Hefei-Luogang, 8, mid, POINT(117.304197015888 31.7798576795778), China, Hefei, POINT(117.2808 31.8639) +HRG, Hurghada Int'l, 8, major, POINT(33.8071606414118 27.1804260918186), Egypt, Al Ghardaqah, POINT(33.8117 27.2578) +HRK, Kharkov Int'l, 8, major, POINT(36.2822010773503 49.9215360631551), Ukraine, Kharkiv, POINT(36.2311 49.9925) +HSV, Huntsville Int'l, 8, major, POINT(-86.7749430563373 34.6483344609319), United States, Hartselle, POINT(-86.9396 34.4391) +IBA, Ibadan, 8, mid, POINT(3.9738133433229 7.36034397269393), Nigeria, Ibadan, POINT(3.9167 7.3964) +ICT, Kansas City Int'l, 8, major, POINT(-97.4287387683976 37.6529279603903), United States, Wichita, POINT(-97.3443 37.6895) +ILM, Wilmington Int'l, 8, mid, POINT(-77.9103756560469 34.2667840671996), United States, Murraysville, POINT(-77.8429 34.2919) +ILR, Ilorin Int'l, 8, mid, POINT(4.49484038819934 8.43537651935241), Nigeria, Ilorin, POINT(4.55 8.5) +INL, Falls Int'l, 8, mid, POINT(-93.3980027552794 48.5659930848414), United States, Hibbing, POINT(-92.9486 47.398) +INV, Inverness, 8, mid, POINT(-4.06359175587141 57.5395002923424), United Kingdom, Nairn, POINT(-3.869 57.586) +IPL, Imperial Cty., 8, mid, POINT(-115.57199556658 32.8339586685524), Mexico, Mexicali, POINT(-115.4678 32.6633) +IXJ, Jammu, 8, mid, POINT(74.8423077638915 32.6810428886225), India, Jammu, POINT(74.87 32.73) +IXM, Madurai, 8, mid, POINT(78.0911394937194 9.83718627877566), India, Madurai, POINT(78.1198 9.9252) +JDH, Jodhpur, 8, [major,military], POINT(73.0505491895671 26.2637623458351), India, Jodhpur, POINT(73.02 26.28) +JLR, Jabalpur, 8, mid, POINT(80.0587438885277 23.1845328746465), India, Jabalpur, POINT(79.9333 23.1667) +JRO, Kilimanjaro Int'l, 8, mid, POINT(37.0651896067748 -3.42444495998178), Tanzania, Arusha, POINT(36.6833 -3.3667) +KAD, Kaduna, 8, mid, POINT(7.32525347407434 10.6946192862391), Nigeria, Kaduna, POINT(7.4333 10.5167) +KGA, Kananga, 8, mid, POINT(22.4783332482689 -5.90016656227041), Congo (Kinshasa), Kananga, POINT(22.4488 -5.897) +KMS, Kumasi, 8, mid, POINT(-1.59257526582361 6.71460638750365), Ghana, Kumasi, POINT(-1.6167 6.6667) +KNA, Viña del Mar, 8, mid, POINT(-71.4806025354969 -32.948391765136), Chile, Viña del Mar, POINT(-71.5517 -33.0244) +KNU, Kanpur, 8, mid, POINT(80.3675338772002 26.4388334467042), India, Cawnpore, POINT(80.3319 26.4499) +KOA, Kona Int'l at Keahole, 8, mid, POINT(-156.040889471058 19.7370991399442), United States, Waimea, POINT(-155.6381 20.0124) +KOI, Kirkwall, 8, mid, POINT(-2.90137849524508 58.9544288788303), United Kingdom, Kirkwall, POINT(-2.96 58.981) +KTU, Kota, 8, mid, POINT(75.8504977944552 25.1634187166743), India, Būndi, POINT(75.6372 25.4383) +KYA, Konya, 8, [major,military], POINT(32.5756732669687 37.9839945531695), Turkey, Konya, POINT(32.4833 37.8667) +LEX, Blue Grass, 8, major, POINT(-84.5982681918786 38.0374273181372), United States, Nicholasville, POINT(-84.5668 37.8906) +LIH, Lihue, 8, mid, POINT(-159.349084290522 21.9781243162088), United States, Kapaa, POINT(-159.3521 22.091) +LIT, Clinton National, 8, major, POINT(-92.2205881319289 34.7284300415179), United States, Little Rock, POINT(-92.3577 34.7256) +LMM, Los Mochis, 8, mid, POINT(-109.082694645261 25.688508826099), Mexico, Los Mochis, POINT(-108.9937 25.7835) +LOV, Venustiano Carranza Int'l, 8, mid, POINT(-101.464960031751 26.9553927160699), Mexico, Monclova, POINT(-101.4222 26.9103) +LRD, Laredo Int'l, 8, mid, POINT(-99.4556603976513 27.5436657175825), Mexico, Nuevo Laredo, POINT(-99.5069 27.4861) +LSI, Sumburgh, 8, mid, POINT(-1.28806068838753 59.8766899598999), United Kingdom, Lerwick, POINT(-1.145 60.155) +LTK, Bassel Al-Assad Int'l, 8, major, POINT(35.9442407096663 35.4073114596744), Syria, Latakia, POINT(35.7833 35.5167) +LTN, London Luton, 8, major, POINT(-0.376227267397439 51.8802952570969), United Kingdom, Luton, POINT(-0.4147 51.8783) +LYR, Svalbard Longyear, 8, mid, POINT(15.495229 78.246717), Svalbard, Longyearbyen, POINT(15.6333 78.2167) +MBJ, Sangster Int'l, 8, mid, POINT(-77.9183907635752 18.5011549298249), Jamaica, Montego Bay, POINT(-77.9167 18.4667) +MDL, Mandalay Int'l, 8, mid, POINT(95.9706535950217 21.7055490680274), Myanmar, Mandalay, POINT(96.0844 21.9831) +MDW, Chicago Midway Int'l, 8, major, POINT(-87.7421266885612 41.7883492597409), United States, Chicago, POINT(-87.6866 41.8375) +MEC, Eloy Alfaro Int'l, 8, [mid,military], POINT(-80.6833845995774 -0.949557002112883),Ecuador, Portoviejo, POINT(-80.4553 -1.0561) +MGM, Montgomery Reg., 8, major, POINT(-86.3903074602686 32.3045879909631), United States, Prattville, POINT(-86.4573 32.4597) +MHT, Manchester-Boston Reg., 8, major, POINT(-71.4375239091857 42.9279139945886), United States, Nashua, POINT(-71.491 42.7491) +DNMA, Maiduguri Int'l, 8, mid, POINT(13.0851390162471 11.8534713188527), Nigeria, Maiduguri, POINT(13.15 11.8333) +MJM, Mbuji Mayi, 8, mid, POINT(23.5721091989052 -6.12484541348812), Congo (Kinshasa), Mbuji-Mayi, POINT(23.6 -6.15) +MOT, Minot Int'l, 8, mid, POINT(-101.2913855313 48.2556049212839), United States, Minot, POINT(-101.278 48.2375) +MSO, Missoula Int'l, 8, mid, POINT(-114.083694923651 46.9187604768831), United States, Missoula, POINT(-114.0214 46.8751) +MXL, Gen R.S. Taboada Int'l, 8, mid, POINT(-115.247874047841 32.6285643324607), Mexico, Mexicali, POINT(-115.4678 32.6633) +MXP, Malpensa, 8, major, POINT(8.71295953502437 45.6274405140381), Italy, Gallarate, POINT(8.7914 45.6649) +NLK, Norfolk Island, 8, mid, POINT(167.943394116205 -29.0351592555275), , , +NUE, Nurnberg, 8, major, POINT(11.0774179739096 49.4945052170345), Germany, Nuremberg, POINT(11.0775 49.4539) +ODS, Odessa Int'l, 8, major, POINT(30.6768308310206 46.4406268759106), Ukraine, Odesa, POINT(30.7326 46.4775) +OOL, Gold Coast, 8, mid, POINT(153.512876264303 -28.1665168540202), Australia, Gold Coast, POINT(153.4 -28.0167) +ORN, Oran Es Senia, 8, mid, POINT(-0.60679696443112 35.6202747312734), Algeria, Oran, POINT(-0.6331 35.6969) +PAT, Lok Nayak Jaiprakash, 8, mid, POINT(85.0909021314663 25.5944434295605), India, Patna, POINT(85.1 25.6) +PDU, Paysandu, 8, mid, POINT(-58.0685346825257 -32.3614545292723), Uruguay, Paysandú, POINT(-58.0756 -32.3214) +PFO, Paphos Int'l, 8, major, POINT(32.4832322064926 34.7134012817335), Cyprus, Paphos, POINT(32.4167 34.7667) +PLM, Sultan Mahmud Badaruddin II, 8, mid, POINT(104.699128326762 -2.89999345005997), Indonesia, Palembang, POINT(104.7556 -2.9861) +PTG, Polokwane Int'l, 8, mid, POINT(29.4533403645644 -23.858986270166), South Africa, Polokwane, POINT(29.45 -23.9) +PUJ, Punta Cana, 8, mid, POINT(-68.3632351074649 18.563039033987), Dominican Republic, Pantanal, POINT(-68.3667 18.5333) +QRO, Queretaro Int'l, 8, mid, POINT(-100.18735943003 20.622466071278), Mexico, Querétaro, POINT(-100.3928 20.5875) +RAJ, Rajkot, 8, mid, POINT(70.7799548311565 22.3092816988361), India, Rājkot, POINT(70.7833 22.3) +RIC, Richmond Int'l, 8, major, POINT(-77.333119638113 37.5082899750901), United States, Highland Springs, POINT(-77.3285 37.5516) +RJH, Shah Makhdum, 8, mid, POINT(88.6138045704431 24.4448068623035), Bangladesh, Rājshāhi, POINT(88.6 24.3667) +ROC, Greater Rochester Int'l, 8, major, POINT(-77.6652445062197 43.1275519826482), United States, Greece, POINT(-77.6988 43.246) +ROK, Rockhampton, 8, mid, POINT(150.478897 -23.378599), Australia, Gracemere, POINT(150.4558 -23.4391) +ROV, Rostov-on-Don, 8, mid, POINT(39.8035144445391 47.2551119519754), Russia, Bataysk, POINT(39.7333 47.1667) +RTW, Saratov, 8, mid, POINT(46.035023249891 51.5606456508842), Russia, Saratov, POINT(46.0167 51.5333) +SAP, Ramón Villeda Morales Int'l, 8, mid, POINT(-87.9272365125409 15.4558630524883), Honduras, San Pedro Sula, POINT(-88.0333 15.5) +SBA, Santa Barbara Muni., 8, mid, POINT(-119.8366015808 34.4257312978783), United States, Goleta, POINT(-119.8594 34.4361) +SCC, Deadhorse, 8, mid, POINT(-148.457855 70.19751), , , +SFJ, Kangerlussuaq, 8, mid, POINT(-50.694199 67.018097), Greenland, Ilulissat, POINT(-51.1 69.2167) +SGF, Springfield Reg., 8, major, POINT(-93.3826379012003 37.2421444903024), United States, Republic, POINT(-93.4446 37.1452) +SHV, Shreveport Reg., 8, major, POINT(-93.8285222229503 32.4545798866513), United States, Shreveport, POINT(-93.7955 32.4653) +SIP, Simferopol Int'l, 8, major, POINT(33.9960529244537 45.0202173978165), Ukraine, Hvardiiske, POINT(34.0142 45.1142) +SIT, Sitka Rocky Gutierrez, 8, mid, POINT(-135.365692 57.05349), United States, Sitka, POINT(-135.3152 57.2401) +SJD, Los Cabos Int'l, 8, major, POINT(-109.717858386909 23.1626574483597), Mexico, San José del Cabo, POINT(-109.7081 23.0614) +SLE, McNary Field, 8, major, POINT(-123.007871479404 44.9105138452142), United States, Keizer, POINT(-123.0243 45.0028) +SLW, Plan de Guadalupe, 8, mid, POINT(-100.932260548587 25.5479976419974), Mexico, Saltillo, POINT(-100.9919 25.4231) +SNN, Shannon, 8, major, POINT(-8.92242885557686 52.6934537102532), Ireland, Shannon, POINT(-8.8686 52.7137) +SON, Santo Pekoa Int'l, 8, mid, POINT(167.220894919375 -15.5055387370858), Vanuatu, Luganville, POINT(167.1667 -15.5333) +SRG, Achmad Yani, 8, mid, POINT(110.378556255666 -6.97873484956982), Indonesia, Semarang, POINT(110.4167 -6.9667) +SXR, Srinagar, 8, [military,mid], POINT(74.7826243672311 33.9830909431623), India, Bāramūla, POINT(74.34 34.2) +TAP, Tapachula Int'l, 8, mid, POINT(-92.370003 14.7911281338773), Mexico, Tapachula, POINT(-92.2667 14.9) +TGD, Podgorica, 8, major, POINT(19.2466868618873 42.3679335195428), Montenegro, Podgorica, POINT(19.2629 42.4413) +TLH, Tallahassee Reg., 8, major, POINT(-84.3449953984858 30.3955576176938), United States, Tallahassee, POINT(-84.2527 30.4551) +TRN, Turin Int'l, 8, major, POINT(7.64416230362133 45.1916600734642), Italy, Turin, POINT(7.6761 45.0792) +TYN, Taiyuan Wusu Int'l, 8, major, POINT(112.625891539315 37.7545117791512), China, Taiyuan, POINT(112.5425 37.8733) +UAK, Narsarsuaq, 8, mid, POINT(-45.4164008923108 61.1625968337328), Greenland, Narsarsuaq, POINT(-45.4347 61.1458) +UTP, U-Tapao, 8, [military,mid], POINT(101.00020929048 12.6852930912664), Thailand, Phatthaya, POINT(100.889 12.9357) +VFA, Victoria Falls, 8, mid, POINT(25.8467677208826 -18.0990155983682), Zambia, Livingstone, POINT(25.8667 -17.85) +VGA, Vijaywada, 8, mid, POINT(80.7973080000675 16.528642778235), India, Vijayavāda, POINT(80.6305 16.5193) +VNS, Varanasi, 8, mid, POINT(82.8538741913527 25.4499077329822), India, Vārānasi, POINT(83.0128 25.3189) +VRA, Juan Gualberto Gomez, 8, major, POINT(-81.4367103850623 23.0395422339631), Cuba, Cárdenas, POINT(-81.2036 23.0428) +VSA, Villahermosa, 8, mid, POINT(-92.8190675836262 17.9930660113111), Mexico, Macuspana, POINT(-92.6 17.7667) +YBR, Brandon, 8, mid, POINT(-99.9458959002463 49.9047279410277), United States, Minot, POINT(-101.278 48.2375) +YED, CFB Edmonton, 8, [military,major], POINT(-113.478839054497 53.6749156618668), Canada, St. Albert, POINT(-113.6258 53.6303) +YFB, Iqaluit, 8, mid, POINT(-68.5367292441812 63.7511523537807), Canada, Iqaluit, POINT(-68.5107 63.7598) +YHM, John C. Munro Hamilton Int'l, 8, mid, POINT(-79.9264230959967 43.1633605305096), Canada, Brantford, POINT(-80.25 43.1667) +YMM, Fort McMurray, 8, mid, POINT(-111.223840046617 56.6563171390962), Canada, Wood Buffalo, POINT(-111.3284 57.6042) +YNT, Yantai, 8, [major,military], POINT(121.372047417773 37.4077044726924), China, Yantai, POINT(121.2664 37.3997) +YPE, Peace River, 8, mid, POINT(-117.443663208082 56.231924036745), Canada, Grande Prairie, POINT(-118.7947 55.1708) +YQM, Greater Moncton Int'l, 8, mid, POINT(-64.6886696807361 46.1162059639259), Canada, Moncton, POINT(-64.7714 46.1328) +YQY, Sydney/J.A. Douglas McCurdy, 8, mid, POINT(-60.0469372117026 46.1673405890504), Canada, Cape Breton, POINT(-60.1931 46.1389) +YRB, Resolute Bay, 8, mid, POINT(-94.9708023244006 74.7181860987594), , , +YSM, Fort Smith, 8, mid, POINT(-111.961059938158 60.0198749602443), Canada, Wood Buffalo, POINT(-111.3284 57.6042) +YTH, Thompson, 8, mid, POINT(-97.860733 55.797482), , , +YTS, Timmins, 8, mid, POINT(-81.372047 48.566158), Canada, Timmins, POINT(-81.3333 48.4667) +YUT, Repulse Bay, 8, mid, POINT(-86.25 66.533302), , , +YVP, Kuujjuaq, 8, mid, POINT(-68.433342 58.101959), , , +YWK, Wabush, 8, mid, POINT(-66.873009 52.926071), , , +YXD, Edmonton City Centre, 8, mid, POINT(-113.522973688581 53.5709436582812), Canada, St. Albert, POINT(-113.6258 53.6303) +YXJ, Fort St. John (N. Peace), 8, mid, POINT(-120.736439 56.246035), Canada, Fort St. John, POINT(-120.8476 56.2465) +YYB, North Bay/Jack Garland, 8, mid, POINT(-79.42491 46.358711), Canada, North Bay, POINT(-79.45 46.3) +ZAR, Zaria, 8, mid, POINT(7.68726764310577 11.1352958601071), Nigeria, Zaria, POINT(7.7 11.0667) +SKP, Skopje, 8, mid, POINT(21.6281971858229 41.9564546081544), Macedonia, Skopje, POINT(21.4317 41.9961) +VE23, Burnpur, 8, mid, POINT(86.974546776573 23.6312179107764), India, Āsansol, POINT(86.99 23.68) +VIDX, Hindon Air Force Station, 8, mid, POINT(77.3507888779117 28.7077968601071), India, Ghāziābād, POINT(77.42 28.67) +, Sunchon, 8, major, POINT(125.890825057486 39.4119659710565), North Korea, Sunch’ŏn, POINT(125.9333 39.4167) +EPLL, Łódź Władysław Reymont, 8, mid, POINT(19.4032148744037 51.72720704517), Poland, Łódź, POINT(19.4547 51.7769) +BXJ, Alma Ata N.W., 8, [mid,military], POINT(76.8782640096648 43.3554190837919), Kazakhstan, Almaty, POINT(76.8958 43.2775) +JMU, Jiamusi Dongjiao, 8, mid, POINT(130.456204704407 46.8430150223379), China, Jiamusi, POINT(130.3653 46.8081) +MDG, Mudanjiang Hailang, 8, major, POINT(129.58015153222 44.5342936299935), China, Mudanjiang, POINT(129.5997 44.5861) +ULMM, Severomorsk-3 (Murmansk N.E.), 8, [military,major], POINT(33.2903527616285 69.0168711826804), Russia, Murmansk, POINT(33.075 68.9706) +OSB, Mosul Int'l, 8, mid, POINT(43.145802 36.308601), Iraq, Mosul, POINT(43.13 36.34) +, Rostov N., 8, [military,mid], POINT(39.6353996343665 47.2774209202867), Russia, Bataysk, POINT(39.7333 47.1667) +, Rostov S.W., 8, mid, POINT(39.7972215345149 47.1158577255835), Russia, Bataysk, POINT(39.7333 47.1667) +OUL, Oulu, 8, mid, POINT(25.3728374704307 64.9287992358849), Finland, Oulu, POINT(25.4719 65.0142) +BOD, Bordeaux, 8, major, POINT(-0.701793449075243 44.8321108662674), France, Bordeaux, POINT(-0.58 44.84) +CEQ, Mandelieu, 8, mid, POINT(6.95431612028937 43.546097987045), France, Mandelieu-la-Napoule, POINT(6.9381 43.5464) +DOL, St Gatien, 8, mid, POINT(0.158653528230218 49.3616609986609), France, Le Havre, POINT(0.1 49.49) +LIL, Lille-Lesquin, 8, mid, POINT(3.10596499799813 50.5716423929581), France, Roubaix, POINT(3.1817 50.6901) +TLS, Toulouse-Blagnac, 8, major, POINT(1.37350918551153 43.6304625661601), France, Toulouse, POINT(1.444 43.6045) +FUK, Fukuoka, 8, major, POINT(130.444189541884 33.5848164332573), Japan, Fukuoka, POINT(130.4 33.5833) +HIW, Hiroshima-Nishi, 8, mid, POINT(132.419372741681 34.3713815628829), Japan, Hiroshima, POINT(132.4519 34.3914) +NKM, Nagoya, 8, mid, POINT(136.91962838414 35.2540532052867), Japan, Nagoya, POINT(136.9 35.1833) +SDJ, Sendai, 8, mid, POINT(140.930247381369 38.1382075615287), Japan, Sendai, POINT(140.8694 38.2682) +KKN, Kirkenes Hoybuktmoen, 8, mid, POINT(29.8913489500406 69.7238318113692), Russia, Nikel, POINT(30.2206 69.4081) +CGB, Marechal Rondon Int'l, 8, mid, POINT(-56.1201774754724 -15.6511470191955), Brazil, Várzea Grande, POINT(-56.1333 -15.65) +FLN, Hercilio Luz Int'l, 8, major, POINT(-48.5448122049599 -27.6646276941638), Brazil, Florianópolis, POINT(-48.4853 -27.6122) +JOI, Joinville-Lauro C. de Loyola, 8, mid, POINT(-48.8016498165616 -26.2242941374785), Brazil, Joinvile, POINT(-48.8437 -26.3204) +JPA, Presidente Castro Pinto Int'l, 8, mid, POINT(-34.9488925911125 -7.14617462402047), Brazil, João Pessoa, POINT(-34.88 -7.12) +NAT, Augusto Severo Int'l, 8, major, POINT(-35.2488410165389 -5.89912054477116), Brazil, Natal, POINT(-35.2 -5.7833) +OPO, Francisco Sa Carneiro, 8, major, POINT(-8.67127240719647 41.2368708920452), Portugal, Matosinhos, POINT(-8.7 41.1833) +SLZ, Marechal Cunha Machado Int'l, 8, mid, POINT(-44.2362344700492 -2.58350921043019), Brazil, São Luís, POINT(-44.3044 -2.5283) +SSZ, Santos Air Force Base, 8, [military,mid], POINT(-46.3052704931003 -23.9237590410637), Brazil, Santos, POINT(-46.325 -23.9369) +THE, Teresina-Senador Petronio Portella, 8, mid, POINT(-42.8212402317845 -5.06346299167191), Brazil, Teresina, POINT(-42.8042 -5.0949) +VCP, Viracopos-Campinas Int'l, 8, mid, POINT(-47.1410791911014 -23.0096239085339), Brazil, Campinas, POINT(-47.0608 -22.9058) +VIX, Eurico de Aguiar Salles, 8, mid, POINT(-40.2885368759913 -20.2574162759418), Brazil, Vitória, POINT(-40.3083 -20.2889) +ALC, Alicante, 8, major, POINT(-0.557230440363588 38.2866408993929), Spain, Alicante, POINT(-0.4831 38.3453) +LEI, Almeria, 8, mid, POINT(-2.3716014405912 36.8477672709643), Spain, Almería, POINT(-2.4681 36.8403) +VLC, Valencia, 8, mid, POINT(-0.473474930771676 39.4914597884489), Spain, Paterna, POINT(-0.4406 39.5028) +KRN, Kiruna_Airport, 8, mid, POINT(20.3351522954898 67.8256066056432), Sweden, Kiruna, POINT(20.3028 67.8489) +NRK, Norrköping Airport, 8, major, POINT(16.2339407695814 58.5833805017541), Sweden, Norrköping, POINT(16.2 58.6) +BDO, Husein Sastranegara Int'l, 8, mid, POINT(107.575611852209 -6.90042408353409), Indonesia, Cimahi, POINT(107.5548 -6.8712) +ROS, Rosario – Islas Malvinas Int'l, 8, mid, POINT(-60.7800787216586 -32.9162269743812), Argentina, Funes, POINT(-60.8167 -32.9167) +MCZ, Maceio/Zumbi dos Palmares Int'l, 8, mid, POINT(-35.7924951215833 -9.51494118540116), Brazil, Maceió, POINT(-35.735 -9.6658) +SSH, Sharm el-Sheikh Int'l, 8, mid, POINT(34.3901189267288 27.9804044199168), Egypt, Sharm ash Shaykh, POINT(34.3297 27.9122) +TCP, Taba Int'l, 8, mid, POINT(34.7758378996779 29.5944990568019), Jordan, Al ‘Aqabah, POINT(35.0056 29.5319) +AGR, Agra, 8, [major,military], POINT(77.960909176509 27.15772773475), India, Agwār, POINT(78.02 27.18) +BDQ, Vadodara, 8, mid, POINT(73.2262889533239 22.3361640021171), India, Vadodara, POINT(73.2 22.3) +KSH, Shahid Ashrafi Esfahani, 8, mid, POINT(47.1565835165639 34.3464167739108), Iran, Kermānshāh, POINT(47.065 34.3142) +BEN, Benina Int'l, 8, mid, POINT(20.2680398018516 32.0872774606553), Libya, Benghazi, POINT(20.0667 32.1167) +DHA, King Abdulaziz AB, 8, [military,major], POINT(50.1477245727844 26.2703680854768), Saudi Arabia, Dhahran, POINT(50.15 26.2667) +STY, Nueva Hespérides Int'l, 8, mid, POINT(-57.9840821176492 -31.4373883387798), Argentina, Federación, POINT(-57.9167 -30.9833) +BAIK, Baikonur Cosmodrome, 8, spaceport, POINT(63.307354423875 45.9635739403124), Kazakhstan, Baikonur, POINT(63.3167 45.6167) +KSC, Kennedy Space Center, 8, spaceport, POINT(-80.6369680911892 28.5163704772027), United States, Titusville, POINT(-80.8193 28.5727) +CSG, Centre Spatial Guyanais, 8, spaceport, POINT(-52.7684296893452 5.23941001258035), French Guiana, Kourou, POINT(-52.6499 5.16) +AUA, Queen Beatrix Int'l, 7, mid, POINT(-70.0076228563496 12.5034643630297), Aruba, Tanki Leendert, POINT(-70.022 12.5418) +JIB, Djibouti-Ambouli Int'l, 7, mid, POINT(43.1497127859956 11.5521018230172), Djibouti, Djibouti, POINT(43.145 11.5883) +IQQ, Diego Aracena Int'l, 7, [mid,military], POINT(-70.178635395533 -20.5478400878309), Chile, Iquique, POINT(-70.15 -20.2167) +SAW, Sabiha Gökçen Havaalani, 7, major, POINT(29.3095991423889 40.9043003553957), Turkey, Istanbul, POINT(28.955 41.0136) +KSA, Kosrae Island, 7, mid, POINT(162.957041225076 5.3520098571828), Federated States of Micronesia, Tofol, POINT(163.0086 5.3258) +FUN, Funafuti Int'l, 7, mid, POINT(179.19544202302 -8.52485415059424), Tuvalu, Funafuti, POINT(179.2 -8.5167) +NAG, Dr. Babasaheb Ambedkar Int'l, 7, mid, POINT(79.0537976421986 21.0899317630087), India, Nāgpur, POINT(79.0806 21.1497) +HKT, Phuket Int'l, 7, mid, POINT(98.3060384900559 8.10768475952735), Thailand, Phuket, POINT(98.3975 7.8881) +NAN, Nadi Int'l, 7, mid, POINT(177.451151198059 -17.7529129479792), Fiji, Nadi, POINT(177.4167 -17.8) +AGU, Lic. Jesús Terán Peredo Int'l, 7, mid, POINT(-102.314093740058 21.7013390329207), Mexico, Aguascalientes, POINT(-102.296 21.876) +ALL, Albenga, 7, mid, POINT(8.12314535436409 44.0458773598158), Italy, Albenga, POINT(8.2167 44.05) +AMM, Queen Alia Int'l, 7, major, POINT(35.989707162193 31.7226621600432), Jordan, Amman, POINT(35.9328 31.9497) +ARI, Chacalluta Int'l, 7, mid, POINT(-70.3357301410959 -18.3492061639579), Chile, Arica, POINT(-70.3167 -18.4667) +ATR, Atar Int'l, 7, mid, POINT(-13.0511704323315 20.4982706101565), Mauritania, Atar, POINT(-13.05 20.5167) +BAQ, Ernesto Cortissoz Int'l, 7, mid, POINT(-74.776555978265 10.8866775959414), Colombia, Barranquilla, POINT(-74.8019 10.9833) +BRC, Teniente Luis Candelaria Int'l, 7, mid, POINT(-71.1614300869763 -41.1459976958105), Argentina, San Carlos de Bariloche, POINT(-71.3 -41.15) +BYK, Bouaké, 7, mid, POINT(-5.06894222275311 7.73610495555032), Côte d'Ivoire, Bouaké, POINT(-5.0167 7.6833) +BZE, Philip S. W. Goldson Int'l, 7, major, POINT(-88.3082064033075 17.5360686575521), Belize, Belize City, POINT(-88.1886 17.4986) +CRP, Corpus Christi Int'l, 7, major, POINT(-97.5022678710298 27.7744560700823), United States, Corpus Christi, POINT(-97.3767 27.7254) +CUR, Hato Int'l, 7, mid, POINT(-68.9568788072761 12.1848346052019), Curaçao, Willemstad, POINT(-68.935 12.108) +CUZ, Velazco Astete Int'l, 7, major, POINT(-71.9436641449722 -13.5382186992639), Peru, Cusco, POINT(-71.9722 -13.525) +DAR, Julius Nyerere Int'l, 7, mid, POINT(39.2074715039165 -6.86672004249119), Tanzania, Dar es Salaam, POINT(39.2803 -6.8161) +DET, Detroit City, 7, mid, POINT(-83.0039681417733 42.4090938431907), United States, Detroit, POINT(-83.1024 42.3834) +DIL, Presidente Nicolau Lobato Int'l, 7, mid, POINT(125.524854209182 -8.54931157414564), Timor-Leste, Dili, POINT(125.5783 -8.5536) +DME, Moscow Domodedovo Int'l, 7, major, POINT(37.9002531289452 55.4141528223023), Russia, Balashikha, POINT(37.9667 55.8167) +DUD, Dunedin Int'l, 7, mid, POINT(170.200027 -45.923431), New Zealand, Mosgiel, POINT(170.3486 -45.875) +DZA, Dzaoudzi Pamanzi Int'l, 7, mid, POINT(45.2817864197899 -12.8049474381643), Mayotte, Mamoudzou, POINT(45.2272 -12.7794) +ELP, El Paso Int'l, 7, mid, POINT(-106.395714679366 31.7990860272589), Mexico, Juárez, POINT(-106.487 31.7386) +EVN, Zvartnots Int'l, 7, major, POINT(44.4000630536938 40.1523679451884), Armenia, Yerevan, POINT(44.5144 40.1814) +FTW, Fort Worth Meacham Field, 7, major, POINT(-97.3551348561587 32.8207529047972), United States, Fort Worth, POINT(-97.3474 32.7817) +GDT, JAGS McCartney Int'l, 7, mid, POINT(-71.1461337448876 21.4421237439063), Turks and Caicos Islands, Grand Turk, POINT(-71.136 21.4664) +GLS, Scholes Int'l, 7, mid, POINT(-94.8554013876264 29.2671239212096), United States, Galveston, POINT(-94.8913 29.2484) +GOM, Goma Int'l, 7, mid, POINT(29.2400534952228 -1.6583179500207), Congo (Kinshasa), Goma, POINT(29.2336 -1.6794) +GOU, Garoua Int'l, 7, mid, POINT(13.3724309377878 9.33068867678854), Cameroon, Garoua, POINT(13.4 9.3) +GUM, Antonio B. Won Pat Int'l, 7, major, POINT(144.805850357093 13.4926462359465), Guam, Hagåtña, POINT(144.7504 13.4745) +GYY, Gary/Chicago Int'l, 7, mid, POINT(-87.4083596247406 41.6177930015166), United States, Chicago, POINT(-87.6866 41.8375) +HAH, Prince Said Ibrahim Int'l, 7, mid, POINT(43.2745612179616 -11.5366393829127), Comoros, Moroni, POINT(43.256 -11.699) +HBA, Hobart Int'l, 7, mid, POINT(147.505996190408 -42.8376083694822), Australia, Kingston, POINT(147.3083 -42.9769) +HIR, Honiara Int'l, 7, mid, POINT(160.045855129925 -9.42757566400146), Solomon Islands, Honiara, POINT(159.9556 -9.4319) +IEV, Kiev Zhuliany Int'l, 7, mid, POINT(30.4451305182104 50.412808165985), Ukraine, Vyshneve, POINT(30.3581 50.3869) +IKT, Irkutsk S.E., 7, [mid,military], POINT(104.355859748002 52.2728893882244), Russia, Irkutsk, POINT(104.2833 52.2833) +IND, Indianapolis Int'l, 7, major, POINT(-86.2734003650885 39.7302043703969), United States, Indianapolis, POINT(-86.1458 39.7771) +INU, Nauru Int'l, 7, mid, POINT(166.91613965882 -0.545037226856384), Nauru, Yaren, POINT(166.9209 -0.5477) +IPC, Mataveri Int'l, 7, mid, POINT(-109.43006441001 -27.1587738388538), , , +JUJ, Gob. Horacio Guzman Int'l, 7, mid, POINT(-65.0937665458812 -24.3861010775846), Argentina, San Salvador de Jujuy, POINT(-65.3 -24.1833) +KHN, Nanchang Changbei Int'l, 7, mid, POINT(115.911979918602 28.8624891200666), China, Nanchang, POINT(115.8872 28.6842) +KMG, Kunming Wujiaba Int'l, 7, major, POINT(102.742117578823 24.999996110081), China, Kunming, POINT(102.7061 25.0433) +LBA, Leeds Bradford, 7, major, POINT(-1.65983106734746 53.8690819474434), United Kingdom, Bradford, POINT(-1.75 53.8) +LBV, Libreville Leon M'ba Int'l, 7, mid, POINT(9.41022337820712 0.457139229503759), Gabon, Libreville, POINT(9.4542 0.3903) +LFW, Lomé Tokoin, 7, mid, POINT(1.25093205640014 6.16687362722297), Togo, Lomé, POINT(1.2228 6.1319) +LWO, Lviv Danylo Halytskyi Int'l, 7, [mid,military], POINT(23.9461269598944 49.8178506050005), Ukraine, Lviv, POINT(24.0322 49.8425) +MAJ, Marshall Islands Int'l, 7, mid, POINT(171.281919370648 7.06811848557091), Marshall Islands, Majuro, POINT(171.3833 7.0833) +MFM, Macau Int'l, 7, major, POINT(113.57451294862 22.1576572529634), China, Zhuhai, POINT(113.5678 22.2769) +MGQ, Aden Adde Int'l, 7, mid, POINT(45.3036374186202 2.01635311214988), Somalia, Mogadishu, POINT(45.3419 2.0392) +MPM, Maputo Int'l, 7, mid, POINT(32.5741915194782 -25.924276711787), Mozambique, Maputo, POINT(32.5833 -25.9667) +MRU, Sir Seewoosagur Ramgoolam Int'l, 7, mid, POINT(57.6769860076636 -20.4317567793216), Mauritius, Curepipe, POINT(57.5263 -20.3188) +NAP, Naples Int'l, 7, major, POINT(14.2828444340203 40.8780728843639), Italy, Casoria, POINT(14.3 40.9) +NDB, Nouadhibou Int'l, 7, mid, POINT(-17.0334398691538 20.9290523064387), Mauritania, Nouadhibou, POINT(-17.0333 20.9333) +NGB, Ningbo Lishe Int'l, 7, major, POINT(121.461819388484 29.8208231906861), China, Ningbo, POINT(121.5492 29.875) +NKC, Nouakchott Int'l, 7, mid, POINT(-15.9519259252201 18.0979231718174), Mauritania, Nouakchott, POINT(-15.9785 18.0858) +NOU, La Tontouta Int'l, 7, mid, POINT(166.217232118699 -22.0136386248981), New Caledonia, Nouméa, POINT(166.458 -22.2758) +OAK, Oakland Int'l, 7, major, POINT(-122.213261257863 37.7123036951691), United States, San Leandro, POINT(-122.1599 37.7074) +ONT, Ontario Int'l, 7, major, POINT(-117.592327651651 34.060191102066), United States, Rancho Cucamonga, POINT(-117.5667 34.1247) +ORK, Cork, 7, major, POINT(-8.49014199983817 51.8485405419923), Ireland, Cork, POINT(-8.47 51.8972) +PDG, Minangkabau Int'l, 7, mid, POINT(100.285455851791 -0.786045714026273), Indonesia, Padang, POINT(100.3531 -0.95) +PDL, João Paulo II, 7, mid, POINT(-25.6969882198711 37.7433316472933), Portugal, Ponta Delgada, POINT(-25.67 37.74) +PEW, Bacha Khan Int'l, 7, mid, POINT(71.5188149912667 33.9914027889596), Pakistan, Peshawar, POINT(71.5675 34.0144) +PIK, Glasgow Prestwick, 7, mid, POINT(-4.61097163901068 55.5088918105142), United Kingdom, Prestwick, POINT(-4.6142 55.4956) +PMG, Ponta Porã Int'l, 7, mid, POINT(-55.7060793748573 -22.551786560876), Brazil, Ponta Porã, POINT(-55.7258 -22.5358) +PMR, Palmerston N. Int'l, 7, mid, POINT(175.62128328196 -40.3233178852055), New Zealand, Palmerston North, POINT(175.6117 -40.355) +PNI, Pohnpei Int'l, 7, mid, POINT(158.203304490964 6.98130676512123), Federated States of Micronesia, Kolonia, POINT(158.2081 6.9639) +PPT, Tahiti Faa'a Int'l, 7, mid, POINT(-149.609757932429 -17.5594577659942), French Polynesia, Papeete, POINT(-149.5667 -17.5334) +PSA, Pisa Galileo Galilei Int'l, 7, [major,military], POINT(10.4001343718056 43.6983224157664), Italy, Pisa, POINT(10.4 43.7167) +PZU, Port Sudan, 7, [mid,military], POINT(37.216065757542 19.5760636531968), Sudan, Port Sudan, POINT(37.2167 19.6167) +RAI, Praia Int'l, 7, mid, POINT(-23.4862019883587 14.9449889352832), Cabo Verde, Praia, POINT(-23.509 14.918) +RAK, Marrakech-Menara, 7, mid, POINT(-8.02460535907989 31.6022946597764), Morocco, Marrakech, POINT(-8.0089 31.63) +RAR, Rarotonga Int'l, 7, mid, POINT(-159.798156308387 -21.2009821724632), Cook Islands, Avarua, POINT(-159.771 -21.207) +REP, Siem Reap Int'l, 7, major, POINT(103.815780528112 13.4087969693538), Cambodia, Siem Reap, POINT(103.8597 13.3622) +RGA, Hermes Quijada Int'l, 7, mid, POINT(-67.7530268462675 -53.7814746058316), Chile, Puerto Williams, POINT(-67.6167 -54.9333) +RGL, Piloto Civil Norberto Fernandez Int'l, 7, mid, POINT(-69.3064711776731 -51.6116980855402), Argentina, Río Gallegos, POINT(-69.2161 -51.6233) +RNO, Reno-Tahoe Int'l, 7, major, POINT(-119.775283308105 39.5058499014703), United States, Reno, POINT(-119.8483 39.5497) +ROR, Roman Tmetuchl Int'l, 7, mid, POINT(134.532953466159 7.3644955361292), Palau, Koror, POINT(134.4792 7.3419) +SID, Amilcar Cabral Int'l, 7, mid, POINT(-22.9440574079648 16.7347932693385), Cabo Verde, Espargos, POINT(-22.946 16.756) +SJJ, Sarajevo, 7, major, POINT(18.3366185457127 43.8258872246797), Bosnia and Herzegovina, Sarajevo, POINT(18.4131 43.8564) +SKB, Robert L. Bradshaw Int'l, 7, mid, POINT(-62.7142125047316 17.311125840442), Saint Kitts and Nevis, Basseterre, POINT(-62.7333 17.3) +SLA, Martín Miguel de Güemes Int, 7, mid, POINT(-65.4784760437796 -24.8443742713315), Argentina, Salta, POINT(-65.4167 -24.7833) +SPN, Saipan Int'l, 7, mid, POINT(145.723694658638 15.1215167197664), Northern Mariana Islands, Capitol Hill, POINT(145.7546 15.2137) +SRE, Juana Azurduy de Padilla Int'l, 7, mid, POINT(-65.2928631387847 -19.0139157924657), Bolivia, Tarabuco, POINT(-64.9167 -19.1667) +SXM, Princess Juliana Int'l, 7, major, POINT(-63.1122760858602 18.042244021474), Sint Maarten, Philipsburg, POINT(-63.0458 18.0237) +TAI, Ta'izz Int'l, 7, mid, POINT(44.134782731062 13.6854970025574), Yemen, Ta‘izz, POINT(44.0219 13.5789) +TAO, Qingdao Liuting Int'l, 7, mid, POINT(120.380685949061 36.2677578081039), China, Qingdao, POINT(120.4 36.1167) +TKK, Chuuk Int'l, 7, mid, POINT(151.842046037403 7.45761780288443), Federated States of Micronesia, Weno, POINT(151.85 7.45) +TNG, Tangier Ibn Battouta, 7, mid, POINT(-5.91288087655914 35.7257656409274), Morocco, Tangier, POINT(-5.8039 35.7767) +TRW, Bonriki Int'l, 7, mid, POINT(173.145990795301 1.3806686975383), Kiribati, Tarawa, POINT(173.0176 1.3382) +TSE, Astana Int'l, 7, major, POINT(71.4609441399936 51.0269352907712), Kazakhstan, Nur-Sultan, POINT(71.4222 51.1472) +TSN, Tianjin Binhai Int'l, 7, major, POINT(117.352723159919 39.1294609909008), China, Tianjin, POINT(117.2056 39.1467) +TSV, Townsville, 7, [major,military], POINT(146.77067890477 -19.2561814376212), Australia, Townsville, POINT(146.8167 -19.25) +TUC, Teniente Gen. Benjamin Matienzo Int'l, 7, mid, POINT(-65.1081246236248 -26.8357310050714), Argentina, San Miguel de Tucumán, POINT(-65.2167 -26.8167) +TUN, Aeroport Tunis, 7, major, POINT(10.2176992447111 36.8474482177219), Tunisia, Tunis, POINT(10.1817 36.8064) +TUS, Tucson Int'l, 7, major, POINT(-110.937713232132 32.1203523441898), United States, Tucson, POINT(-110.8787 32.1541) +ULN, Chinggis Khaan Int'l, 7, mid, POINT(106.762873994929 47.8525260966684), Mongolia, Ulaanbaatar, POINT(106.9172 47.9203) +URC, Ürümqi Diwopu Int'l, 7, major, POINT(87.4671298487808 43.8983382193653), China, Ürümqi, POINT(87.6125 43.8225) +VLI, Bauerfield Int'l, 7, mid, POINT(168.319622739662 -17.7016990681781), Vanuatu, Port-Vila, POINT(168.3167 -17.7333) +WWK, Wewak Int'l, 7, mid, POINT(143.669102299698 -3.58022689444744), Papua New Guinea, Wewak, POINT(143.6333 -3.55) +XCR, Châlons Vatry, 7, [military,mid], POINT(4.19111982574289 48.7803946138566), France, Châlons-en-Champagne, POINT(4.365 48.9575) +XMN, Xiamen Gaoqi Int'l, 7, major, POINT(118.12696884672 24.537192570557), China, Xiamen, POINT(118.0819 24.4797) +YAP, Yap Int'l, 7, mid, POINT(138.086430283619 9.49791733361348), , , +ZLO, Playa de Oro Int'l, 7, mid, POINT(-104.560095200097 19.1480860285854), Mexico, Cihuatlán, POINT(-104.5667 19.25) +CAY, Cayenne – Rochambeau, 7, mid, POINT(-52.3638068572357 4.82126714308924), French Guiana, Cayenne, POINT(-52.33 4.933) +UIII, Irkutsk N.W., 7, mid, POINT(104.197359284494 52.3616476700131), Russia, Irkutsk, POINT(104.2833 52.2833) +SJW, Shijiazhuang Zhengding Int'l, 7, major, POINT(114.692266598902 38.278140913112), China, Shijiazhuang, POINT(114.5086 38.0422) +GYD, Heydar Aliyev Int'l, 7, major, POINT(50.0498394867405 40.462746883908), Azerbaijan, Baku, POINT(49.8352 40.3667) +LAK, Lakatamia Airbase, 7, [military,mid], POINT(33.322201334899 35.1063448067362), Cyprus, Latsia, POINT(33.3667 35.1) +CFB, Cabo Frio Int'l, 7, mid, POINT(-42.0792517520184 -22.9256317091328), Brazil, Cabo Frio, POINT(-42.0189 -22.8789) +HEM, Helsinki-Malmi, 7, mid, POINT(25.0455353698315 60.2493778499587), Finland, Helsinki, POINT(24.9375 60.1708) +LUX, Luxembourg-Findel, 7, major, POINT(6.21642121728731 49.6343040925102), Luxembourg, Luxembourg, POINT(6.1319 49.6117) +VCE, Venice Marco Polo, 7, major, POINT(12.3410673004369 45.5048477588455), Italy, Mestre, POINT(12.2381 45.4906) +YNY, Yangyang Int'l, 7, mid, POINT(128.66298866884 38.0587824162585), South Korea, Gangneung, POINT(128.9 37.75) +TBT, Tabatinga Int'l, 7, mid, POINT(-69.939473933909 -4.25032469493379), Colombia, Leticia, POINT(-69.9333 -4.2167) +BVB, Boa Vista Int'l, 7, mid, POINT(-60.6922206338682 2.84119534121157), Brazil, Boa Vista, POINT(-60.6714 2.8194) +LPA, Gran Canaria, 7, major, POINT(-15.3899245158461 27.9368899716574), Spain, Las Palmas, POINT(-15.4314 28.1272) +ING, Com. Armando Tola Int'l, 7, mid, POINT(-72.0538569101296 -50.2839008690038), Argentina, El Calafate, POINT(-72.2833 -50.3333) +NYO, Stockholm-Skavsta, 7, mid, POINT(16.9216055584254 58.7851041303448), Sweden, Nyköping, POINT(17.0086 58.7531) +MES, Polonia Int'l, 7, mid, POINT(98.6761925714641 3.56659179990894), Indonesia, Medan, POINT(98.6739 3.5894) +BGF, Bangui M'Poko Int'l, 7, mid, POINT(18.524123630208 4.39885153695957), Central African Republic, Bimbo, POINT(18.5163 4.3313) +HGH, Hangzhou Xiaoshan Int'l, 7, major, POINT(120.432097376313 30.2351862790414), China, Hangzhou, POINT(120.1675 30.25) +CXI, Cassidy Int'l, 7, mid, POINT(-157.34977789343 1.98616119792402), , , +SQQ, Šiauliai Int'l, 7, mid, POINT(23.3831885738691 55.90376945404), Lithuania, Šiauliai, POINT(23.3167 55.9333) +IUE, Niue Int'l, 7, mid, POINT(-169.926129774217 -19.0767129354511), Niue, Alofi, POINT(-169.921 -19.056) +AGT, Guaraní Int'l, 7, mid, POINT(-54.8393995296062 -25.4568570715812), Paraguay, Ciudad del Este, POINT(-54.6167 -25.5167) +AQP, Rodríguez Ballón Int'l, 7, mid, POINT(-71.5679335385285 -16.344552065352), Peru, Arequipa, POINT(-71.5333 -16.4) +VVO, Vladivostok Int'l, 7, [mid,military], POINT(132.139841720715 43.3776492533885), Russia, Vladivostok, POINT(131.9 43.1333) +PRN, Pristina, 7, major, POINT(21.0302690124746 42.5850331153448), Kosovo, Pristina, POINT(21.1622 42.6633) +ANR, Deurne, 6, mid, POINT(4.45092277399909 51.1891285063806), Belgium, Antwerp, POINT(4.4003 51.2178) +LAP, Gen. Márquez de León Int'l, 6, mid, POINT(-110.367197859809 24.0760903521803), Mexico, Los Mochis, POINT(-108.9937 25.7835) +HRB, Harbin Taiping, 6, major, POINT(126.236983030863 45.6206011723245), China, Harbin, POINT(126.6333 45.75) +TRV, Trivandrum Int'l, 6, mid, POINT(76.9189025612913 8.47650993894514), India, Thiruvananthapuram, POINT(76.9525 8.4875) +ADB, Adnan Menderes, 6, major, POINT(27.1492975952664 38.2912347645175), Turkey, İzmir, POINT(27.14 38.42) +NKG, Nanjing Lukou Int'l, 6, major, POINT(118.866102146906 31.7353249296177), China, Nanjing, POINT(118.7789 32.0608) +FPO, Freeport Int'l, 6, mid, POINT(-78.7039343114497 26.548246747189), The Bahamas, Freeport City, POINT(-78.6967 26.5286) +TIP, Tripoli Int'l, 6, major, POINT(13.1442589810713 32.6691695504993), Libya, Az Zāwīyah, POINT(12.7278 32.7522) +YQX, Gander Int'l, 6, mid, POINT(-54.5755719093578 48.9465980060736), Canada, Gander, POINT(-54.6089 48.9569) +DOH, Doha Int'l, 6, [major,military], POINT(51.5585487876547 25.2682461310506), Qatar, Doha, POINT(51.5333 25.2867) +ABQ, Albuquerque Int'l, 6, major, POINT(-106.6166851616 35.0491578018276), United States, Albuquerque, POINT(-106.6465 35.1054) +ANU, V.C. Bird Int'l, 6, mid, POINT(-61.7923676698358 17.1403599371617), Antigua and Barbuda, Saint John’s, POINT(-61.85 17.1167) +APW, Faleolo, 6, mid, POINT(-171.99732221834 -13.8325013323956), Samoa, Apia, POINT(-171.75 -13.8333) +ATZ, Asyut, 6, mid, POINT(31.0162490438011 27.0508158406978), Egypt, Asyūţ, POINT(31.1667 27.1833) +BAH, Bahrain Int'l, 6, major, POINT(50.6260028757534 26.2696971499497), Bahrain, Manama, POINT(50.5775 26.225) +BDL, Bradley Int'l, 6, major, POINT(-72.685394743339 41.9303160058352), United States, Windsor Locks, POINT(-72.6544 41.9267) +BGI, Grantley Adams Int'l, 6, mid, POINT(-59.4874188953158 13.079661104553), Martinique, Fort-de-France, POINT(-61.0667 14.6) +BJL, Yundum Int'l, 6, mid, POINT(-16.6523132698075 13.3438604788942), The Gambia, Serekunda, POINT(-16.6667 13.4333) +BJM, Bujumbura Int'l, 6, mid, POINT(29.3209840169939 -3.32204434913113), Burundi, Bujumbura, POINT(29.3667 -3.3833) +BLZ, Chileka Int'l, 6, mid, POINT(34.9719441837933 -15.6813844793272), Malawi, Blantyre, POINT(35.0058 -15.7861) +BME, Broome Int'l, 6, mid, POINT(122.233850515022 -17.952576129268), Australia, Broome, POINT(122.2361 -17.9619) +BND, Bandar Abbass Int'l, 6, mid, POINT(56.368886456411 27.2103258455145), Iran, Bandar ‘Abbās, POINT(56.2667 27.1833) +BSR, Basrah Int'l, 6, major, POINT(47.6683766633518 30.552799016106), Iraq, Al Başrah, POINT(47.81 30.515) +CJS, Ciudad Juarez Int'l, 6, mid, POINT(-106.435846631055 31.6357566201951), Mexico, Juárez, POINT(-106.487 31.7386) +CMB, Katunayake Int'l, 6, major, POINT(79.8852573421506 7.17807710544221), Sri Lanka, Negombo, POINT(79.8386 7.2111) +CNS, Cairns Int'l, 6, mid, POINT(145.7535848444 -16.8767421554062), Australia, Cairns, POINT(145.78 -16.92) +CNX, Chiang Mai Int'l, 6, major, POINT(98.9681181241593 18.7688473919675), Thailand, Chiang Mai, POINT(98.9986 18.7953) +COS, City of Colorado Springs, 6, major, POINT(-104.700880274111 38.7974248779125), United States, Colorado Springs, POINT(-104.7605 38.8674) +CPE, Ign. Alberto Ongay Int'l, 6, mid, POINT(-90.5036283734038 19.8142247992074), Mexico, Campeche, POINT(-90.5306 19.85) +CSX, Changsha Huanghua Int'l, 6, major, POINT(113.214054203252 28.1899218619451), China, Zhuzhou, POINT(113.1469 27.8407) +CVG, Greater Cincinnati Int'l, 6, major, POINT(-84.6561699153392 39.055418904783), United States, Cincinnati, POINT(-84.506 39.1413) +DAD, Da Nang, 6, major, POINT(108.202706257936 16.053144145167), Vietnam, Quảng Hà, POINT(108.2667 15.9333) +DAL, Dallas Love Field, 6, major, POINT(-96.84986377098 32.8444253732738), United States, Irving, POINT(-96.9702 32.8583) +DAM, Damascus Int'l, 6, major, POINT(36.5128954718126 33.4114366702732), Syria, Qabr as Sitt, POINT(36.3361 33.4472) +DAV, Enrique Malek Int'l, 6, mid, POINT(-82.4317583369387 8.39126106116917), Panama, David, POINT(-82.4333 8.4333) +DIR, Aba Tenna D. Yilma Int'l, 6, mid, POINT(41.857756722253 9.61267784753569), Ethiopia, Dire Dawa, POINT(41.8667 9.6) +DPS, Bali Int'l, 6, major, POINT(115.162322961107 -8.74475731595652), Indonesia, Denpasar, POINT(115.2167 -8.65) +DSM, Des Moines Int'l, 6, major, POINT(-93.6484612563736 41.5327904242113), United States, West Des Moines, POINT(-93.7806 41.5521) +EBB, Entebbe Int'l, 6, mid, POINT(32.4427573135214 0.044940949388672), Uganda, Kampala, POINT(32.5811 0.3136) +FKI, Kisangani Bangoka Int'l, 6, mid, POINT(25.3302714896212 0.492225136917501), Congo (Kinshasa), Kisangani, POINT(25.1911 0.5153) +FOC, Fuzhou Changle Int'l, 6, mid, POINT(119.668043820999 25.9318233148143), China, Fuzhou, POINT(119.2917 26.0769) +GAU, Lokpriya G. Bordoloi Int'l, 6, mid, POINT(91.588229058187 26.1052475924255), India, Guwāhāti, POINT(91.7458 26.1722) +GDN, Gdansk Lech Walesa, 6, major, POINT(18.4684422165911 54.3807025352925), Poland, Gdańsk, POINT(18.6453 54.3475) +GND, Point Salines Int'l, 6, mid, POINT(-61.7858529909285 12.0072683054283), Grenada, Saint David’s, POINT(-61.6806 12.0444) +GOJ, Nizhny Novgorod Int'l, 6, mid, POINT(43.7896337062935 56.2185525910656), Russia, Nizhniy Novgorod, POINT(44.0075 56.3269) +GYM, Gen. José M. Yáñez Int'l, 6, mid, POINT(-110.921651270402 27.9694553962829), Mexico, Heroica Guaymas, POINT(-110.8989 27.9183) +HET, Hohhot Baita Int'l, 6, mid, POINT(111.814681821626 40.8540600906552), China, Hohhot, POINT(111.6629 40.8151) +HLN, Helena Reg., 6, mid, POINT(-111.989896896008 46.6102043529), United States, Helena Valley Southeast, POINT(-111.8973 46.6219) +HMO, Gen. Ignacio P. Garcia Int'l, 6, mid, POINT(-111.051901711819 29.0900772523445), Mexico, Hermosillo, POINT(-110.9542 29.0989) +IAD, Dulles Int'l, 6, major, POINT(-77.4477925769206 38.952774037953), United States, Centreville, POINT(-77.4389 38.839) +ITO, Hilo Int'l, 6, mid, POINT(-155.039629733435 19.7147976868663), United States, Hilo, POINT(-155.0863 19.6883) +JAN, Jackson Int'l, 6, major, POINT(-90.0750986276924 32.3100600273635), United States, Pearl, POINT(-90.0918 32.273) +JAX, Jacksonville Int'l, 6, major, POINT(-81.6835767278311 30.491352730948), United States, Fruit Cove, POINT(-81.6175 30.0972) +KCH, Kuching Int'l, 6, mid, POINT(110.341837054315 1.4872079377901), Malaysia, Kuching, POINT(110.3439 1.5575) +KGL, Kigali Int'l, 6, mid, POINT(30.1348768187856 -1.96365443664138), Rwanda, Kigali, POINT(30.0606 -1.9536) +KRK, Kraków-Balice, 6, major, POINT(19.8009772844504 50.0722630648331), Poland, Kraków, POINT(19.9372 50.0614) +KUF, Kurumoch, 6, major, POINT(50.1472655210191 53.5083848190935), Russia, Samara, POINT(50.1408 53.2028) +KWL, Guilin Liangjiang Int'l, 6, major, POINT(110.04689349777 25.2176055252293), China, Guilin, POINT(110.2864 25.2819) +LAO, Laoag Int'l, 6, mid, POINT(120.533876196127 18.1824180866379), Philippines, Laoag, POINT(120.5936 18.1978) +LGA, LaGuardia, 6, major, POINT(-73.8719858204814 40.7745539398858), United States, New York, POINT(-73.9249 40.6943) +LGW, London Gatwick, 6, major, POINT(-0.162961639139456 51.1557567519275), United Kingdom, Crawley, POINT(-0.1872 51.1092) +LJU, Ljubljana, 6, major, POINT(14.4548126283266 46.2305445554486), Slovenia, Ljubljana, POINT(14.5061 46.0514) +LKO, Amausi Int'l, 6, mid, POINT(80.8841719732472 26.7639328700916), India, Lucknow, POINT(80.95 26.85) +LPG, La Plata, 6, mid, POINT(-57.895382063651 -34.9655441559234), Argentina, Berisso, POINT(-57.8858 -34.8728) +MAM, Gen. Sevando Canales, 6, mid, POINT(-97.5308217121187 25.7708412640619), Mexico, Heroica Matamoros, POINT(-97.5042 25.8797) +MAN, Manchester Int'l, 6, major, POINT(-2.27337159069427 53.3624896066518), United Kingdom, Wythenshawe, POINT(-2.264 53.392) +MCI, Kansas City Int'l, 6, major, POINT(-94.7159148579154 39.2978958263659), United States, Kansas City, POINT(-94.7443 39.1235) +MCT, Seeb Int'l, 6, major, POINT(58.2904804753493 23.5885704175856), Oman, Muscat, POINT(58.5922 23.6139) +MIR, Habib Bourguiba Int'l, 6, mid, POINT(10.753368185054 35.760710442178), Tunisia, Sousse, POINT(10.6333 35.8333) +MRS, Marseille Provence Airport, 6, major, POINT(5.22137917720337 43.4410600016468), France, Marseille, POINT(5.37 43.2964) +NLD, Quetzalcoatl Int'l, 6, mid, POINT(-99.5680081930063 27.4496896508316), Mexico, Nuevo Laredo, POINT(-99.5069 27.4861) +NNG, Nanning Wuwu Int'l, 6, major, POINT(108.168012273331 22.6120370541785), China, Nanning, POINT(108.315 22.8192) +OAX, Xoxocotlán Int'l, 6, mid, POINT(-96.7217959384975 17.0005592569745), Mexico, Oaxaca, POINT(-96.7253 17.0606) +OGG, Kahului, 6, mid, POINT(-156.437429581353 20.8932885151112), United States, Kahului, POINT(-156.4603 20.8715) +OKC, Will Rogers, 6, major, POINT(-97.5961177542092 35.3952774911744), United States, Oklahoma City, POINT(-97.5136 35.4676) +ORF, Norfolk Int'l, 6, major, POINT(-76.2044231712327 36.8982394673674), United States, Virginia Beach, POINT(-76.0435 36.7335) +PBI, Palm Beach Int'l, 6, major, POINT(-80.0901893383387 26.688441666433), United States, West Palm Beach, POINT(-80.132 26.7469) +PBM, Pengel Int'l, 6, mid, POINT(-55.1999113892902 5.45599967797439), Suriname, Paramaribo, POINT(-55.2039 5.8522) +PEE, Bolshesavino, 6, mid, POINT(56.0195602820297 57.9197711231691), Russia, Perm, POINT(56.2489 58.0139) +PEN, Penang Int'l, 6, mid, POINT(100.265786380955 5.29265627790489), Malaysia, George Town, POINT(100.3292 5.4144) +PHC, Port Harcourt Int'l, 6, mid, POINT(6.94989742723191 5.00700347673943), Nigeria, Port Harcourt, POINT(7.0336 4.8242) +PHE, Port Hedland Int'l, 6, mid, POINT(118.631797815615 -20.3781272960723), Australia, Port Hedland, POINT(118.6011 -20.31) +PIR, Pierre Regional, 6, mid, POINT(-100.292641981705 44.3801534668762), United States, Pierre, POINT(-100.3205 44.3748) +PIT, Greater Pittsburgh Int'l, 6, major, POINT(-80.2561290571918 40.4960518915285), United States, Pittsburgh, POINT(-79.9763 40.4397) +PPG, Pago Pago Int'l, 6, mid, POINT(-170.713307053734 -14.3290641850306), American Samoa, Pago Pago, POINT(-170.7046 -14.274) +BHX, Birmingham Int'l, 6, major, POINT(-1.73373170434452 52.4529085542838), United Kingdom, Solihull, POINT(-1.778 52.413) +ROB, Roberts Int'l, 6, mid, POINT(-10.3530851867934 6.24183456554525), Liberia, Harbel, POINT(-10.35 6.2833) +RPR, Raipur, 6, mid, POINT(81.7403775915201 21.1859868561447), India, Bhilai, POINT(81.38 21.21) +SAL, El Salvador Int'l, 6, mid, POINT(-89.0572035692743 13.4447481228616), El Salvador, Santa Tecla, POINT(-89.2406 13.6731) +SAN, San Diego Int'l, 6, major, POINT(-117.197511025731 32.7322645570132), Mexico, Tijuana, POINT(-117.0333 32.525) +SAT, San Antonio Int'l, 6, major, POINT(-98.4719699991559 29.5266203391315), United States, New Braunfels, POINT(-98.1148 29.6994) +SAV, Savannah Int'l, 6, major, POINT(-81.2099647750913 32.1356415522902), United States, Savannah, POINT(-81.1821 32.0286) +SCU, Antonio Maceo, 6, mid, POINT(-75.8398877639791 19.9724288717622), Cuba, Santiago de Cuba, POINT(-75.8294 20.0217) +SLP, Ponciano Arriaga Int'l, 6, mid, POINT(-100.936477816267 22.2557130495903), Mexico, San Luis Potosí, POINT(-100.9761 22.1511) +SMF, Sacramento Int'l, 6, major, POINT(-121.587894877723 38.6927238925554), United States, Elk Grove, POINT(-121.3842 38.4161) +STI, Cibao Int'l, 6, mid, POINT(-70.6941783224468 19.4659219152888), Dominican Republic, Gurabo al Medio, POINT(-70.6727 19.4739) +SVX, Koltsovo, 6, major, POINT(60.8058033432174 56.732245612046), Russia, Yekaterinburg, POINT(60.6128 56.8356) +SYR, Syracuse Hancock Int'l, 6, major, POINT(-76.1130789991049 43.1317844943741), United States, Cicero, POINT(-76.0662 43.1662) +TBZ, Tabriz, 6, mid, POINT(46.244713373574 38.1311107688175), Iran, Tabrīz, POINT(46.3006 38.0814) +TRC, Torreon Int'l, 6, mid, POINT(-103.398787828579 25.5632164399896), Mexico, Torreón, POINT(-103.4486 25.5394) +TUL, Tulsa Int'l, 6, major, POINT(-95.889882271542 36.190127565195), United States, Tulsa, POINT(-95.9042 36.1283) +TYS, Mcghee Tyson, 6, major, POINT(-83.9899378327585 35.8057448027088), United States, Knoxville, POINT(-83.9496 35.9692) +UFA, Ufa Int'l, 6, major, POINT(55.8840773411837 54.5651323578972), Russia, Ufa, POINT(55.9475 54.7261) +UVF, Hewanorra Int'l, 6, mid, POINT(-60.9499737723461 13.7365238050489), Saint Lucia, Vieux Fort, POINT(-60.954 13.728) +WDH, Windhoek Hosea Kutako Int'l, 6, mid, POINT(17.4632259028133 -22.4869531202041), Namibia, Windhoek, POINT(17.0836 -22.57) +YAM, Sault Ste Marie, 6, mid, POINT(-84.5006089999717 46.4854175101926), United States, Sault Ste. Marie, POINT(-84.3723 46.4817) +YDQ, Dawson Cr., 6, mid, POINT(-120.185595619101 55.7394117074557), Canada, Dawson Creek, POINT(-120.2356 55.7606) +YEG, Edmonton Int'l, 6, major, POINT(-113.584492564406 53.3072001619183), Canada, Leduc, POINT(-113.5492 53.2594) +YHZ, Halifax Int'l, 6, major, POINT(-63.5149652501673 44.886545450101), Canada, Moncton, POINT(-64.7714 46.1328) +YKA, Kamloops, 6, mid, POINT(-120.441734763962 50.7051955184591), Canada, Kamloops, POINT(-120.3408 50.6761) +YSB, Sudbury, 6, mid, POINT(-80.7957747817105 46.6227508204893), Canada, North Bay, POINT(-79.45 46.3) +YSJ, Saint John, 6, mid, POINT(-65.8905573681168 45.3292305955017), Canada, Saint John, POINT(-66.0761 45.2806) +YXS, Prince George, 6, mid, POINT(-122.674014743986 53.8842485751138), Canada, Prince George, POINT(-122.7494 53.9169) +YYJ, Victoria Int'l, 6, major, POINT(-123.430624539528 48.640529482179), Canada, Saanich, POINT(-123.381 48.484) +ZAM, Zamboanga Int'l, 6, mid, POINT(122.062432321637 6.9197577480583), Philippines, Zamboanga City, POINT(122.0761 6.9042) +ZGC, Lanzhou Zhongchuan, 6, mid, POINT(103.615415363043 36.5078842461237), China, Lanzhou, POINT(103.8318 36.0617) +ALB, Albany Int'l, 6, mid, POINT(-73.8093518843173 42.7456619801729), United States, Colonie, POINT(-73.7874 42.7396) +MKE, General Mitchell Int'l, 6, major, POINT(-87.9021056250744 42.9479198729586), United States, Milwaukee, POINT(-87.9675 43.0642) +ZHHH, Wang-Chia Tun Airbase, 6, [military,mid], POINT(114.24694737615 30.6017141196702), China, Wuhan, POINT(114.2881 30.5872) +SYX, Sanya Phoenix Int'l, 6, major, POINT(109.40823949108 18.3090959908593), China, Sanya, POINT(109.5036 18.2533) +LXA, Lhasa Gonggar, 6, mid, POINT(90.9005610194027 29.2936936123184), China, Lhasa, POINT(91.1719 29.6534) +HTN, Hotan, 6, mid, POINT(79.8723005212191 37.0400363509765), China, Hotan, POINT(80.0167 37.1) +DRS, Dresden, 6, major, POINT(13.7649671440047 51.1250912428871), Germany, Dresden, POINT(13.74 51.05) +NNA, Kenitra Air Base, 6, [military,major], POINT(-6.597753628116 34.2986673638223), Morocco, Kenitra, POINT(-6.5833 34.25) +QNJ, Annemasse, 6, mid, POINT(6.26491085364159 46.1957283286261), France, Annemasse, POINT(6.2364 46.1958) +NOG, Nogales Int'l, 6, mid, POINT(-110.972721301675 31.2255371741159), Mexico, Heroica Nogales, POINT(-110.9458 31.3186) +SXB, Strasbourg, 6, mid, POINT(7.62784196688924 48.5446961721759), France, Strasbourg, POINT(7.7458 48.5833) +CGN, Cologne/Bonn, 6, major, POINT(7.12235975524539 50.8782596629471), Germany, Cologne, POINT(6.9528 50.9364) +PUS, Kimhae Int'l, 6, major, POINT(128.948801379039 35.1702840636829), South Korea, Busan, POINT(129.075 35.18) +CJU, Jeju Int'l, 6, major, POINT(126.491629401972 33.5247173150399), South Korea, Jeju, POINT(126.5219 33.5097) +SVG, Stavanger Sola, 6, major, POINT(5.6298103297218 58.8821564842185), Norway, Sandnes, POINT(5.7361 58.8517) +TRD, Trondheim Vaernes, 6, major, POINT(10.9168095241445 63.472029381717), Norway, Stjørdalshalsen, POINT(10.9189 63.4712) +CMG, Corumbá Int'l, 6, mid, POINT(-57.6636078925543 -19.0141662885534), Brazil, Corumbá, POINT(-57.6528 -19.0089) +FNC, Madeira, 6, mid, POINT(-16.7756374531213 32.6933642847489), Portugal, Machico, POINT(-16.7667 32.7) +IGU, Foz do Iguaçu Int'l, 6, mid, POINT(-54.4885922735633 -25.5976832162102), Brazil, Foz do Iguaçu, POINT(-54.5875 -25.54) +PVH, Gov. Jorge Teixeira de Oliveira Int'l, 6, mid, POINT(-63.8984625004213 -8.71442482859288), Brazil, Porto Velho, POINT(-63.9039 -8.7619) +BIO, Bilbao, 6, mid, POINT(-2.90609011679805 43.3050829811195), Spain, Bilbao, POINT(-2.9236 43.2569) +PMI, Palma de Mallorca, 6, major, POINT(2.72997660200647 39.5657758586254), Spain, Marratxi, POINT(2.7527 39.6421) +TFN, Tenerife N., 6, major, POINT(-16.3463175679264 28.4875770267731), Spain, La Laguna, POINT(-16.3167 28.4853) +GOT, Gothenburg, 6, major, POINT(12.2938269092573 57.6857493534879), Sweden, Gothenburg, POINT(11.9675 57.7075) +LLA, Lulea, 6, major, POINT(22.1230271243945 65.5490362477616), Sweden, Luleå, POINT(22.1539 65.5844) +AUH, Abu Dhabi Int'l, 6, major, POINT(54.6463293225558 24.4272271529764), United Arab Emirates, Abu Dhabi, POINT(54.3667 24.4667) +CZL, Mohamed Boudiaf Int'l, 6, mid, POINT(6.62194665181219 36.2834409441601), Algeria, Constantine, POINT(6.6147 36.365) +ASW, Aswan Int'l, 6, mid, POINT(32.8244372462973 23.9682765441778), Egypt, Aswān, POINT(32.8997 24.0889) +RVN, Rovaniemi, 6, mid, POINT(25.8294409760452 66.5595564168509), Finland, Rovaniemi, POINT(25.7333 66.5) +GEO, Cheddi Jagan Int'l, 6, mid, POINT(-58.2541191925889 6.49855290813572), Guyana, Bartica, POINT(-58.6167 6.4) +COK, Cochin Int'l, 6, major, POINT(76.3905198502024 10.1551187628118), India, Kochi, POINT(76.28 9.97) +EDL, Eldoret Int'l, 6, mid, POINT(35.2236930658301 0.40507147546036), Kenya, Eldoret, POINT(35.2833 0.5167) +ICN, Incheon Int'l, 6, major, POINT(126.450875980796 37.4492088624346), South Korea, Incheon, POINT(126.6333 37.4833) +CUL, Federal de Bachigualato Int'l, 6, mid, POINT(-107.469863792896 24.7668040390461), Mexico, Culiacán, POINT(-107.3939 24.8069) +ISB, Benazir Bhutto Int'l, 6, [major,military], POINT(73.1007936471882 33.6074457507526), Pakistan, Rawalpindi, POINT(73.0333 33.6) +BRU, Brussels, 5, major, POINT(4.48464032408272 50.8972949641511), Belgium, Brussels, POINT(4.3525 50.8467) +ABV, Abuja Int'l, 5, major, POINT(7.27025993974356 9.00437659781094), Nigeria, Abuja, POINT(7.4833 9.0667) +ACV, Arcata-Eureka, 5, mid, POINT(-124.107065520139 40.9719245381314), United States, McKinleyville, POINT(-124.0857 40.9488) +AUS, Austin-Bergstrom Int'l, 5, major, POINT(-97.6668367646054 30.2021081920749), United States, Round Rock, POINT(-97.6642 30.527) +AYT, Antalya, 5, major, POINT(30.8025526439415 36.9153233051868), Turkey, Antalya, POINT(30.7075 36.8874) +BFS, Belfast Int'l, 5, major, POINT(-6.21616943734958 54.6615575470103), United Kingdom, Belfast, POINT(-5.93 54.5964) +BGY, Orio Al Serio, 5, major, POINT(9.6989176939974 45.6654980560695), Italy, Bergamo, POINT(9.67 45.695) +BKI, Kota Kinabalu Int'l, 5, mid, POINT(116.051087873369 5.92289445474807), Malaysia, Kota Kinabalu, POINT(116.0725 5.975) +BLR, Bengaluru Int'l, 5, major, POINT(77.7095579889575 13.2006108069609), India, Bangalore, POINT(77.5917 12.9789) +CBR, Canberra Int'l, 5, major, POINT(149.190760539671 -35.3071855902909), Australia, Canberra, POINT(149.1269 -35.2931) +CMH, Port Columbus Int'l, 5, major, POINT(-82.8840306426634 39.9981181922432), United States, Gahanna, POINT(-82.8637 40.0251) +CMN, Mohamed V Int'l, 5, major, POINT(-7.5814559902572 33.3747274815396), Morocco, Mediouna, POINT(-7.51 33.45) +DUS, Düsseldorf Int'l, 5, major, POINT(6.76494446612174 51.2781820420774), Germany, Düsseldorf, POINT(6.7833 51.2333) +ESB, Esenboğa Int'l, 5, major, POINT(32.9930100772014 40.1151278273234), Turkey, Ankara, POINT(32.85 39.93) +HLZ, Hamilton Int'l, 5, mid, POINT(175.336221432708 -37.8658411484827), New Zealand, Te Awamutu, POINT(175.3167 -38.0167) +HYD, Rajiv Gandhi Int'l, 5, major, POINT(78.42953613452 17.2359831507471), India, Hyderābād, POINT(78.4867 17.385) +JFK, John F Kennedy Int'l, 5, major, POINT(-73.7863268609295 40.6459595584081), United States, New York, POINT(-73.9249 40.6943) +KBP, Boryspil Int'l, 5, major, POINT(30.8951621615528 50.340902338877), Ukraine, Boryspil, POINT(30.95 50.35) +KRT, Khartoum, 5, major, POINT(32.550153296633 15.5922226530858), Sudan, Khartoum, POINT(32.56 15.5006) +MSN, Dane Cty. Reg. (Truax Field), 5, major, POINT(-89.3457847894487 43.1363082385868), United States, Sun Prairie, POINT(-89.2362 43.1825) +MSQ, Minsk Int'l, 5, major, POINT(28.0341933346378 53.8893792398005), Belarus, Minsk, POINT(27.5667 53.9) +PMO, Palermo, 5, major, POINT(13.1055309888638 38.1863351084895), Italy, Palermo, POINT(13.3613 38.1157) +PVD, T.F. Green, 5, mid, POINT(-71.4357841445789 41.7260019847189), United States, Providence, POINT(-71.4187 41.823) +RSW, Southwest Florida Int'l, 5, major, POINT(-81.7551231409306 26.5279288067651), United States, Cape Coral, POINT(-81.9957 26.6443) +SHE, Shenyang Taoxian Int'l, 5, major, POINT(123.487974430338 41.6347891339582), China, Shenyang, POINT(123.4281 41.8025) +SHJ, Sharjah Int'l, 5, major, POINT(55.5205071948853 25.3211964019068), United Arab Emirates, Dubai, POINT(55.2972 25.2631) +SJC, San Jose Int'l, 5, major, POINT(-121.929428983532 37.3694905908965), United States, Sunnyvale, POINT(-122.0255 37.3836) +SNA, John Wayne, 5, major, POINT(-117.861489220393 33.6794857329549), United States, Mission Viejo, POINT(-117.6551 33.6096) +STR, Stuttgart, 5, major, POINT(9.19395108945536 48.6901051358913), Germany, Stuttgart, POINT(9.18 48.7775) +SYQ, Nacional Tobías Bolaños, 5, mid, POINT(-84.1386091971594 9.95827851919623), Costa Rica, La Uruca, POINT(-84.1327 9.9575) +SZX, Shenzhen Bao'an Int'l, 5, major, POINT(113.815852751085 22.6465077147868), China, Shenzhen, POINT(114.054 22.535) +SDF, Louisville Int'l, 5, major, POINT(-85.7417027597367 38.1860207152699), United States, Jeffersonville, POINT(-85.7026 38.3376) +GVA, Geneva, 5, major, POINT(6.10794577423603 46.231009510158), Switzerland, Le Grand-Saconnex, POINT(6.1167 46.2333) +LYS, Lyon-Saint Exupery, 5, mid, POINT(5.07594431813459 45.7210186834669), France, Lyon, POINT(4.84 45.76) +KIX, Kansai Int'l, 5, major, POINT(135.244459772476 34.4347941629269), Japan, Ōsaka, POINT(135.5022 34.6939) +LIS, Lisbon Portela, 5, major, POINT(-9.13069440931071 38.7707623427514), Portugal, Loures, POINT(-9.1667 38.8333) +CNF, Tancredo Neves Int'l, 5, major, POINT(-43.9635815209949 -19.6327821218747), Brazil, Belo Horizonte, POINT(-43.9333 -19.9167) +BMA, Bromma, 5, mid, POINT(17.9456175406145 59.3555902065112), Sweden, Stockholm, POINT(18.0686 59.3294) +SUB, Juanda Int'l, 5, major, POINT(112.777034594933 -7.383578985276), Indonesia, Surabaya, POINT(112.7378 -7.2458) +MDQ, Astor Piazzolla Int'l, 5, mid, POINT(-57.5816150932392 -37.9332161204482), Argentina, Mar del Plata, POINT(-57.55 -38.0) +GCM, Owen Roberts Int'l, 5, major, POINT(-81.3576706162289 19.2959107437122), , , +CGO, Zhengzhou Xinzheng Int'l, 5, major, POINT(113.841831302845 34.5263027198957), China, Zhengzhou, POINT(113.6605 34.7492) +DLC, Dalian Zhoushuizi Int'l, 5, major, POINT(121.538913780101 38.9615702300222), China, Dalian, POINT(121.6 38.9) +HER, Heraklion Int'l, 5, major, POINT(25.1740558243272 35.3369024101045), Greece, Néa Alikarnassós, POINT(25.1833 35.3167) +TBS, Tbilisi Int'l, 5, major, POINT(44.9646146141664 41.6694420187261), Georgia, Tbilisi, POINT(44.7925 41.7225) +XXC, Cascais, 5, mid, POINT(-9.35458240263928 38.7235353208323), Portugal, Sintra, POINT(-9.3883 38.7992) +KHH, Kaohsiung Int'l, 4, major, POINT(120.345156342151 22.5717061054422), Taiwan, Kaohsiung, POINT(120.2975 22.615) +SKO, Sadiq Abubakar III, 4, mid, POINT(5.20022616032651 12.9174824166181), Nigeria, Sokoto, POINT(5.2339 13.0622) +UIO, Mariscal Sucre Int'l, 4, mid, POINT(-78.4899925545701 -0.145552408466882),Ecuador, Quito, POINT(-78.5125 -0.22) +KHI, Karachi Civil, 4, mid, POINT(67.1521283592947 24.8985243689595), Pakistan, Karachi, POINT(67.01 24.86) +KIV, Kishinev S.E., 4, mid, POINT(28.9360487562255 46.9341619900391), Moldova, Chisinau, POINT(28.8353 47.0228) +LIM, Jorge Chávez, 4, major, POINT(-77.1075656931342 -12.0237161502221), Peru, Callao, POINT(-77.1333 -12.0333) +YQT, Thunder Bay Int'l, 4, mid, POINT(-89.3121421238136 48.3718811492508), Canada, Thunder Bay, POINT(-89.2461 48.3822) +VNO, Vilnius, 4, major, POINT(25.2807164497285 54.6430549307542), Lithuania, Vilnius, POINT(25.28 54.6872) +XIY, Hsien Yang, 4, major, POINT(108.755811342151 34.4429391054422), China, Xi’an, POINT(108.9 34.2667) +NTR, Del Norte Int'l, 4, mid, POINT(-100.238394186577 25.859873767729), Mexico, Ciudad Apodaca, POINT(-100.1886 25.7817) +TBU, Fua'amotu Int'l, 4, mid, POINT(-175.135635 -21.24861), Tonga, Nuku‘alofa, POINT(-175.2 -21.1333) +IFN, Esfahan Int'l, 4, mid, POINT(51.8763916812681 32.7460805344321), Iran, Eşfahān, POINT(51.6675 32.6447) +HRE, Harare Int'l, 4, mid, POINT(31.1014 -17.9228), Zimbabwe, Harare, POINT(31.0522 -17.8292) +KWI, Kuwait Int'l, 4, major, POINT(47.9714825593316 29.2396800581583), Kuwait, Kuwait City, POINT(47.9783 29.3697) +YOW, Macdonald-Cartier Int'l, 4, major, POINT(-75.6648933870205 45.3201348196531), Canada, Gatineau, POINT(-75.65 45.4833) +KBL, Kabul Int'l, 4, mid, POINT(69.2100736270874 34.5633978864149), Afghanistan, Kabul, POINT(69.1783 34.5253) +ABJ, Abidjan Port Bouet, 4, mid, POINT(-3.93221929167636 5.2543984451492), Côte d'Ivoire, Abidjan, POINT(-4.0333 5.3167) +ACA, General Juan N Alvarez Int'l, 4, major, POINT(-99.7545085619681 16.76196735278), Mexico, Acapulco de Juárez, POINT(-99.8825 16.8636) +ACC, Kotoka Int'l, 4, major, POINT(-0.171402855660817 5.60698152381193), Ghana, Accra, POINT(-0.2 5.55) +ADD, Bole Int'l, 4, mid, POINT(38.7931904366343 8.98173027581099), Ethiopia, Addis Ababa, POINT(38.74 9.03) +ADE, Aden Int'l, 4, mid, POINT(45.030602 12.8278), Yemen, Aden, POINT(45.0333 12.8) +ADL, Adelaide Int'l, 4, mid, POINT(138.532101457699 -34.9405860275154), Australia, Adelaide, POINT(138.6 -34.9275) +ALA, Almaty Int'l, 4, major, POINT(77.0120458771175 43.3464943144793), Kazakhstan, Almaty, POINT(76.8958 43.2775) +ALG, Houari Boumediene, 4, major, POINT(3.21207353516506 36.6997206663535), Algeria, Algiers, POINT(3.0589 36.7539) +ALP, Aleppo Int'l, 4, major, POINT(37.2273414057828 36.1846237314314), Syria, Aleppo, POINT(37.16 36.2) +AMD, Sardar Vallabhbhai Patel Int'l, 4, mid, POINT(72.6209000884332 23.0707454635881), India, Ahmedabad, POINT(72.58 23.03) +ANF, Cerro Moreno Int'l, 4, mid, POINT(-70.4409908509407 -23.4489545248317), Chile, Antofagasta, POINT(-70.4 -23.65) +ASB, Ashkhabad Northwest, 4, mid, POINT(58.3639659208246 37.984853438957), Turkmenistan, Ashgabat, POINT(58.3833 37.95) +ASM, Yohannes Iv Int'l, 4, mid, POINT(38.9063540136321 15.2936159696499), Eritrea, Asmara, POINT(38.925 15.3228) +ASU, Silvio Pettirossi Int'l, 4, mid, POINT(-57.5139078247136 -25.2416592533816), Paraguay, Luque, POINT(-57.4872 -25.27) +BDA, Bermuda Int'l, 4, mid, POINT(-64.7027740686514 32.3591739601581), , , +BEG, Surcin, 4, major, POINT(20.2912845946621 44.8190766654433), Serbia, Surčin, POINT(20.2833 44.8) +BEY, Beirut Int'l, 4, major, POINT(35.4930853618161 33.8254400618668), Lebanon, Beirut, POINT(35.5131 33.8869) +BHO, Bairagarh, 4, mid, POINT(77.3408714713579 23.2855684869809), India, Bhopāl, POINT(77.4167 23.25) +BKO, Bamako Sénou, 4, mid, POINT(-7.94727226970801 12.5393363425867), Mali, Bamako, POINT(-7.9922 12.6458) +BNA, Nashville Int'l, 4, major, POINT(-86.6692867356375 36.1314876361697), United States, Nashville, POINT(-86.7842 36.1715) +BNE, Brisbane Int'l, 4, major, POINT(153.120256418844 -27.3853965939099), Australia, Brisbane, POINT(153.0281 -27.4678) +BOI, Boise Air Terminal, 4, major, POINT(-116.221841070549 43.5689592234704), United States, Boise, POINT(-116.2308 43.6005) +BRW, Wiley Post Will Rogers Mem., 4, mid, POINT(-156.771835 71.289299), , , +BUF, Greater Buffalo Int'l, 4, major, POINT(-78.7319965523308 42.9340337493526), United States, Cheektowaga, POINT(-78.7466 42.9082) +BUQ, Bulawayo, 4, mid, POINT(28.622552042904 -20.0155684094908), Zimbabwe, Bulawayo, POINT(28.58 -20.17) +BWN, Brunei Int'l, 4, major, POINT(114.933119029209 4.94547528227685), Brunei, Bandar Seri Begawan, POINT(114.9422 4.8903) +CAN, Guangzhou Baiyun Int'l, 4, major, POINT(113.297516552171 23.3891511573243), China, Guangzhou, POINT(113.26 23.13) +CCP, Carriel Sur Int'l, 4, mid, POINT(-73.0621061746214 -36.7763727437881), Chile, Talcahuano, POINT(-73.1219 -36.7167) +CCU, Netaji Subhash Chandra Bose Int'l, 4, major, POINT(88.4400010130197 22.6453893785064), India, Kolkāta, POINT(88.37 22.5675) +CGP, Chittagong, 4, mid, POINT(91.8147107162383 22.2455658585738), Bangladesh, Chattogram, POINT(91.8325 22.335) +CHC, Christchurch Int'l, 4, major, POINT(172.538675565223 -43.4885486784104), New Zealand, Rolleston, POINT(172.3833 -43.5833) +CKY, Conakry, 4, mid, POINT(-13.6210656251671 9.57418115850082), Guinea, Conakry, POINT(-13.7122 9.5092) +CLE, Hopkins Int'l, 4, major, POINT(-81.8384406064046 41.4111916124966), United States, Akron, POINT(-81.5219 41.0798) +CLO, Alfonso Bonilla Aragón Int'l, 4, mid, POINT(-76.3850714728091 3.54328635123219), Colombia, Cali, POINT(-76.5222 3.4206) +COO, Cotonou Cadjehon, 4, mid, POINT(2.3838000724352 6.3582465034691), Benin, Cotonou, POINT(2.4333 6.3667) +COR, Ingeniero Ambrosio L.V. Taravella Int'l, 4, mid, POINT(-64.2123157670801 -31.3156811684889), Argentina, Villa Allende, POINT(-64.3 -31.3) +CTG, Rafael Nunez, 4, mid, POINT(-75.5123349559682 10.4449381764915), Colombia, Turbaco, POINT(-75.3333 10.35) +CUN, Cancún, 4, major, POINT(-86.8744172506694 21.04019667144), Mexico, Cancún, POINT(-86.8475 21.1606) +CUU, General R F Villalobos Int'l, 4, mid, POINT(-105.969204692629 28.7039984997679), Mexico, Chihuahua, POINT(-106.0889 28.6353) +DAC, Zia Int'l Dhaka, 4, mid, POINT(90.4049241599237 23.8481243218127), Bangladesh, Dhaka, POINT(90.3889 23.7639) +DRW, Darwin Int'l, 4, [major,military], POINT(130.877501436774 -12.4080559966556), Australia, Darwin, POINT(130.8411 -12.4381) +DUR, Louis Botha, 4, mid, POINT(30.9457081940881 -29.965914250828), South Africa, Durban, POINT(31.05 -29.8833) +FBM, Lubumbashi Luano Int'l, 4, mid, POINT(27.5292 -11.5908), Congo (Kinshasa), Lubumbashi, POINT(27.4794 -11.6647) +FEZ, Saiss, 4, mid, POINT(-4.98214637678303 33.9305251844673), Morocco, Fès, POINT(-5.0033 34.0433) +FIH, Kinshasa N Djili Int'l, 4, mid, POINT(15.4465162074561 -4.38916882197582), Congo (Kinshasa), Kinshasa, POINT(15.3222 -4.325) +FNA, Freetown Lungi, 4, mid, POINT(-13.2002296786483 8.61542361726369), Sierra Leone, Port Loko, POINT(-12.7875 8.7667) +FNJ, Sunan, 4, mid, POINT(125.675321571201 39.2001771667656), North Korea, Pyongyang, POINT(125.7381 39.0194) +FRU, Vasilyevka, 4, major, POINT(74.468800339909 43.0554527233303), Kyrgyzstan, Bishkek, POINT(74.6122 42.8747) +GBE, Sir Seretse Khama Int'l, 4, mid, POINT(25.9243808264147 -24.5580718089441), Botswana, Gaborone, POINT(25.9122 -24.6581) +GDL, Don Miguel Hidalgo Int'l, 4, major, POINT(-103.300766222752 20.5246863485173), Mexico, Tlaquepaque, POINT(-103.3167 20.6167) +GLA, Glasgow Int'l, 4, major, POINT(-4.43167796995107 55.8641828570355), United Kingdom, Paisley, POINT(-4.4239 55.8456) +GUA, La Aurora, 4, mid, POINT(-90.530181111378 14.5881608290051), Guatemala, Guatemala City, POINT(-90.5252 14.6099) +GYE, Simon Bolivar Int'l, 4, mid, POINT(-79.887009643933 -2.15833790699136), Ecuador, Guayaquil, POINT(-79.8875 -2.19) +HAN, Noi Bai, 4, major, POINT(105.803759436806 21.2145596707245), Vietnam, Hanoi, POINT(105.8542 21.0283) +HAV, José Martí Int'l, 4, major, POINT(-82.4074206289499 22.9973533364428), Cuba, Havana, POINT(-82.3589 23.1367) +HBE, Borg El Arab Int'l, 4, mid, POINT(29.69266601523 30.9183712786239), Egypt, Al ‘Ajamī, POINT(29.7604 31.0959) +JED, King Abdul Aziz Int'l, 4, major, POINT(39.1504996780448 21.6706857878128), Saudi Arabia, Jeddah, POINT(39.1728 21.5433) +KAN, Kano Mallam Aminu Int'l, 4, mid, POINT(8.52213718395767 12.0457071601746), Nigeria, Kano, POINT(8.5167 12.0) +KHG, Kashi, 4, mid, POINT(76.0130148060075 39.5379686306258), China, Kashgar, POINT(75.9938 39.4681) +KIN, Norman Manley Int'l, 4, major, POINT(-76.7786897616576 17.9375751552752), Jamaica, Portmore, POINT(-76.8799 17.95) +KTM, Tribhuvan Int'l, 4, mid, POINT(85.357139531668 27.7002816751609), Nepal, Kathmandu, POINT(85.324 27.7172) +LAD, Luanda 4 de Fevereiro, 4, mid, POINT(13.2347957502699 -8.84831327917379), Angola, Luanda, POINT(13.2344 -8.8383) +LED, Pulkovo 2, 4, major, POINT(30.3070976454648 59.8054061601897), Russia, Saint Petersburg, POINT(30.3167 59.95) +LHE, Allama Iqbal Int'l, 4, mid, POINT(74.4108810181748 31.5206296518206), Pakistan, Lahore, POINT(74.3436 31.5497) +LLW, Kamuzu Int'l, 4, mid, POINT(33.7827885019788 -13.788622823746), Malawi, Lilongwe, POINT(33.7833 -13.9833) +LOS, Lagos Murtala Muhammed, 4, major, POINT(3.32112435281334 6.57825944540467), Nigeria, Ikeja, POINT(3.3426 6.6186) +LPB, El Alto Int'l, 4, mid, POINT(-68.1780055277945 -16.5098792213977), Bolivia, El Alto, POINT(-68.1633 -16.5047) +LUN, Lusaka Int'l, 4, mid, POINT(28.4455443211019 -15.3268522509447), Zambia, Lusaka, POINT(28.2833 -15.4167) +LXR, Luxor, 4, mid, POINT(32.7032970848623 25.6730347786023), Egypt, Luxor, POINT(32.65 25.6833) +MAA, Chennai Int'l, 4, major, POINT(80.1637759731545 12.9825301669154), India, Chennai, POINT(80.275 13.0825) +MAR, La Chinita Int'l, 4, mid, POINT(-71.7237688094687 10.5557594684972), Venezuela, Maracaibo, POINT(-71.6333 10.6333) +MDE, José María Córdova, 4, mid, POINT(-75.4269557399772 6.171001614358), Colombia, Medellín, POINT(-75.5906 6.2308) +MEM, Memphis Int'l, 4, major, POINT(-89.9816280353237 35.0444101240089), United States, Southaven, POINT(-89.9786 34.9514) +MGA, Augusto Cesar Sandino Int'l, 4, mid, POINT(-86.1712846229543 12.144635873435), Nicaragua, Managua, POINT(-86.2738 12.1544) +MHD, Mashhad, 4, major, POINT(59.6421943574029 36.2275503134984), Iran, Mashhad, POINT(59.6 36.3) +MIA, Miami Int'l, 4, major, POINT(-80.2789718277441 25.7949407212406), United States, Hialeah, POINT(-80.3045 25.8696) +MID, Lic M Crecencio Rejon Int'l, 4, mid, POINT(-89.6630235736434 20.9338603864296), Mexico, Kanasín, POINT(-89.5578 20.9344) +MLA, Luqa, 4, major, POINT(14.4952644555055 35.8489307943501), Malta, Valletta, POINT(14.5125 35.8983) +MBA, Moi Int'l, 4, major, POINT(39.6026631870383 -4.03265262579657), Kenya, Mombasa, POINT(39.6667 -4.05) +MSU, Moshoeshoe I Int'l, 4, mid, POINT(27.5592160333614 -29.4555740046101), Lesotho, Maseru, POINT(27.48 -29.31) +MSY, New Orleans Int'l, 4, major, POINT(-90.2566939480594 29.9851141460622), United States, New Orleans, POINT(-89.9288 30.0687) +MUX, Multan, 4, [major,military], POINT(71.418995432932 30.1950780904965), Pakistan, Multan, POINT(71.4697 30.1978) +MVD, Carrasco Int'l, 4, major, POINT(-56.026636146282 -34.8410485988569), Uruguay, Montevideo, POINT(-56.1819 -34.8836) +MZT, General Rafael Buelna Int'l, 4, mid, POINT(-106.270016617885 23.1665960971344), Mexico, Mazatlán, POINT(-106.4167 23.2167) +NAS, Nassau Int'l, 4, major, POINT(-77.4648472290944 25.0486910600866), The Bahamas, Nassau, POINT(-77.3386 25.0781) +NDJ, Ndjamena, 4, mid, POINT(15.0330446385559 12.1295400184115), Chad, N’Djamena, POINT(15.05 12.11) +NIM, Niamey, 4, mid, POINT(2.17730671184125 13.4767572807942), Niger, Niamey, POINT(2.1175 13.515) +CEB, Mactan-Cebu Int'l, 4, major, POINT(123.979134508664 10.3158756727292), Philippines, Lapu-Lapu City, POINT(123.9488 10.3127) +NOV, Nova Lisboa, 4, mid, POINT(15.7497618459595 -12.8025414575915), Angola, Huambo, POINT(15.7347 -12.7767) +OMA, Eppley Airfield, 4, mid, POINT(-95.8994157953121 41.2997111453012), United States, Omaha, POINT(-96.0529 41.2627) +OME, Nome, 4, mid, POINT(-165.441641712281 64.5072207026631), , , +OUA, Ouagadougou, 4, mid, POINT(-1.51380536165114 12.3535800260473), Burkina Faso, Ouagadougou, POINT(-1.5275 12.3686) +PAP, Mais Gate Int'l, 4, mid, POINT(-72.2944780260473 18.5756829054286), Haiti, Port-au-Prince, POINT(-72.3333 18.5333) +PBC, Puebla, 4, mid, POINT(-98.375759790423 19.163793546584), Mexico, Puebla, POINT(-98.1833 19.0333) +PDX, Portland Int'l, 4, major, POINT(-122.592738881254 45.5889569315305), United States, Vancouver, POINT(-122.5967 45.6366) +PER, Perth Int'l, 4, major, POINT(115.974224942233 -31.9411297945783), Australia, Kwinana, POINT(115.7702 -32.2394) +PLZ, H F Verwoerd, 4, mid, POINT(25.6117777567602 -33.9840877431374), South Africa, Port Elizabeth, POINT(25.6 -33.9581) +PMC, El Tepual Int'l, 4, mid, POINT(-73.0983841336424 -41.4333820702269), Chile, Puerto Montt, POINT(-72.9333 -41.4667) +PNH, Pochentong, 4, major, POINT(104.845027612457 11.5526449176513), Cambodia, Phnom Penh, POINT(104.9211 11.5694) +PNQ, Pune, 4, [major,military], POINT(73.9089838110016 18.5791766115328), India, Pune, POINT(73.8567 18.5203) +POM, Port Moresby Int'l, 4, major, POINT(147.211250855977 -9.43865269316142), Papua New Guinea, Port Moresby, POINT(147.1494 -9.4789) +PTY, Tocumen Int'l, 4, major, POINT(-79.3871348215438 9.06687242265839), Panama, Tocumen, POINT(-79.38 9.08) +PUQ, Carlos Ibáñez de Campo Int'l, 4, mid, POINT(-70.8431237851324 -53.0050698255177), Chile, Punta Arenas, POINT(-70.9333 -53.1667) +RDU, Durham Int'l, 4, major, POINT(-78.7913814006751 35.8752323452255), United States, Raleigh, POINT(-78.6429 35.8324) +RGN, Mingaladon, 4, major, POINT(96.1341946114947 16.9011542818251), Myanmar, Rangoon, POINT(96.16 16.795) +RIX, Riga, 4, major, POINT(23.9793791116995 56.9220038786097), Latvia, Jūrmala, POINT(23.7703 56.9681) +SAH, Sanaa Int'l, 4, mid, POINT(44.2246467902561 15.4739027755737), Yemen, Sanaa, POINT(44.2064 15.3483) +SDA, Baghdad Int'l, 4, major, POINT(44.2289125352942 33.268162986377), Iraq, Baghdad, POINT(44.3661 33.3153) +SDQ, De Las Américas Int'l, 4, major, POINT(-69.6764726754667 18.4302196948173), Dominican Republic, Santo Domingo Este, POINT(-69.8734 18.4855) +SGN, Tan Son Nhat, 4, major, POINT(106.664246141375 10.8163005571879), Vietnam, Ho Chi Minh City, POINT(106.7019 10.7756) +SKG, Thessaloniki, 4, major, POINT(22.9764353610613 40.5238736887775), Greece, Thessaloníki, POINT(22.9347 40.6403) +SOF, Vrazhdebna, 4, major, POINT(23.4024521357708 42.6891841273195), Bulgaria, Sofia, POINT(23.33 42.7) +STV, Surat, 4, major, POINT(72.7424384372589 21.1204503297172), India, Sūrat, POINT(72.8311 21.1702) +SUV, Nausori Int'l, 4, mid, POINT(178.560048369959 -18.0458996922854), Fiji, Nausori, POINT(178.5454 -18.0244) +SYZ, Shiraz Int'l, 4, major, POINT(52.5897712745211 29.5458013842874), Iran, Shīrāz, POINT(52.5425 29.61) +TAM, Gen Francisco J Mina Int'l, 4, mid, POINT(-97.8698137568394 22.2893319525064), Mexico, Tampico, POINT(-97.8686 22.2553) +TGU, Toncontin Int'l, 4, mid, POINT(-87.2192116348986 14.0599852192071), Honduras, Tegucigalpa, POINT(-87.2167 14.1) +THR, Mehrabad Int'l, 4, major, POINT(51.3208069717572 35.6913743304946), Iran, Tehran, POINT(51.3889 35.6892) +TIA, Tirane Rinas, 4, major, POINT(19.7150324049722 41.4208514680567), Albania, Tirana, POINT(19.8178 41.3289) +TIJ, General Abelardo L Rodriguez Int'l, 4, major, POINT(-116.975476095598 32.5460499135013), Mexico, Tijuana, POINT(-117.0333 32.525) +TLC, Jose Maria Morelos Y Pavon, 4, mid, POINT(-99.5706494463542 19.3386880423032), Mexico, Mexico City, POINT(-99.1333 19.4333) +TLL, Ulemiste, 4, major, POINT(24.798964869983 59.4165014697451), Estonia, Tallinn, POINT(24.7453 59.4372) +TLV, Ben Gurion, 4, major, POINT(34.8708499180995 32.0007468501844), Israel, Tel Aviv-Yafo, POINT(34.78 32.08) +TMS, São Tomé Salazar, 4, mid, POINT(6.71282193005667 0.374744213699427), Sao Tome and Principe, Neves, POINT(6.5517 0.3592) +TNR, Antananarivo Ivato, 4, mid, POINT(47.4753540009579 -18.7993348763082), Madagascar, Antananarivo, POINT(47.5167 -18.9333) +TPA, Tampa Int'l, 4, major, POINT(-82.534824252055 27.9800400852184), United States, Tampa, POINT(-82.4447 27.9945) +VLN, Zim Valencia, 4, mid, POINT(-67.9223617121873 10.1540056883979), Venezuela, Los Guayos, POINT(-67.9333 10.1833) +VOG, Gumrak, 4, mid, POINT(44.354767968489 48.7916764657611), Russia, Volgograd, POINT(44.5147 48.7086) +VTE, Vientiane, 4, mid, POINT(102.568238195728 17.9754595948321), Laos, Vientiane, POINT(102.6 17.9667) +VVI, Viru Viru Int'l, 4, mid, POINT(-63.1403888218213 -17.6479468257839), Bolivia, Warnes, POINT(-63.1667 -17.5167) +WLG, Wellington Int'l, 4, major, POINT(174.811665268238 -41.3289891844659), New Zealand, Lower Hutt, POINT(174.9167 -41.2167) +YPR, Prince Rupert, 4, mid, POINT(-130.445587 54.292), Canada, Prince Rupert, POINT(-130.3271 54.3122) +YQG, Windsor, 4, mid, POINT(-82.9600877389448 42.2658784727198), United States, Detroit, POINT(-83.1024 42.3834) +YQR, Regina, 4, mid, POINT(-104.655433975371 50.4332192867183), Canada, Regina, POINT(-104.6067 50.4547) +YVR, Vancouver Int'l, 4, major, POINT(-123.180867003812 49.1935590395715), Canada, Surrey, POINT(-122.8489 49.19) +YWG, Winnipeg Int'l, 4, major, POINT(-97.2267694809585 49.9033302471671), Canada, Winnipeg, POINT(-97.1464 49.8844) +YXE, John G Diefenbaker Int'l, 4, mid, POINT(-106.690181967554 52.1701439447381), Canada, Saskatoon, POINT(-106.6833 52.1333) +YXY, Whitehorse Int'l, 4, mid, POINT(-135.076210089402 60.7141521481397), Canada, Whitehorse, POINT(-135.0691 60.7029) +YYC, Calgary Int'l, 4, major, POINT(-114.010560500236 51.1308572567549), Canada, Calgary, POINT(-114.0667 51.05) +YYG, Charlottetown, 4, mid, POINT(-63.1312341333234 46.2858131367525), Canada, Charlottetown, POINT(-63.1347 46.2403) +YYQ, Churchill, 4, mid, POINT(-94.0813639506318 58.7497237849788), , , +YYT, St John's Int'l, 4, mid, POINT(-52.7433337428638 47.6131179007955), Canada, St. John's, POINT(-52.7971 47.4817) +YZF, Yellowknife, 4, mid, POINT(-114.437846335049 62.4707373610202), Canada, Yellowknife, POINT(-114.4053 62.4709) +ZAG, Zagreb, 4, major, POINT(16.0615138009014 45.7333266730984), Croatia, Velika Gorica, POINT(16.0667 45.7) +ZNZ, Zanzibar, 4, mid, POINT(39.2223319841558 -6.21857034620282), Tanzania, Zanzibar, POINT(39.199 -6.165) +REK, Reykjavik Air Terminal, 4, mid, POINT(-21.9466344031327 64.1318728609901), Iceland, Reykjavík, POINT(-21.94 64.1467) +ARH, Arkhangelsk-Talagi, 4, mid, POINT(40.7133465694594 64.5967437730455), Russia, Arkhangelsk, POINT(40.5333 64.55) +KZN, Kazan Int'l, 4, major, POINT(49.2984458036407 55.6080601429764), Russia, Kazan, POINT(49.1089 55.7964) +ORY, Paris Orly, 4, major, POINT(2.36737912783773 48.7313030458052), France, Vitry-sur-Seine, POINT(2.3928 48.7875) +YQB, Québec, 4, major, POINT(-71.3839280711731 46.7915684363308), Canada, Quebec City, POINT(-71.2081 46.8139) +YUL, Montréal-Trudeau, 4, major, POINT(-73.7493162650417 45.4583512294531), Canada, Montréal, POINT(-73.5617 45.5089) +NRT, Narita Int'l, 4, major, POINT(140.384401709179 35.7640560727828), Japan, Chiba, POINT(140.1064 35.6073) +NGO, Chubu Centrair Int'l, 4, major, POINT(136.814771286824 34.8590296958162), Japan, Nagoya, POINT(136.9 35.1833) +OKD, Okadama, 4, mid, POINT(141.382100450075 43.1106495990978), Japan, Sapporo, POINT(141.35 43.0667) +BGO, Bergen Flesland, 4, major, POINT(5.22725311562336 60.2890610502966), Norway, Askøy, POINT(5.15 60.4667) +TOS, Tromsø Langnes, 4, major, POINT(18.9072624292132 69.6796790473478), Norway, Tromsø, POINT(18.9428 69.6828) +BEL, Val de Caes Int'l, 4, mid, POINT(-48.4795602893793 -1.38974628795546), Brazil, Ananindeua, POINT(-48.3719 -1.3658) +CGR, Campo Grande Int'l, 4, mid, POINT(-54.6689498781305 -20.4572717360311), Brazil, Campo Grande, POINT(-54.615 -20.4839) +CWB, Afonso Pena Int'l, 4, mid, POINT(-49.1737093663469 -25.5360001430558), Brazil, Curitiba, POINT(-49.2711 -25.4297) +FOR, Pinto Martins Int'l, 4, mid, POINT(-38.5407472498334 -3.77859496233091), Brazil, Fortaleza, POINT(-38.5275 -3.7275) +GRU, São Paulo-Guarulhos Int'l, 4, major, POINT(-46.481753608842 -23.4261155770421), Brazil, São Paulo, POINT(-46.6333 -23.55) +GYN, Santa Genoveva, 4, mid, POINT(-49.2266464905994 -16.6323665721637), Brazil, Goiânia, POINT(-49.25 -16.6667) +POA, Salgado Filho Int'l, 4, mid, POINT(-51.1770409488172 -29.9901930170609), Brazil, Porto Alegre, POINT(-51.23 -30.0331) +REC, Gilberto Freyre Int'l, 4, mid, POINT(-34.9182667174851 -8.13162553076239), Brazil, Recife, POINT(-34.9 -8.05) +SSA, Deputado Luis Eduardo Magalhaes Int'l, 4, mid, POINT(-38.3347989911732 -12.9143614970326), Brazil, Camaçari, POINT(-38.3239 -12.6978) +MDZ, El Plumerillo, 4, mid, POINT(-68.7984838394473 -32.8278001692719), Argentina, Godoy Cruz, POINT(-68.8333 -32.9167) +MAO, Eduardo Gomes Int'l, 4, mid, POINT(-60.0460645898854 -3.0321390062591), Brazil, Manaus, POINT(-60.0167 -3.1) +NSI, Yaoundé Nsimalen Int'l, 4, mid, POINT(11.5479941396807 3.71484520708126), Cameroon, Yaoundé, POINT(11.5167 3.8667) +PVG, Shanghai Pudong Int'l, 4, major, POINT(121.801518760578 31.1523090295533), China, Shanghai, POINT(121.4667 31.1667) +ADJ, Marka Int'l, 4, mid, POINT(35.9841052362449 31.9741994015442), Jordan, Amman, POINT(35.9328 31.9497) +MLE, Male Int'l, 4, major, POINT(73.5273902836844 4.18870090323372), Maldives, Male, POINT(73.5089 4.1753) +VER, Gen. Heriberto Jara Int'l, 4, mid, POINT(-96.1835702143695 19.1424237025017), Mexico, Veracruz, POINT(-96.1533 19.1903) +OXB, Osvaldo Vieira Int'l, 4, mid, POINT(-15.651185561666 11.8889231454855), Guinea-Bissau, Bissau, POINT(-15.5667 11.85) +DVO, Francisco Bangoy Int'l, 4, major, POINT(125.645066609434 7.13053746163073), Philippines, Davao, POINT(125.6 7.0667) +SEZ, Seychelles Int'l, 4, mid, POINT(55.5115519246793 -4.67106914178521), , , +DKR, Léopold Sedar Senghor Int'l, 4, major, POINT(-17.490407907719 14.7456306146748), Senegal, Dakar, POINT(-17.4467 14.6928) +PZU, Port Sudan New Int'l, 4, mid, POINT(37.2387475981025 19.4341052385231), Sudan, Port Sudan, POINT(37.2167 19.6167) +TAS, Tashkent Int'l, 4, major, POINT(69.2666137241129 41.2622338767383), Uzbekistan, Tashkent, POINT(69.2797 41.3111) +CPH, Copenhagen, 3, major, POINT(12.6493508684508 55.6285017221528), Denmark, Copenhagen, POINT(12.5683 55.6761) +BBU, Aeroportul National Bucuresti-Baneasa, 3, mid, POINT(26.0857251587764 44.497041455972), Romania, Bucharest, POINT(26.1039 44.4325) +BUD, Ferihegy, 3, major, POINT(19.2622301677881 47.433274269248), Hungary, Budapest, POINT(19.0514 47.4925) +CKG, Chongqing Jiangbei Int'l, 3, major, POINT(106.638019704811 29.7240422241688), China, Chongqing, POINT(106.5069 29.55) +CLT, Douglas Int'l, 3, major, POINT(-80.9439277342763 35.2204281685597), United States, Gastonia, POINT(-81.1854 35.2494) +DTW, Detroit Metro, 3, major, POINT(-83.3478935065615 42.2257204508004), United States, Detroit, POINT(-83.1024 42.3834) +DUB, Dublin, 3, major, POINT(-6.24388491037139 53.42700828497), Ireland, Finglas, POINT(-6.2181 53.4597) +FAI, Fairbanks Int'l, 3, major, POINT(-147.865721120795 64.8180981117369), United States, Fairbanks, POINT(-147.6533 64.8353) +HAM, Hamburg, 3, major, POINT(10.005647830925 53.6320011640866), Germany, Norderstedt, POINT(10.0103 53.7064) +KUL, Kuala Lumpur Int'l, 3, major, POINT(101.713886325743 2.74751295791811), Malaysia, Kuala Lumpur, POINT(101.6953 3.1478) +LAS, Mccarran Int'l, 3, major, POINT(-115.151323951283 36.0849602383367), United States, Sunrise Manor, POINT(-115.0487 36.1783) +MCO, Orlando Int'l, 3, major, POINT(-81.3073713307985 28.4311506791138), United States, Orlando, POINT(-81.337 28.4773) +MSP, Minneapolis St. Paul Int'l, 3, major, POINT(-93.2081003718301 44.8820263631968), United States, Minneapolis, POINT(-93.2678 44.9635) +MUC, Franz-Josef-Strauss, 3, major, POINT(11.7880627192437 48.3538373961608), Germany, Munich, POINT(11.575 48.1375) +PHL, Philadelphia Int'l, 3, major, POINT(-75.2429857676998 39.876087236427), United States, Philadelphia, POINT(-75.1339 40.0077) +PHX, Sky Harbor Int'l, 3, major, POINT(-112.01363529773 33.4358607639498), United States, Phoenix, POINT(-112.0892 33.5722) +SLC, Salt Lake City Int'l, 3, major, POINT(-111.981984879993 40.7867290053708), United States, West Valley City, POINT(-112.0123 40.6886) +STL, Lambert St Louis Int'l, 3, major, POINT(-90.3659545350675 38.7427163155204), United States, St. Louis, POINT(-90.2451 38.6359) +WAW, Okecie Int'l, 3, major, POINT(20.9727263383587 52.171026749259), Poland, Piaseczno, POINT(21.0167 52.0667) +ZRH, Zurich Int'l, 3, major, POINT(8.56221279534765 47.4523895064915), Switzerland, Zürich, POINT(8.5411 47.3744) +CRL, Gosselies, 3, mid, POINT(4.4543736298165 50.4571296549567), Belgium, Brussels, POINT(4.3525 50.8467) +MUCf, Munich Freight Terminal, 3, major, POINT(11.7694828593654 48.3497964078377), Germany, Munich, POINT(11.575 48.1375) +BCN, Barcelona, 3, major, POINT(2.07800334981292 41.3031552797463), Spain, El Prat de Llobregat, POINT(2.0953 41.3246) +PRG, Ruzyn, 3, major, POINT(14.2674849854076 50.1076511703671), Czechia, Prague, POINT(14.4214 50.0875) +HKG, Hong Kong Int'l, 2, major, POINT(113.935016387376 22.3153328280868), China, Shenzhen, POINT(114.054 22.535) +TPE, Taoyuan, 2, major, POINT(121.231370453323 25.0767411043346), Taiwan, Taipei, POINT(121.5625 25.0375) +AMS, Schiphol, 2, major, POINT(4.76437693232812 52.3089323889822), Netherlands, Hoofddorp, POINT(4.6907 52.3061) +SIN, Singapore Changi, 2, major, POINT(103.986413880993 1.35616083528126), Singapore, Singapore, POINT(103.8 1.3) +LHR, London Heathrow, 2, major, POINT(-0.453156652063309 51.4709958799938), United Kingdom, Hounslow, POINT(-0.375 51.4668) +AKL, Auckland Int'l, 2, major, POINT(174.791719433715 -37.0063551142815), New Zealand, Auckland, POINT(174.74 -36.8406) +ANC, Anchorage Int'l, 2, major, POINT(-149.981725100633 61.1728936745367), United States, Knik-Fairview, POINT(-149.6252 61.5082) +ATL, Hartsfield-Jackson Atlanta Int'l, 2, major, POINT(-84.4253974336047 33.6405290807352), United States, Atlanta, POINT(-84.422 33.7628) +PEK, Beijing Capital, 2, major, POINT(116.588174004661 40.078766336331), China, Beijing, POINT(116.4075 39.904) +BOG, Eldorado Int'l, 2, major, POINT(-74.1433718001028 4.69883276192097), Colombia, Bogotá, POINT(-74.0722 4.7111) +BOM, Chhatrapati Shivaji Int'l, 2, major, POINT(72.8745639500051 19.0951019488402), India, Mumbai, POINT(72.8775 19.0761) +BOS, Gen E L Logan Int'l, 2, major, POINT(-71.0164066172958 42.3665658198506), United States, Revere, POINT(-71.004 42.4189) +BWI, Baltimore-Washington Int'l Thurgood Marshall,2, major, POINT(-76.6686428352448 39.1793943583568), United States, Baltimore, POINT(-76.6144 39.3051) +CAI, Cairo Int'l, 2, major, POINT(31.3997430067114 30.1119904385575), Egypt, Giza, POINT(31.2118 29.987) +CAS, Casablanca-Anfa, 2, mid, POINT(-7.66321880771143 33.5627883851079), Morocco, Mediouna, POINT(-7.51 33.45) +CCS, Simón Bolivar Int'l, 2, mid, POINT(-67.0057488076316 10.5973549146064), Venezuela, Catia La Mar, POINT(-67.0333 10.6) +CPT, Cape Town Int'l, 2, major, POINT(18.5976565083138 -33.9704466120395), South Africa, Mitchells Plain, POINT(18.6181 -34.0506) +CTU, Chengdushuang Liu, 2, major, POINT(103.956136481695 30.5810712647464), China, Chengdu, POINT(104.0633 30.66) +DEL, Indira Gandhi Int'l, 2, major, POINT(77.0878362565332 28.5592039760586), India, Najafgarh, POINT(76.9798 28.6092) +DEN, Denver Int'l, 2, major, POINT(-104.673797338542 39.8494613881509), United States, Denver, POINT(-104.8758 39.762) +DFW, Dallas-Ft. Worth Int'l, 2, major, POINT(-97.0403710741144 32.9001505594816), United States, Irving, POINT(-96.9702 32.8583) +DMK, Don Muang Int'l, 2, major, POINT(100.602578626505 13.9202766010347), Thailand, Bangkok, POINT(100.4942 13.7525) +DXB, Dubai Int'l, 2, major, POINT(55.3540769172243 25.2525655938182), United Arab Emirates, Dubai, POINT(55.2972 25.2631) +EWR, Newark Int'l, 2, major, POINT(-74.1771472796706 40.6904798278929), United States, New York, POINT(-73.9249 40.6943) +EZE, Ministro Pistarini Int'l, 2, major, POINT(-58.5412456939382 -34.8136469380323), Argentina, José María Ezeiza, POINT(-58.5167 -34.8333) +FLL, Fort Lauderdale Hollywood Int'l, 2, major, POINT(-80.1452588465189 26.0717095746827), United States, Hollywood, POINT(-80.1679 26.0293) +IAH, George Bush Intercontinental, 2, major, POINT(-95.3337047912947 29.9865909034907), United States, Houston, POINT(-95.3885 29.786) +IST, Atatürk Hava Limani, 2, major, POINT(28.8195493087893 40.9778388177797), Turkey, Istanbul, POINT(28.955 41.0136) +JNB, OR Tambo Int'l, 2, major, POINT(28.2319885648741 -26.1320953994887), South Africa, Johannesburg, POINT(28.0456 -26.2044) +JNU, Juneau Int'l, 2, mid, POINT(-134.583573037872 58.3589441045951), United States, Juneau, POINT(-134.1739 58.4546) +LAX, Los Angeles Int'l, 2, major, POINT(-118.402468548522 33.9441742543586), United States, Los Angeles, POINT(-118.4068 34.1141) +LIN, Linate, 2, major, POINT(9.27996629691061 45.4603938456252), Italy, Milan, POINT(9.19 45.4669) +MEL, Melbourne Int'l, 2, major, POINT(144.848998091131 -37.6699411967893), Australia, Melton, POINT(144.5833 -37.6833) +MEX, Lic Benito Juarez Int'l, 2, major, POINT(-99.0826079514239 19.4354695720494), Mexico, Mexico City, POINT(-99.1333 19.4333) +MNL, Ninoy Aquino Int'l, 2, major, POINT(121.004122083437 14.5068323762967), Philippines, Manila, POINT(120.9772 14.5958) +NBO, Jomo Kenyatta Int'l, 2, major, POINT(36.9250887490365 -1.33052964350634), Kenya, Nairobi, POINT(36.8172 -1.2864) +HNL, Honolulu Int'l, 2, major, POINT(-157.919783173755 21.332022315024), United States, Honolulu, POINT(-157.846 21.3294) +ORD, Chicago O'Hare Int'l, 2, major, POINT(-87.90513439065 41.9765291023803), United States, Chicago, POINT(-87.6866 41.8375) +RUH, King Khalid Int'l, 2, major, POINT(46.701829023464 24.9590317436512), Saudi Arabia, Riyadh, POINT(46.7167 24.6333) +SCL, Arturo Merino Benitez Int'l, 2, major, POINT(-70.7936860162974 -33.3968336342597), Chile, Quilicura, POINT(-70.7333 -33.3667) +SEA, Tacoma Int'l, 2, major, POINT(-122.302289722924 47.4435819127259), United States, Seattle, POINT(-122.3244 47.6211) +SFO, San Francisco Int'l, 2, major, POINT(-122.383470344449 37.6170250868053), United States, South San Francisco, POINT(-122.4196 37.6538) +SHA, Hongqiao, 2, major, POINT(121.341183788567 31.1872574314078), China, Shanghai, POINT(121.4667 31.1667) +SVO, Sheremtyevo, 2, major, POINT(37.4159690348414 55.966447172512), Russia, Khimki, POINT(37.445 55.8892) +YYZ, Toronto-Pearson Int'l, 2, major, POINT(-79.6114193247449 43.6809595186356), Canada, Toronto, POINT(-79.3733 43.7417) +SYD, Kingsford Smith, 2, major, POINT(151.166067305601 -33.9365832057717), Australia, Sydney, POINT(151.21 -33.8678) +HEL, Helsinki Vantaa, 2, major, POINT(24.9682078665914 60.3187158912982), Finland, Helsinki, POINT(24.9375 60.1708) +CDG, Charles de Gaulle Int'l, 2, major, POINT(2.54186776739457 49.0144200969386), France, Aulnay-sous-Bois, POINT(2.4906 48.9386) +TXL, Berlin-Tegel Int'l, 2, major, POINT(13.2903090925074 52.5544287044101), Germany, Hohen Neuendorf, POINT(13.2833 52.6667) +VIE, Vienna Schwechat Int'l, 2, major, POINT(16.5607679642129 48.1197563052538), Hungary, Sopron, POINT(16.5831 47.6849) +FRA, Frankfurt Int'l, 2, major, POINT(8.57182286907608 50.0506770895207), Germany, Frankfurt, POINT(8.6822 50.1106) +FCO, Leonardo da Vinci Int'l, 2, major, POINT(12.2501008973638 41.7950786307394), Italy, Fiumicino, POINT(12.2333 41.7667) +ITM, Osaka Int'l, 2, major, POINT(135.442475256249 34.7901980848749), Japan, Ōsaka, POINT(135.5022 34.6939) +GMP, Gimpo Int'l, 2, major, POINT(126.802392860276 37.5573005399508), South Korea, Seoul, POINT(126.99 37.56) +OSL, Oslo Gardermoen, 2, major, POINT(11.0991032762581 60.1935783171386), Norway, Oslo, POINT(10.7389 59.9133) +BSB, Juscelino Kubitschek Int'l, 2, major, POINT(-47.9207885133625 -15.8699985002824), Brazil, Brasília, POINT(-47.8828 -15.7939) +CGH, Congonhas Int'l, 2, major, POINT(-46.6591155302196 -23.62685882701), Brazil, São Paulo, POINT(-46.6333 -23.55) +GIG, Rio de Janeiro-Antonio Carlos Jobim Int'l, 2, major, POINT(-43.2483813790683 -22.8123437125006), Brazil, Rio de Janeiro, POINT(-43.2056 -22.9111) +MAD, Madrid Barajas, 2, major, POINT(-3.56902665458863 40.4681282733923), Spain, Torrejón de Ardoz, POINT(-3.4978 40.4614) +SJU, Luis Muñoz Marin, 2, major, POINT(-66.0042299757548 18.4380770734949), Puerto Rico, Carolina, POINT(-65.9792 18.4054) +ARN, Arlanda, 2, major, POINT(17.9307299016916 59.6511203397372), Sweden, Stockholm, POINT(18.0686 59.3294) +CGK, Soekarno-Hatta Int'l, 2, major, POINT(106.654296151172 -6.1266029559729), Indonesia, Jakarta, POINT(106.8275 -6.175) +ATH, Eleftherios Venizelos Int'l, 2, major, POINT(23.9471160554073 37.9362331299254), Greece, Piraeus, POINT(23.6469 37.943) diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-airports.json b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-airports.json index 78f85112bd516..ce7dc45caf44a 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-airports.json +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/mapping-airports.json @@ -14,6 +14,15 @@ }, "location": { "type": "geo_point" + }, + "country": { + "type": "keyword" + }, + "city": { + "type": "keyword" + }, + "city_location": { + "type": "geo_point" } } } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec index 754d4a0e156cf..b8d6193887c34 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec @@ -66,6 +66,7 @@ sin |"double sin(n:double|integer|long|unsigned_long)" |n sinh |"double sinh(n:double|integer|long|unsigned_long)"|n |"double|integer|long|unsigned_long" |"The number to return the hyperbolic sine of" |double | "Returns the hyperbolic sine of a number" | false | false | false split |"keyword split(str:keyword|text, delim:keyword|text)" |[str, delim] |["keyword|text", "keyword|text"] |["", ""] |keyword | "Split a single valued string into multiple strings." | [false, false] | false | false sqrt |"double sqrt(n:double|integer|long|unsigned_long)" |n |"double|integer|long|unsigned_long" | "" |double | "Returns the square root of a number." | false | false | false +st_centroid |"geo_point|cartesian_point st_centroid(field:geo_point|cartesian_point)" |field |"geo_point|cartesian_point" | "" |"geo_point|cartesian_point" | "The centroid of a spatial field." | false | false | true starts_with |"boolean starts_with(str:keyword|text, prefix:keyword|text)" |[str, prefix] |["keyword|text", "keyword|text"] |["", ""] |boolean | "Returns a boolean that indicates whether a keyword string starts with another string" | [false, false] | false | false substring |"keyword substring(str:keyword|text, start:integer, ?length:integer)" |[str, start, length] |["keyword|text", "integer", "integer"] |["", "", ""] |keyword | "Returns a substring of a string, specified by a start position and an optional length" | [false, false, true]| false | false sum |"long sum(field:double|integer|long|unsigned_long)" |field |"double|integer|long|unsigned_long" | "" |long | "The sum of a numeric field." | false | false | true @@ -161,6 +162,7 @@ double pi() "double sinh(n:double|integer|long|unsigned_long)" "keyword split(str:keyword|text, delim:keyword|text)" "double sqrt(n:double|integer|long|unsigned_long)" +"geo_point|cartesian_point st_centroid(field:geo_point|cartesian_point)" "boolean starts_with(str:keyword|text, prefix:keyword|text)" "keyword substring(str:keyword|text, start:integer, ?length:integer)" "long sum(field:double|integer|long|unsigned_long)" @@ -216,5 +218,5 @@ countFunctions#[skip:-8.12.99] show functions | stats a = count(*), b = count(*), c = count(*) | mv_expand c; a:long | b:long | c:long -88 | 88 | 88 +89 | 89 | 89 ; diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial.csv-spec index 24a85c39a1a10..5c4aae740910b 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial.csv-spec @@ -31,22 +31,255 @@ wkt:keyword ["POINT(42.97109630194 14.7552534413725)", "POINT(75.8092915005895 22.727749187571)"] |[POINT(42.97109630194 14.7552534413725), POINT(75.8092915005895 22.727749187571)] ; +centroidFromStringNested#[skip:-8.12.99, reason:st_centroid added in 8.13] +row wkt = "POINT(42.97109629958868 14.7552534006536)" +| STATS c = ST_CENTROID(TO_GEOPOINT(wkt)); + +c:geo_point +POINT(42.97109629958868 14.7552534006536) +; + +centroidFromString1#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(42.97109629958868 14.7552534006536)"] +| MV_EXPAND wkt +| EVAL pt = TO_GEOPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:geo_point +POINT(42.97109629958868 14.7552534006536) +; + +centroidFromString2#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(42.97109629958868 14.7552534006536)", "POINT(75.80929149873555 22.72774917539209)"] +| MV_EXPAND wkt +| EVAL pt = TO_GEOPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:geo_point +POINT(59.390193899162114 18.741501288022846) +; + +centroidFromString3#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(42.97109629958868 14.7552534006536)", "POINT(75.80929149873555 22.72774917539209)", "POINT(-0.030548143003023033 24.37553649504829)"] +| MV_EXPAND wkt +| EVAL pt = TO_GEOPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:geo_point +POINT(39.58327988510707 20.619513023697994) +; + simpleLoad#[skip:-8.12.99, reason:spatial type geo_point improved precision in 8.13] FROM airports | WHERE scalerank == 9 | SORT abbrev | WHERE length(name) > 12; -abbrev:keyword | location:geo_point | name:text | scalerank:i | type:k -CJJ | POINT(127.495916124681 36.7220227766673) | Cheongju Int'l | 9 | major -HOD | POINT(42.97109630194 14.7552534413725) | Hodeidah Int'l | 9 | mid -IDR | POINT(75.8092915005895 22.727749187571) | Devi Ahilyabai Holkar Int'l | 9 | mid -IXC | POINT(76.8017261105242 30.6707248949667) | Chandigarh Int'l | 9 | [major, military] -LYP | POINT(72.9878190922305 31.3627435480862) | Faisalabad Int'l | 9 | [mid, military] -MLG | POINT(112.711418617258 -7.92998002840567) | Abdul Rachman Saleh | 9 | [mid, military] -OMS | POINT(73.3163595376585 54.9576482934059) | Omsk Tsentralny | 9 | mid -OVB | POINT(82.6671524525865 55.0095847136264) | Novosibirsk Tolmachev | 9 | mid -OZH | POINT(35.3018728575279 47.8732635579023) | Zaporozhye Int'l | 9 | [mid, military] -TRZ | POINT(78.7089578747476 10.7603571306554) | Tiruchirappalli | 9 | mid -WIIT | POINT(105.176060419161 -5.242566777132) | Radin Inten II | 9 | mid -ZAH | POINT(60.900708564915 29.4752941956573) | Zahedan Int'l | 9 | mid +abbrev:keyword | city:keyword | city_location:geo_point | country:keyword | location:geo_point | name:text | scalerank:i | type:k +CJJ | Cheongju | POINT(127.4833 36.6333) | South Korea | POINT(127.495916124681 36.7220227766673) | Cheongju Int'l | 9 | major +HOD | Al Ḩudaydah | POINT(42.9511 14.8022) | Yemen | POINT(42.97109630194 14.7552534413725) | Hodeidah Int'l | 9 | mid +IDR | Indore | POINT(75.8472 22.7167) | India | POINT(75.8092915005895 22.727749187571) | Devi Ahilyabai Holkar Int'l | 9 | mid +IXC | Chandīgarh | POINT(76.78 30.75) | India | POINT(76.8017261105242 30.6707248949667) | Chandigarh Int'l | 9 | [major, military] +LYP | Faisalabad | POINT(73.0911 31.4167) | Pakistan | POINT(72.9878190922305 31.3627435480862) | Faisalabad Int'l | 9 | [mid, military] +MLG | Malang | POINT(112.62 -7.98) | Indonesia | POINT(112.711418617258 -7.92998002840567) | Abdul Rachman Saleh | 9 | [mid, military] +OMS | Omsk | POINT(73.3833 54.9667) | Russia | POINT(73.3163595376585 54.9576482934059) | Omsk Tsentralny | 9 | mid +OVB | Novosibirsk | POINT(82.9167 55.0333) | Russia | POINT(82.6671524525865 55.0095847136264) | Novosibirsk Tolmachev | 9 | mid +OZH | Zaporizhzhia | POINT(35.1175 47.85) | Ukraine | POINT(35.3018728575279 47.8732635579023) | Zaporozhye Int'l | 9 | [mid, military] +TRZ | Trichinopoly | POINT(78.7047 10.7903) | India | POINT(78.7089578747476 10.7603571306554) | Tiruchirappalli | 9 | mid +WIIT | Bandar Lampung | POINT(105.2667 -5.45) | Indonesia | POINT(105.176060419161 -5.242566777132) | Radin Inten II | 9 | mid +ZAH | Zāhedān | POINT(60.8628 29.4964) | Iran | POINT(60.900708564915 29.4752941956573) | Zahedan Int'l | 9 | mid +; + +centroidFromAirports#[skip:-8.12.99, reason:st_centroid added in 8.13] +// tag::st_centroid-airports[] +FROM airports +| STATS centroid=ST_CENTROID(location) +// end::st_centroid-airports[] +; + +// tag::st_centroid-airports-result[] +centroid:geo_point +POINT(-0.030548143003023033 24.37553649504829) +// end::st_centroid-airports-result[] +; + +centroidFromAirportsNested#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(TO_GEOPOINT(location)) +; + +centroid:geo_point +POINT (-0.03054810272375508 24.37553651570554) +; + +centroidFromAirportsCount#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:geo_point | count:long +POINT(-0.030548143003023033 24.37553649504829) | 891 +; + +centroidFromAirportsCountGrouped#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +| SORT scalerank DESC +; + +centroid:geo_point | count:long | scalerank:i +POINT(83.27726172452623 28.99289782286029) | 33 | 9 +POINT(-12.330427954750142 29.554613442537242) | 247 | 8 +POINT(19.934784222002094 13.864835376774234) | 133 | 7 +POINT(-10.861430599274028 28.170889387807705) | 151 | 6 +POINT(9.394940837974781 28.953888530174837) | 46 | 5 +POINT(-3.118828632340757 17.868389564340685) | 194 | 4 +POINT(-26.976065734634176 42.907839377294295) | 24 | 3 +POINT(1.2588642098541771 24.379140841774642) | 63 | 2 +; + +centroidFromAirportsFiltered#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| WHERE scalerank == 9 +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:geo_point | count:long +POINT(83.27726172452623 28.99289782286029) | 33 +; + +centroidFromAirportsCountGroupedCentroid#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +| STATS centroid=ST_CENTROID(centroid), count=SUM(count) +; + +centroid:geo_point | count:long +POINT (7.572387259169772 26.836561792945492) | 891 +; + +centroidFromAirportsCountCityLocations#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(city_location), count=COUNT() +; + +centroid:geo_point | count:long +POINT (1.3965610809060276 24.127649406297987) | 891 +; + +centroidFromAirportsCountGroupedCountry#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(city_location), count=COUNT() BY country +| SORT count DESC, country ASC +| WHERE count >= 10 +; + +centroid:geo_point | count:long | country:k +POINT (-97.3333946136801 38.07953176370194) | 129 | United States +POINT (78.42264595516026 21.91585598140955) | 50 | India +POINT (-102.37784670852125 24.268197756260633) | 45 | Mexico +POINT (112.92023897966052 32.982985347554816) | 41 | China +POINT (-94.40291355867443 50.70210267953273) | 37 | Canada +POINT (-47.3366032685003 -15.80931615144495) | 31 | Brazil +POINT (57.30444226807986 55.01281536452902) | 26 | Russia +null | 19 | null +POINT (140.68777053945644 -27.688147084349218) | 17 | Australia +POINT (-2.594152985359816 54.359511745107525) | 17 | United Kingdom +POINT (-64.19630772720735 -34.977900019058815) | 13 | Argentina +POINT (3.710366631858051 46.890841646818444) | 12 | France +POINT (107.94202494900674 -4.528175020823255) | 12 | Indonesia +POINT (10.15085451220247 50.663009069605984) | 11 | Germany +POINT (11.015199956230141 43.04051815532148) | 11 | Italy +POINT (6.725663595240224 9.201645437966693) | 11 | Nigeria +POINT (70.7946499697864 30.69746997440234) | 10 | Pakistan +; + +centroidFromAirportsFilteredCountry#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| WHERE country == "United States" +| STATS centroid=ST_CENTROID(city_location), count=COUNT() +; + +centroid:geo_point | count:long +POINT (-97.3333946136801 38.07953176370194) | 129 +; + +centroidFromAirportsCountGroupedCountryCentroid#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS centroid=ST_CENTROID(city_location), count=COUNT() BY country +| STATS centroid=ST_CENTROID(centroid), count=SUM(count) +; + +centroid:geo_point | count:long +POINT (17.55538044598613 18.185558743854063) | 891 +; + +centroidFromAirportsCountryCount#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| STATS airports=ST_CENTROID(location), cities=ST_CENTROID(city_location), count=COUNT() +; + +airports:geo_point | cities:geo_point | count:long +POINT(-0.030548143003023033 24.37553649504829) | POINT (1.3965610809060276 24.127649406297987) | 891 +; + +centroidFromAirportsFilteredAndSorted#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| WHERE scalerank == 9 +| SORT abbrev +| WHERE length(name) > 12 +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:geo_point | count:long +POINT(78.73736493755132 26.761841227998957) | 12 +; + +centroidFromAirportsAfterMvExpand#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| MV_EXPAND type +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:geo_point | count:long +POINT(2.121611400672094 24.559172889205755) | 933 +; + +centroidFromAirportsGroupedAfterMvExpand#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| MV_EXPAND type +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +| SORT scalerank DESC +; + +centroid:geo_point | count:long | scalerank:i +POINT(83.16847535921261 28.79002037679311) | 40 | 9 +POINT(-9.579701727760353 29.651473146404623) | 266 | 8 +POINT(21.64429362312379 14.766539423726499) | 142 | 7 +POINT(-9.082370867592193 28.242454005495436) | 155 | 6 +POINT(9.394940837974781 28.953888530174837) | 46 | 5 +POINT(-1.6692755030477562 17.78088210212057) | 197 | 4 +POINT(-26.976065734634176 42.907839377294295) | 24 | 3 +POINT(1.2588642098541771 24.379140841774642) | 63 | 2 +; + +centroidFromAirportsGroupedAfterMvExpandFiltered#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| WHERE scalerank == 9 +| MV_EXPAND type +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +; + +centroid:geo_point | count:long | scalerank:i +POINT(83.16847535921261 28.79002037679311) | 40 | 9 +; + +centroidFromAirportsAfterMvExpandFiltered#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports +| WHERE scalerank == 9 +| MV_EXPAND type +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:geo_point | count:long +POINT(83.16847535921261 28.79002037679311) | 40 ; geoPointEquals#[skip:-8.12.99, reason:spatial type geo_point improved in 8.13] @@ -124,15 +357,53 @@ convertCartesianFromStringArray#[skip:-8.12.99, reason:spatial type cartesian_po row wkt = ["POINT(4297.11 -1475.53)", "POINT(7580.93 2272.77)"] | eval pt = to_cartesianpoint(wkt); -wkt:keyword |pt:cartesian_point +wkt:keyword |pt:cartesian_point ["POINT(4297.11 -1475.53)", "POINT(7580.93 2272.77)"] |[POINT(4297.11 -1475.53), POINT(7580.93 2272.77)] ; +centroidCartesianFromStringNested#[skip:-8.12.99, reason:st_centroid added in 8.13] +row wkt = "POINT(4297.10986328125 -1475.530029296875)" +| STATS c = ST_CENTROID(TO_CARTESIANPOINT(wkt)); + +c:cartesian_point +POINT(4297.10986328125 -1475.530029296875) +; + +centroidFromCartesianString1#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(4297.10986328125 -1475.530029296875)"] +| MV_EXPAND wkt +| EVAL pt = TO_CARTESIANPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:cartesian_point +POINT(4297.10986328125 -1475.530029296875) +; + +centroidFromCartesianString2#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(4297.10986328125 -1475.530029296875)", "POINT(7580.93017578125 2272.77001953125)"] +| MV_EXPAND wkt +| EVAL pt = TO_CARTESIANPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:cartesian_point +POINT(5939.02001953125 398.6199951171875) +; + +centroidFromCartesianString3#[skip:-8.12.99, reason:st_centroid added in 8.13] +ROW wkt = ["POINT(4297.10986328125 -1475.530029296875)", "POINT(7580.93017578125 2272.77001953125)", "POINT(-30.548143003023033 2437.553649504829)"] +| MV_EXPAND wkt +| EVAL pt = TO_CARTESIANPOINT(wkt) +| STATS c = ST_CENTROID(pt); + +c:cartesian_point +POINT(3949.163965353159 1078.2645465797348) +; + simpleCartesianLoad#[skip:-8.12.99, reason:spatial type cartesian_point improved precision in 8.13] FROM airports_web | WHERE scalerank == 9 | SORT abbrev | WHERE length(name) > 12; abbrev:keyword | location:cartesian_point | name:text | scalerank:i | type:k -CJJ | POINT(14192780.461221408 4400430.851323913) | Cheongju Int'l | 9 | major +CJJ | POINT (14192780.461221408 4400430.851323913) | Cheongju Int'l | 9 | major HOD | POINT (4783520.559160681 1661010.0197476079) | Hodeidah Int'l | 9 | mid IDR | POINT (8439051.727244465 2599127.5424638605) | Devi Ahilyabai Holkar Int'l | 9 | mid OMS | POINT (8161539.810548711 7353650.845101996) | Omsk Tsentralny | 9 | mid @@ -142,6 +413,80 @@ WIIT | POINT (11708145.489503577 -584415.9142832769) | Radin Inten II ZAH | POINT (6779435.866395892 3436280.545331025) | Zahedan Int'l | 9 | mid ; +cartesianCentroidFromAirports#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| STATS centroid=ST_CENTROID(location); + +centroid:cartesian_point +POINT(-266681.67563861894 3053301.5120195406) +; + +cartesianCentroidFromAirportsNested#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| STATS centroid=ST_CENTROID(TO_CARTESIANPOINT(location)); + +centroid:cartesian_point +POINT (-266681.66530554957 3053301.506061676) +; + +cartesianCentroidFromAirportsCount#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:cartesian_point | count:long +POINT(-266681.67563861894 3053301.5120195406) | 849 +; + +cartesianCentroidFromAirportsCountGrouped#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +| SORT scalerank DESC +; + +centroid:cartesian_point | count:long | scalerank:i +POINT(9289013.153846154 3615537.0533353365) | 26 | 9 +POINT(-1729861.3231565242 3781377.2572642546) | 228 | 8 +POINT(2001203.8796622984 1588548.3963268648) | 124 | 7 +POINT(-1417910.8585910928 3496034.7206865167) | 147 | 6 +POINT(1045839.9891304348 3582296.444293478) | 46 | 5 +POINT(-513618.93804196664 2279874.075660586) | 191 | 4 +POINT(-3002961.9270833335 5451641.91796875) | 24 | 3 +POINT(140136.12878224207 3081220.7881944445) | 63 | 2 +; + +cartesianCentroidFromAirportsFiltered#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| WHERE scalerank == 9 +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:cartesian_point | count:long +POINT(9289013.153846154 3615537.0533353365) | 26 +; + +cartesianCentroidFromAirportsFilteredAndSorted#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| WHERE scalerank == 9 +| SORT abbrev +| WHERE length(name) > 12 +| STATS centroid=ST_CENTROID(location), count=COUNT() +; + +centroid:cartesian_point | count:long +POINT(9003597.4375 3429344.0078125) | 8 +; + +cartesianCentroidFromAirportsCountGroupedCentroid#[skip:-8.12.99, reason:st_centroid added in 8.13] +FROM airports_web +| STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank +| STATS centroid=ST_CENTROID(centroid), count=SUM(count) +; + +centroid:cartesian_point | count:long +POINT (726480.0130685265 3359566.331716279) | 849 +; + cartesianPointEquals#[skip:-8.12.99, reason:spatial type cartesian_point improved in 8.13] // tag::to_cartesianpoint-equals[] ROW wkt = ["POINT(4297.11 -1475.53)", "POINT(7580.93 2272.77)"] diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java index 73a10acb74739..1115329fd2c73 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichLookupService.java @@ -281,7 +281,8 @@ private void doLookup( ); BlockLoader loader = ctx.blockLoader( extractField instanceof Alias a ? ((NamedExpression) a.child()).name() : extractField.name(), - EsqlDataTypes.isUnsupported(extractField.dataType()) + EsqlDataTypes.isUnsupported(extractField.dataType()), + MappedFieldType.FieldExtractPreference.NONE ); fields.add( new ValuesSourceReaderOperator.FieldInfo( diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/EsqlTypeResolutions.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/EsqlTypeResolutions.java index d47ccf11c9985..e774ba36b16e6 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/EsqlTypeResolutions.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/EsqlTypeResolutions.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.esql.expression; +import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.expression.FieldAttribute; import org.elasticsearch.xpack.ql.expression.TypeResolutions; @@ -17,6 +18,7 @@ import static org.elasticsearch.common.logging.LoggerMessageFormat.format; import static org.elasticsearch.xpack.ql.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isType; public class EsqlTypeResolutions { @@ -42,4 +44,8 @@ public static Expression.TypeResolution isExact(Expression e, String operationNa } return Expression.TypeResolution.TYPE_RESOLVED; } + + public static Expression.TypeResolution isSpatialPoint(Expression e, String operationName, TypeResolutions.ParamOrdinal paramOrd) { + return isType(e, EsqlDataTypes::isSpatialPoint, operationName, paramOrd, "geo_point or cartesian_point"); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java index b3229f1c36c2b..fa00ec5430657 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.MedianAbsoluteDeviation; import org.elasticsearch.xpack.esql.expression.function.aggregate.Min; import org.elasticsearch.xpack.esql.expression.function.aggregate.Percentile; +import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialCentroid; import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Greatest; @@ -169,6 +170,8 @@ private FunctionDefinition[][] functions() { def(DateParse.class, DateParse::new, "date_parse"), def(DateTrunc.class, DateTrunc::new, "date_trunc"), def(Now.class, Now::new, "now") }, + // spatial + new FunctionDefinition[] { def(SpatialCentroid.class, SpatialCentroid::new, "st_centroid") }, // conditional new FunctionDefinition[] { def(Case.class, Case::new, "case") }, // null diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SpatialCentroid.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SpatialCentroid.java new file mode 100644 index 0000000000000..6ce07a272711b --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SpatialCentroid.java @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +package org.elasticsearch.xpack.esql.expression.function.aggregate; + +import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier; +import org.elasticsearch.compute.aggregation.spatial.SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier; +import org.elasticsearch.compute.aggregation.spatial.SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier; +import org.elasticsearch.compute.aggregation.spatial.SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier; +import org.elasticsearch.compute.aggregation.spatial.SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier; +import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; +import org.elasticsearch.xpack.esql.planner.ToAggregator; +import org.elasticsearch.xpack.esql.type.EsqlDataTypes; +import org.elasticsearch.xpack.ql.expression.Expression; +import org.elasticsearch.xpack.ql.expression.function.aggregate.SpatialAggregateFunction; +import org.elasticsearch.xpack.ql.tree.NodeInfo; +import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; + +import java.util.List; + +import static org.elasticsearch.xpack.esql.expression.EsqlTypeResolutions.isSpatialPoint; +import static org.elasticsearch.xpack.ql.expression.TypeResolutions.ParamOrdinal.DEFAULT; + +/** + * Calculate spatial centroid of all geo_point or cartesian point values of a field in matching documents. + */ +public class SpatialCentroid extends SpatialAggregateFunction implements ToAggregator { + + @FunctionInfo(returnType = { "geo_point", "cartesian_point" }, description = "The centroid of a spatial field.", isAggregation = true) + public SpatialCentroid(Source source, @Param(name = "field", type = { "geo_point", "cartesian_point" }) Expression field) { + super(source, field, false); + } + + private SpatialCentroid(Source source, Expression field, boolean useDocValues) { + super(source, field, useDocValues); + } + + @Override + public SpatialCentroid withDocValues() { + return new SpatialCentroid(source(), field(), true); + } + + @Override + protected Expression.TypeResolution resolveType() { + // TODO: Support geo_shape and cartesian_shape + return isSpatialPoint(field(), sourceText(), DEFAULT); + } + + @Override + public DataType dataType() { + // We aggregate incoming GEO_POINTs into a single GEO_POINT, or incoming CARTESIAN_POINTs into a single CARTESIAN_POINT. + return field().dataType(); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, SpatialCentroid::new, field()); + } + + @Override + public SpatialCentroid replaceChildren(List newChildren) { + return new SpatialCentroid(source(), newChildren.get(0)); + } + + @Override + public AggregatorFunctionSupplier supplier(List inputChannels) { + DataType type = field().dataType(); + if (useDocValues) { + // When the points are read as doc-values (eg. from the index), feed them into the doc-values aggregator + if (type == EsqlDataTypes.GEO_POINT) { + return new SpatialCentroidGeoPointDocValuesAggregatorFunctionSupplier(inputChannels); + } + if (type == EsqlDataTypes.CARTESIAN_POINT) { + return new SpatialCentroidCartesianPointDocValuesAggregatorFunctionSupplier(inputChannels); + } + } else { + // When the points are read as WKB from source or as point literals, feed them into the source-values aggregator + if (type == EsqlDataTypes.GEO_POINT) { + return new SpatialCentroidGeoPointSourceValuesAggregatorFunctionSupplier(inputChannels); + } + if (type == EsqlDataTypes.CARTESIAN_POINT) { + return new SpatialCentroidCartesianPointSourceValuesAggregatorFunctionSupplier(inputChannels); + } + } + throw EsqlIllegalArgumentException.illegalDataType(type); + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java index f7f7ecd0118dd..0aa867aef9e0e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java @@ -36,6 +36,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.MedianAbsoluteDeviation; import org.elasticsearch.xpack.esql.expression.function.aggregate.Min; import org.elasticsearch.xpack.esql.expression.function.aggregate.Percentile; +import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialCentroid; import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; import org.elasticsearch.xpack.esql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case; @@ -392,6 +393,7 @@ public static List namedTypeEntries() { of(AggregateFunction.class, Median.class, PlanNamedTypes::writeAggFunction, PlanNamedTypes::readAggFunction), of(AggregateFunction.class, MedianAbsoluteDeviation.class, PlanNamedTypes::writeAggFunction, PlanNamedTypes::readAggFunction), of(AggregateFunction.class, Percentile.class, PlanNamedTypes::writePercentile, PlanNamedTypes::readPercentile), + of(AggregateFunction.class, SpatialCentroid.class, PlanNamedTypes::writeAggFunction, PlanNamedTypes::readAggFunction), of(AggregateFunction.class, Sum.class, PlanNamedTypes::writeAggFunction, PlanNamedTypes::readAggFunction), // Multivalue functions of(ScalarFunction.class, MvAvg.class, PlanNamedTypes::writeMvFunction, PlanNamedTypes::readMvFunction), @@ -1602,7 +1604,8 @@ static void writeArithmeticOperation(PlanStreamOutput out, ArithmeticOperation a entry(name(Min.class), Min::new), entry(name(Max.class), Max::new), entry(name(Median.class), Median::new), - entry(name(MedianAbsoluteDeviation.class), MedianAbsoluteDeviation::new) + entry(name(MedianAbsoluteDeviation.class), MedianAbsoluteDeviation::new), + entry(name(SpatialCentroid.class), SpatialCentroid::new) ); static AggregateFunction readAggFunction(PlanStreamInput in, String name) throws IOException { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizer.java index e59d80ed96b76..fb57c5eb6ecbb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizer.java @@ -43,6 +43,7 @@ import org.elasticsearch.xpack.ql.expression.NamedExpression; import org.elasticsearch.xpack.ql.expression.Order; import org.elasticsearch.xpack.ql.expression.TypedAttribute; +import org.elasticsearch.xpack.ql.expression.function.aggregate.SpatialAggregateFunction; import org.elasticsearch.xpack.ql.expression.function.scalar.UnaryScalarFunction; import org.elasticsearch.xpack.ql.expression.predicate.Predicates; import org.elasticsearch.xpack.ql.expression.predicate.logical.BinaryLogic; @@ -62,6 +63,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -113,7 +115,7 @@ protected List> rules(boolean optimizeForEsSource) { var pushdown = new Batch("Push to ES", esSourceRules.toArray(Rule[]::new)); // add the field extraction in just one pass // add it at the end after all the other rules have ran - var fieldExtraction = new Batch<>("Field extraction", Limiter.ONCE, new InsertFieldExtraction()); + var fieldExtraction = new Batch<>("Field extraction", Limiter.ONCE, new InsertFieldExtraction(), new SpatialDocValuesExtraction()); return asList(pushdown, fieldExtraction); } @@ -427,4 +429,52 @@ public static boolean isPushableFieldAttribute(Expression exp, Predicate { + @Override + protected PhysicalPlan rule(AggregateExec aggregate) { + var foundAttributes = new HashSet(); + + PhysicalPlan plan = aggregate.transformDown(UnaryExec.class, exec -> { + if (exec instanceof AggregateExec agg) { + var orderedAggregates = new ArrayList(); + var changedAggregates = false; + for (NamedExpression aggExpr : agg.aggregates()) { + if (aggExpr instanceof Alias as && as.child() instanceof SpatialAggregateFunction af) { + if (af.field() instanceof FieldAttribute fieldAttribute) { + // We need to both mark the field to load differently, and change the spatial function to know to use it + foundAttributes.add(fieldAttribute); + changedAggregates = true; + orderedAggregates.add(as.replaceChild(af.withDocValues())); + } else { + orderedAggregates.add(aggExpr); + } + } else { + orderedAggregates.add(aggExpr); + } + } + if (changedAggregates) { + exec = new AggregateExec( + agg.source(), + agg.child(), + agg.groupings(), + orderedAggregates, + agg.getMode(), + agg.estimatedRowSize() + ); + } + } + if (exec instanceof FieldExtractExec fieldExtractExec) { + // Tell the field extractor that it should extract the field from doc-values instead of source values + for (Attribute found : foundAttributes) { + if (fieldExtractExec.attributesToExtract().contains(found)) { + fieldExtractExec = fieldExtractExec.preferDocValues(found); + } + } + exec = fieldExtractExec; + } + return exec; + }); + return plan; + } + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/FieldExtractExec.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/FieldExtractExec.java index d252385acc89a..e6a0d352462c8 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/FieldExtractExec.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/FieldExtractExec.java @@ -7,23 +7,32 @@ package org.elasticsearch.xpack.esql.plan.physical; +import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.NodeUtils; import org.elasticsearch.xpack.ql.tree.Source; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; public class FieldExtractExec extends UnaryExec implements EstimatesRowSize { private final List attributesToExtract; private final Attribute sourceAttribute; + private final Set preferDocValues; public FieldExtractExec(Source source, PhysicalPlan child, List attributesToExtract) { + this(source, child, attributesToExtract, new HashSet<>()); + } + + public FieldExtractExec(Source source, PhysicalPlan child, List attributesToExtract, Set preferDocValues) { super(source, child); this.attributesToExtract = attributesToExtract; this.sourceAttribute = extractSourceAttributesFrom(child); + this.preferDocValues = preferDocValues; } public static Attribute extractSourceAttributesFrom(PhysicalPlan plan) { @@ -32,12 +41,12 @@ public static Attribute extractSourceAttributesFrom(PhysicalPlan plan) { @Override protected NodeInfo info() { - return NodeInfo.create(this, FieldExtractExec::new, child(), attributesToExtract); + return NodeInfo.create(this, FieldExtractExec::new, child(), attributesToExtract, preferDocValues); } @Override public UnaryExec replaceChild(PhysicalPlan newChild) { - return new FieldExtractExec(source(), newChild, attributesToExtract); + return new FieldExtractExec(source(), newChild, attributesToExtract, preferDocValues); } public List attributesToExtract() { @@ -63,7 +72,7 @@ public PhysicalPlan estimateRowSize(State state) { @Override public int hashCode() { - return Objects.hash(attributesToExtract, child()); + return Objects.hash(attributesToExtract, preferDocValues, child()); } @Override @@ -77,11 +86,28 @@ public boolean equals(Object obj) { } FieldExtractExec other = (FieldExtractExec) obj; - return Objects.equals(attributesToExtract, other.attributesToExtract) && Objects.equals(child(), other.child()); + return Objects.equals(attributesToExtract, other.attributesToExtract) + && Objects.equals(preferDocValues, other.preferDocValues) + && Objects.equals(child(), other.child()); } @Override public String nodeString() { - return nodeName() + NodeUtils.limitedToString(attributesToExtract); + return nodeName() + NodeUtils.limitedToString(attributesToExtract) + NodeUtils.limitedToString(preferDocValues); + } + + public FieldExtractExec preferDocValues(Attribute attr) { + Set newForStats = new HashSet<>(preferDocValues); + newForStats.add(attr); + return new FieldExtractExec(source(), child(), attributesToExtract, newForStats); + } + + /** + * Returns DOC_VALUES if the given attribute should be preferrentially extracted from doc-values. + */ + public MappedFieldType.FieldExtractPreference extractPreference(Attribute attr) { + return preferDocValues.contains(attr) + ? MappedFieldType.FieldExtractPreference.DOC_VALUES + : MappedFieldType.FieldExtractPreference.NONE; } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java index 0bc682edaeef9..c375ef24da829 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.Min; import org.elasticsearch.xpack.esql.expression.function.aggregate.NumericAggregate; import org.elasticsearch.xpack.esql.expression.function.aggregate.Percentile; +import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialCentroid; import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.AttributeMap; @@ -30,6 +31,7 @@ import org.elasticsearch.xpack.ql.expression.ReferenceAttribute; import org.elasticsearch.xpack.ql.expression.function.Function; import org.elasticsearch.xpack.ql.expression.function.aggregate.AggregateFunction; +import org.elasticsearch.xpack.ql.expression.function.aggregate.SpatialAggregateFunction; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; @@ -44,9 +46,13 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.CARTESIAN_POINT; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.GEO_POINT; + public class AggregateMapper { static final List NUMERIC = List.of("Int", "Long", "Double"); + static final List SPATIAL = List.of("GeoPoint", "CartesianPoint"); /** List of all ESQL agg functions. */ static final List> AGG_FUNCTIONS = List.of( @@ -57,11 +63,12 @@ public class AggregateMapper { MedianAbsoluteDeviation.class, Min.class, Percentile.class, + SpatialCentroid.class, Sum.class ); /** Record of agg Class, type, and grouping (or non-grouping). */ - record AggDef(Class aggClazz, String type, boolean grouping) {} + record AggDef(Class aggClazz, String type, String extra, boolean grouping) {} /** Map of AggDef types to intermediate named expressions. */ private final Map> mapper; @@ -131,28 +138,40 @@ private List getNonNull(AggDef aggDef) { return l; } - static Stream, String>> typeAndNames(Class clazz) { + private static Stream, Tuple>> typeAndNames(Class clazz) { List types; + List extraConfigs = List.of(""); if (NumericAggregate.class.isAssignableFrom(clazz)) { types = NUMERIC; } else if (clazz == Count.class) { types = List.of(""); // no extra type distinction + } else if (SpatialAggregateFunction.class.isAssignableFrom(clazz)) { + types = SPATIAL; + extraConfigs = List.of("SourceValues", "DocValues"); } else { assert clazz == CountDistinct.class : "Expected CountDistinct, got: " + clazz; types = Stream.concat(NUMERIC.stream(), Stream.of("Boolean", "BytesRef")).toList(); } - return types.stream().map(type -> new Tuple<>(clazz, type)); + return combinations(types, extraConfigs).map(combo -> new Tuple<>(clazz, combo)); + } + + private static Stream> combinations(List types, List extraConfigs) { + return types.stream().flatMap(type -> extraConfigs.stream().map(config -> new Tuple<>(type, config))); } - static Stream groupingAndNonGrouping(Tuple, String> tuple) { - return Stream.of(new AggDef(tuple.v1(), tuple.v2(), true), new AggDef(tuple.v1(), tuple.v2(), false)); + private static Stream groupingAndNonGrouping(Tuple, Tuple> tuple) { + return Stream.of( + new AggDef(tuple.v1(), tuple.v2().v1(), tuple.v2().v2(), true), + new AggDef(tuple.v1(), tuple.v2().v1(), tuple.v2().v2(), false) + ); } - static AggDef aggDefOrNull(Expression aggregate, boolean grouping) { + private static AggDef aggDefOrNull(Expression aggregate, boolean grouping) { if (aggregate instanceof AggregateFunction aggregateFunction) { return new AggDef( aggregateFunction.getClass(), dataTypeToString(aggregateFunction.field().dataType(), aggregateFunction.getClass()), + aggregate instanceof SpatialCentroid ? "SourceValues" : "", grouping ); } @@ -160,9 +179,9 @@ static AggDef aggDefOrNull(Expression aggregate, boolean grouping) { } /** Retrieves the intermediate state description for a given class, type, and grouping. */ - static List lookupIntermediateState(AggDef aggDef) { + private static List lookupIntermediateState(AggDef aggDef) { try { - return (List) lookup(aggDef.aggClazz(), aggDef.type(), aggDef.grouping()).invokeExact(); + return (List) lookup(aggDef.aggClazz(), aggDef.type(), aggDef.extra(), aggDef.grouping()).invokeExact(); } catch (Throwable t) { // invokeExact forces us to handle any Throwable thrown by lookup. throw new EsqlIllegalArgumentException(t); @@ -170,11 +189,11 @@ static List lookupIntermediateState(AggDef aggDef) { } /** Looks up the intermediate state method for a given class, type, and grouping. */ - static MethodHandle lookup(Class clazz, String type, boolean grouping) { + private static MethodHandle lookup(Class clazz, String type, String extra, boolean grouping) { try { return MethodHandles.lookup() .findStatic( - Class.forName(determineAggName(clazz, type, grouping)), + Class.forName(determineAggName(clazz, type, extra, grouping)), "intermediateStateDesc", MethodType.methodType(List.class) ); @@ -184,24 +203,34 @@ static MethodHandle lookup(Class clazz, String type, boolean grouping) { } /** Determines the engines agg class name, for the given class, type, and grouping. */ - static String determineAggName(Class clazz, String type, boolean grouping) { + private static String determineAggName(Class clazz, String type, String extra, boolean grouping) { StringBuilder sb = new StringBuilder(); - sb.append("org.elasticsearch.compute.aggregation."); + sb.append(determinePackageName(clazz)).append("."); sb.append(clazz.getSimpleName()); sb.append(type); + sb.append(extra); sb.append(grouping ? "Grouping" : ""); sb.append("AggregatorFunction"); return sb.toString(); } + /** Determines the engine agg package name, for the given class. */ + private static String determinePackageName(Class clazz) { + if (clazz.getSimpleName().startsWith("Spatial")) { + // All spatial aggs are in the spatial sub-package + return "org.elasticsearch.compute.aggregation.spatial"; + } + return "org.elasticsearch.compute.aggregation"; + } + /** Maps intermediate state description to named expressions. */ - static Stream isToNE(List intermediateStateDescs) { + private static Stream isToNE(List intermediateStateDescs) { return intermediateStateDescs.stream().map(is -> new ReferenceAttribute(Source.EMPTY, is.name(), toDataType(is.type()))); } /** Returns the data type for the engines element type. */ // defaults to aggstate, but we'll eventually be able to remove this - static DataType toDataType(ElementType elementType) { + private static DataType toDataType(ElementType elementType) { return switch (elementType) { case BOOLEAN -> DataTypes.BOOLEAN; case BYTES_REF -> DataTypes.KEYWORD; @@ -213,7 +242,7 @@ static DataType toDataType(ElementType elementType) { } /** Returns the string representation for the data type. This reflects the engine's aggs naming structure. */ - static String dataTypeToString(DataType type, Class aggClass) { + private static String dataTypeToString(DataType type, Class aggClass) { if (aggClass == Count.class) { return ""; // no type distinction } @@ -227,8 +256,17 @@ static String dataTypeToString(DataType type, Class aggClass) { return "Double"; } else if (type.equals(DataTypes.KEYWORD) || type.equals(DataTypes.IP) || type.equals(DataTypes.TEXT)) { return "BytesRef"; + } else if (type.equals(GEO_POINT)) { + return "GeoPoint"; + } else if (type.equals(CARTESIAN_POINT)) { + return "CartesianPoint"; } else { throw new EsqlIllegalArgumentException("illegal agg type: " + type.typeName()); } } + + private static Expression unwrapAlias(Expression expression) { + if (expression instanceof Alias alias) return alias.child(); + return expression; + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java index 43d02a00c4db4..8a1c82baf45cf 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/EsPhysicalOperationProviders.java @@ -58,6 +58,7 @@ import static org.elasticsearch.common.lucene.search.Queries.newNonNestedFilter; import static org.elasticsearch.compute.lucene.LuceneSourceOperator.NO_LIMIT; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE; public class EsPhysicalOperationProviders extends AbstractPhysicalOperationProviders { /** @@ -77,7 +78,7 @@ public interface ShardContext extends org.elasticsearch.compute.lucene.ShardCont /** * Returns something to load values from this field into a {@link Block}. */ - BlockLoader blockLoader(String name, boolean asUnsupportedSource); + BlockLoader blockLoader(String name, boolean asUnsupportedSource, MappedFieldType.FieldExtractPreference fieldExtractPreference); } private final List shardContexts; @@ -88,9 +89,6 @@ public EsPhysicalOperationProviders(List shardContexts) { @Override public final PhysicalOperation fieldExtractPhysicalOperation(FieldExtractExec fieldExtractExec, PhysicalOperation source) { - // TODO: see if we can get the FieldExtractExec to know if spatial types need to be read from source or doc values, and capture - // that information in the BlockReaderFactories.loaders method so it is passed in the BlockLoaderContext - // to GeoPointFieldMapper.blockLoader Layout.Builder layout = source.layout.builder(); var sourceAttr = fieldExtractExec.sourceAttribute(); List readers = shardContexts.stream() @@ -101,10 +99,11 @@ public final PhysicalOperation fieldExtractPhysicalOperation(FieldExtractExec fi for (Attribute attr : fieldExtractExec.attributesToExtract()) { layout.append(attr); DataType dataType = attr.dataType(); - ElementType elementType = PlannerUtils.toElementType(dataType); + MappedFieldType.FieldExtractPreference fieldExtractPreference = fieldExtractExec.extractPreference(attr); + ElementType elementType = PlannerUtils.toElementType(dataType, fieldExtractPreference); String fieldName = attr.name(); boolean isSupported = EsqlDataTypes.isUnsupported(dataType); - IntFunction loader = s -> shardContexts.get(s).blockLoader(fieldName, isSupported); + IntFunction loader = s -> shardContexts.get(s).blockLoader(fieldName, isSupported, fieldExtractPreference); fields.add(new ValuesSourceReaderOperator.FieldInfo(fieldName, elementType, loader)); } return source.with(new ValuesSourceReaderOperator.Factory(fields, readers, docChannel), layout.build()); @@ -186,7 +185,7 @@ public final Operator.OperatorFactory ordinalGroupingOperatorFactory( // Costin: why are they ready and not already exposed in the layout? boolean isUnsupported = EsqlDataTypes.isUnsupported(attrSource.dataType()); return new OrdinalsGroupingOperator.OrdinalsGroupingOperatorFactory( - shardIdx -> shardContexts.get(shardIdx).blockLoader(attrSource.name(), isUnsupported), + shardIdx -> shardContexts.get(shardIdx).blockLoader(attrSource.name(), isUnsupported, NONE), vsShardContexts, groupElementType, docChannel, @@ -255,7 +254,11 @@ public Query toQuery(QueryBuilder queryBuilder) { } @Override - public BlockLoader blockLoader(String name, boolean asUnsupportedSource) { + public BlockLoader blockLoader( + String name, + boolean asUnsupportedSource, + MappedFieldType.FieldExtractPreference fieldExtractPreference + ) { if (asUnsupportedSource) { return BlockLoader.CONSTANT_NULLS; } @@ -270,6 +273,11 @@ public String indexName() { return ctx.getFullyQualifiedIndex().getName(); } + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return fieldExtractPreference; + } + @Override public SearchLookup lookup() { return ctx.lookup(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/PlannerUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/PlannerUtils.java index 933b0174aebc0..f84b9d3798a4c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/PlannerUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/PlannerUtils.java @@ -13,6 +13,7 @@ import org.elasticsearch.compute.data.BlockFactory; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; @@ -52,6 +53,7 @@ import java.util.function.Predicate; import static java.util.Arrays.asList; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; import static org.elasticsearch.xpack.esql.optimizer.LocalPhysicalPlanOptimizer.PushFiltersToSource.canPushToSource; import static org.elasticsearch.xpack.esql.optimizer.LocalPhysicalPlanOptimizer.TRANSLATOR_HANDLER; import static org.elasticsearch.xpack.ql.util.Queries.Clause.FILTER; @@ -210,6 +212,15 @@ public static ElementType toSortableElementType(DataType dataType) { * Map QL's {@link DataType} to the compute engine's {@link ElementType}. */ public static ElementType toElementType(DataType dataType) { + return toElementType(dataType, MappedFieldType.FieldExtractPreference.NONE); + } + + /** + * Map QL's {@link DataType} to the compute engine's {@link ElementType}. + * Under some situations, the same data type might be extracted into a different element type. + * For example, spatial types can be extracted into doc-values under specific conditions, otherwise they extract as BytesRef. + */ + public static ElementType toElementType(DataType dataType, MappedFieldType.FieldExtractPreference fieldExtractPreference) { if (dataType == DataTypes.LONG || dataType == DataTypes.DATETIME || dataType == DataTypes.UNSIGNED_LONG) { return ElementType.LONG; } @@ -237,7 +248,11 @@ public static ElementType toElementType(DataType dataType) { if (dataType == EsQueryExec.DOC_DATA_TYPE) { return ElementType.DOC; } + if (EsqlDataTypes.isSpatialPoint(dataType)) { + return fieldExtractPreference == DOC_VALUES ? ElementType.LONG : ElementType.BYTES_REF; + } if (EsqlDataTypes.isSpatial(dataType)) { + // TODO: support forStats for shape aggregations, like st_centroid return ElementType.BYTES_REF; } throw EsqlIllegalArgumentException.illegalDataType(dataType); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypes.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypes.java index e8cc5a77291bb..0813069330879 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypes.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypes.java @@ -175,6 +175,10 @@ public static boolean isSpatial(DataType t) { return t == GEO_POINT || t == CARTESIAN_POINT || t == GEO_SHAPE || t == CARTESIAN_SHAPE; } + public static boolean isSpatialPoint(DataType t) { + return t == GEO_POINT || t == CARTESIAN_POINT; + } + /** * Supported types that can be contained in a block. */ diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java index 85612427a1867..6f3991a0e8323 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java @@ -34,6 +34,7 @@ import org.elasticsearch.xpack.esql.expression.function.aggregate.MedianAbsoluteDeviation; import org.elasticsearch.xpack.esql.expression.function.aggregate.Min; import org.elasticsearch.xpack.esql.expression.function.aggregate.Percentile; +import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialCentroid; import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Round; @@ -604,6 +605,7 @@ static AggregateFunction randomAggFunction() { case 6 -> new MedianAbsoluteDeviation(Source.EMPTY, field); case 7 -> new CountDistinct(Source.EMPTY, field, right); case 8 -> new Percentile(Source.EMPTY, field, right); + case 9 -> new SpatialCentroid(Source.EMPTY, field); default -> throw new AssertionError(v); }; } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 50649f79516e5..ed3df60ecf13b 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -92,6 +92,7 @@ import static org.elasticsearch.xpack.esql.EsqlTestUtils.loadMapping; import static org.elasticsearch.xpack.esql.EsqlTestUtils.localSource; import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.GEO_POINT; import static org.elasticsearch.xpack.ql.TestUtils.relation; import static org.elasticsearch.xpack.ql.tree.Source.EMPTY; import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; @@ -117,15 +118,12 @@ public class LogicalPlanOptimizerTests extends ESTestCase { private static Analyzer analyzer; private static LogicalPlanOptimizer logicalOptimizer; private static Map mapping; + private static Map mappingAirports; + private static Analyzer analyzerAirports; @BeforeClass public static void init() { parser = new EsqlParser(); - - mapping = loadMapping("mapping-basic.json"); - EsIndex test = new EsIndex("test", mapping); - IndexResolution getIndexResult = IndexResolution.valid(test); - logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); var enrichResolution = AnalyzerTestUtils.loadEnrichPolicyResolution( "languages_idx", @@ -133,10 +131,24 @@ public static void init() { "languages_idx", "mapping-languages.json" ); + + // Most tests used data from the test index, so we load it here, and use it in the plan() function. + mapping = loadMapping("mapping-basic.json"); + EsIndex test = new EsIndex("test", mapping); + IndexResolution getIndexResult = IndexResolution.valid(test); analyzer = new Analyzer( new AnalyzerContext(EsqlTestUtils.TEST_CFG, new EsqlFunctionRegistry(), getIndexResult, enrichResolution), TEST_VERIFIER ); + + // Some tests use data from the airports index, so we load it here, and use it in the plan_airports() function. + mappingAirports = loadMapping("mapping-airports.json"); + EsIndex airports = new EsIndex("airports", mappingAirports); + IndexResolution getIndexResultAirports = IndexResolution.valid(airports); + analyzerAirports = new Analyzer( + new AnalyzerContext(EsqlTestUtils.TEST_CFG, new EsqlFunctionRegistry(), getIndexResultAirports, enrichResolution), + TEST_VERIFIER + ); } public void testEmptyProjections() { @@ -2762,6 +2774,31 @@ public void testIsNotNullConstraintForAliasedExpressions() { var from = as(eval.child(), EsRelation.class); } + /** + * Expects + * Limit[500[INTEGER]] + * \_Aggregate[[],[SPATIALCENTROID(location{f}#9) AS centroid]] + * \_EsRelation[airports][abbrev{f}#5, location{f}#9, name{f}#6, scalerank{f}..] + */ + public void testSpatialTypesAndStatsUseDocValues() { + var plan = planAirports(""" + from test + | stats centroid = st_centroid(location) + """); + + var limit = as(plan, Limit.class); + var agg = as(limit.child(), Aggregate.class); + assertThat(Expressions.names(agg.aggregates()), contains("centroid")); + assertTrue("Expected GEO_POINT aggregation for STATS", agg.aggregates().stream().allMatch(aggExp -> { + var alias = as(aggExp, Alias.class); + var aggFunc = as(alias.child(), AggregateFunction.class); + var aggField = as(aggFunc.field(), FieldAttribute.class); + return aggField.dataType() == GEO_POINT; + })); + + var from = as(agg.child(), EsRelation.class); + } + /** * Expects * Limit[500[INTEGER]] @@ -2870,6 +2907,14 @@ private LogicalPlan plan(String query) { return optimized; } + private LogicalPlan planAirports(String query) { + var analyzed = analyzerAirports.analyze(parser.createStatement(query)); + // System.out.println(analyzed); + var optimized = logicalOptimizer.optimize(analyzed); + // System.out.println(optimized); + return optimized; + } + private void assertNullLiteral(Expression expression) { assertEquals(Literal.class, expression.getClass()); assertNull(expression.fold()); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java index 6a1bffe22cd7a..00a1bb1bfd0df 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.core.Tuple; +import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.RangeQueryBuilder; @@ -31,8 +32,13 @@ import org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThan; import org.elasticsearch.xpack.esql.evaluator.predicate.operator.comparison.LessThanOrEqual; import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry; +import org.elasticsearch.xpack.esql.expression.function.aggregate.Count; +import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialCentroid; +import org.elasticsearch.xpack.esql.expression.function.aggregate.Sum; +import org.elasticsearch.xpack.esql.expression.function.scalar.convert.ToGeoPoint; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Round; import org.elasticsearch.xpack.esql.parser.EsqlParser; +import org.elasticsearch.xpack.esql.plan.logical.Eval; import org.elasticsearch.xpack.esql.plan.logical.local.LocalSupplier; import org.elasticsearch.xpack.esql.plan.physical.AggregateExec; import org.elasticsearch.xpack.esql.plan.physical.DissectExec; @@ -51,6 +57,7 @@ import org.elasticsearch.xpack.esql.plan.physical.LocalSourceExec; import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan; import org.elasticsearch.xpack.esql.plan.physical.ProjectExec; +import org.elasticsearch.xpack.esql.plan.physical.RowExec; import org.elasticsearch.xpack.esql.plan.physical.TopNExec; import org.elasticsearch.xpack.esql.planner.Mapper; import org.elasticsearch.xpack.esql.planner.PhysicalVerificationException; @@ -60,6 +67,7 @@ import org.elasticsearch.xpack.esql.session.EsqlConfiguration; import org.elasticsearch.xpack.esql.stats.SearchStats; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; +import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.Expressions; import org.elasticsearch.xpack.ql.expression.FieldAttribute; @@ -68,9 +76,16 @@ import org.elasticsearch.xpack.ql.expression.NamedExpression; import org.elasticsearch.xpack.ql.expression.Order; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; +import org.elasticsearch.xpack.ql.expression.function.aggregate.AggregateFunction; +import org.elasticsearch.xpack.ql.expression.function.aggregate.SpatialAggregateFunction; import org.elasticsearch.xpack.ql.expression.predicate.logical.Not; +import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.BinaryComparison; import org.elasticsearch.xpack.ql.index.EsIndex; import org.elasticsearch.xpack.ql.index.IndexResolution; +import org.elasticsearch.xpack.ql.plan.logical.Aggregate; +import org.elasticsearch.xpack.ql.plan.logical.EsRelation; +import org.elasticsearch.xpack.ql.plan.logical.Filter; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.type.EsField; import org.junit.Before; @@ -83,6 +98,7 @@ import static java.util.Arrays.asList; import static org.elasticsearch.core.Tuple.tuple; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.existsQuery; import static org.elasticsearch.xpack.esql.EsqlTestUtils.TEST_VERIFIER; @@ -93,6 +109,8 @@ import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning; import static org.elasticsearch.xpack.esql.SerializationTestUtils.assertSerialization; import static org.elasticsearch.xpack.esql.plan.physical.AggregateExec.Mode.FINAL; +import static org.elasticsearch.xpack.esql.plan.physical.AggregateExec.Mode.PARTIAL; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.GEO_POINT; import static org.elasticsearch.xpack.ql.expression.Expressions.name; import static org.elasticsearch.xpack.ql.expression.Expressions.names; import static org.elasticsearch.xpack.ql.expression.Order.OrderDirection.ASC; @@ -116,12 +134,14 @@ public class PhysicalPlanOptimizerTests extends ESTestCase { private static final int KEYWORD_EST = EstimatesRowSize.estimateSize(DataTypes.KEYWORD); private EsqlParser parser; - private Analyzer analyzer; private LogicalPlanOptimizer logicalOptimizer; private PhysicalPlanOptimizer physicalPlanOptimizer; private Mapper mapper; private Map mapping; + private Analyzer analyzer; private int allFieldRowSize; + private static Map mappingAirports; + private static Analyzer analyzerAirports; private final EsqlConfiguration config; @@ -144,21 +164,6 @@ public PhysicalPlanOptimizerTests(String name, EsqlConfiguration config) { @Before public void init() { parser = new EsqlParser(); - - mapping = loadMapping("mapping-basic.json"); - allFieldRowSize = mapping.values() - .stream() - .mapToInt( - f -> (EstimatesRowSize.estimateSize(EsqlDataTypes.widenSmallNumericTypes(f.getDataType())) + f.getProperties() - .values() - .stream() - // check one more level since the mapping contains TEXT fields with KEYWORD multi-fields - .mapToInt(x -> EstimatesRowSize.estimateSize(EsqlDataTypes.widenSmallNumericTypes(x.getDataType()))) - .sum()) - ) - .sum(); - EsIndex test = new EsIndex("test", mapping); - IndexResolution getIndexResult = IndexResolution.valid(test); logicalOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG)); physicalPlanOptimizer = new PhysicalPlanOptimizer(new PhysicalOptimizerContext(config)); FunctionRegistry functionRegistry = new EsqlFunctionRegistry(); @@ -174,7 +179,32 @@ public void init() { ) ); enrichResolution.addExistingPolicies(Set.of("foo")); + + // Most tests used data from the test index, so we load it here, and use it in the plan() function. + mapping = loadMapping("mapping-basic.json"); + EsIndex test = new EsIndex("test", mapping); + IndexResolution getIndexResult = IndexResolution.valid(test); analyzer = new Analyzer(new AnalyzerContext(config, functionRegistry, getIndexResult, enrichResolution), TEST_VERIFIER); + allFieldRowSize = mapping.values() + .stream() + .mapToInt( + f -> (EstimatesRowSize.estimateSize(EsqlDataTypes.widenSmallNumericTypes(f.getDataType())) + f.getProperties() + .values() + .stream() + // check one more level since the mapping contains TEXT fields with KEYWORD multi-fields + .mapToInt(x -> EstimatesRowSize.estimateSize(EsqlDataTypes.widenSmallNumericTypes(x.getDataType()))) + .sum()) + ) + .sum(); + + // Some tests use data from the airports index, so we load it here, and use it in the plan_airports() function. + mappingAirports = loadMapping("mapping-airports.json"); + EsIndex airports = new EsIndex("airports", mappingAirports); + IndexResolution getIndexResultAirports = IndexResolution.valid(airports); + analyzerAirports = new Analyzer( + new AnalyzerContext(config, functionRegistry, getIndexResultAirports, enrichResolution), + TEST_VERIFIER + ); } public void testSingleFieldExtractor() { @@ -1891,7 +1921,7 @@ public void testAvgSurrogateFunctionAfterRenameAndLimit() { var aggFinal = as(limit.child(), AggregateExec.class); assertThat(aggFinal.getMode(), equalTo(FINAL)); var aggPartial = as(aggFinal.child(), AggregateExec.class); - assertThat(aggPartial.getMode(), equalTo(AggregateExec.Mode.PARTIAL)); + assertThat(aggPartial.getMode(), equalTo(PARTIAL)); limit = as(aggPartial.child(), LimitExec.class); assertThat(limit.limit(), instanceOf(Literal.class)); assertThat(limit.limit().fold(), equalTo(10)); @@ -2041,6 +2071,577 @@ public void testPartialAggFoldingOutputForSyntheticAgg() { assertThat(Expressions.names(source.output()), contains("sum", "seen", "count", "seen")); } + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#9) AS centroid],FINAL,null] + * \_ExchangeExec[[xVal{r}#10, xDel{r}#11, yVal{r}#12, yDel{r}#13, count{r}#14],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[],[SPATIALCENTROID(location{f}#9) AS centroid]] + * \_EsRelation[airports][abbrev{f}#5, location{f}#9, name{f}#6, scalerank{f}..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#9) AS centroid],FINAL,50] + * \_ExchangeExec[[xVal{r}#10, xDel{r}#11, yVal{r}#12, yDel{r}#13, count{r}#14],true] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#9) AS centroid],PARTIAL,50] + * \_FilterExec[ISNOTNULL(location{f}#9)] + * \_FieldExtractExec[location{f}#9][location{f}#9] + * \_EsQueryExec[airports], query[][_doc{f}#26], limit[], sort[] estimatedRowSize[54] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValues() { + var plan = physicalPlanAirports(""" + from airports + | stats centroid = st_centroid(location) + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + as(fAgg.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + // below the exchange (in data node) the aggregation is using doc-values + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, true); + var filter = as(agg.child(), FilterExec.class); + var extract = as(filter.child(), FieldExtractExec.class); + source(extract.child()); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@b54a93a7{r}#10) AS centroid],FINAL,null] + * \_ExchangeExec[[xVal{r}#11, xDel{r}#12, yVal{r}#13, yDel{r}#14, count{r}#15],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@b54a93a7{r}#10) AS centroid]] + * \_Eval[[TOGEOPOINT(location{f}#9) AS __centroid_SPATIALCENTROID@b54a93a7]] + * \_EsRelation[airports][abbrev{f}#5, location{f}#9, name{f}#6, scalerank{f}..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@ad2847b6{r}#10) AS centroid],FINAL,50] + * \_ExchangeExec[[xVal{r}#11, xDel{r}#12, yVal{r}#13, yDel{r}#14, count{r}#15],true] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@ad2847b6{r}#10) AS centroid],PARTIAL,50] + * \_EvalExec[[TOGEOPOINT(location{f}#9) AS __centroid_SPATIALCENTROID@ad2847b6]] + * \_FieldExtractExec[location{f}#9][] + * \_EsQueryExec[airports], query[][_doc{f}#28], limit[], sort[] estimatedRowSize[104] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesNested() { + var plan = physicalPlanAirports(""" + from airports + | stats centroid = st_centroid(to_geopoint(location)) + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + var eval = as(fAgg.child(), Eval.class); + var toGeoPoint = as(eval.fields().get(0).child(), ToGeoPoint.class); + assertThat("Expected point field", toGeoPoint.field().dataType(), equalTo(GEO_POINT)); + as(eval.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + // TODO: Change this to expect to useDocValues for correctly nested reference attributes that relate to functions on fields + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + var evalExec = as(agg.child(), EvalExec.class); + var extract = as(evalExec.child(), FieldExtractExec.class); + source(extract.child()); + // TODO: update this test when we support nested fields in SpatialDocValuesExtraction + // assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> + // { + // MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + // return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + // })); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@ec8dd77e{r}#7) AS centroid],FINAL,null] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@ec8dd77e{r}#7) AS centroid],PARTIAL,null] + * \_EvalExec[[[1 1 0 0 0 0 0 30 e2 4c 7c 45 40 0 0 e0 92 b0 82 2d 40][GEO_POINT] AS __centroid_SPATIALCENTROID@ec8dd77e]] + * \_RowExec[[[50 4f 49 4e 54 28 34 32 2e 39 37 31 30 39 36 32 39 39 35 38 38 36 38 20 31 34 2e 37 35 35 32 35 33 34 30 30 + * 36 35 33 36 29][KEYWORD] AS wkt]] + * + * After local optimizations we expect no changes because field is extracted: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@7ff910a{r}#7) AS centroid],FINAL,50] + * \_AggregateExec[[],[SPATIALCENTROID(__centroid_SPATIALCENTROID@7ff910a{r}#7) AS centroid],PARTIAL,50] + * \_EvalExec[[[1 1 0 0 0 0 0 30 e2 4c 7c 45 40 0 0 e0 92 b0 82 2d 40][GEO_POINT] AS __centroid_SPATIALCENTROID@7ff910a]] + * \_RowExec[[[50 4f 49 4e 54 28 34 32 2e 39 37 31 30 39 36 32 39 39 35 38 38 36 38 20 31 34 2e 37 35 35 32 35 33 34 30 30 + * 36 35 33 36 29][KEYWORD] AS wkt]] + */ + public void testSpatialTypesAndStatsUseDocValuesNestedLiteral() { + var plan = physicalPlanAirports(""" + row wkt = "POINT(42.97109629958868 14.7552534006536)" + | stats centroid = st_centroid(to_geopoint(wkt)) + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + var eval = as(agg.child(), EvalExec.class); + as(eval.child(), RowExec.class); + + // Now optimize the plan and assert the same plan again, since no FieldExtractExec is added + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + eval = as(agg.child(), EvalExec.class); + as(eval.child(), RowExec.class); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#11) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,null] + * \_ExchangeExec[[xVal{r}#12, xDel{r}#13, yVal{r}#14, yDel{r}#15, count{r}#16, count{r}#17, seen{r}#18],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[],[SPATIALCENTROID(location{f}#11) AS centroid, COUNT([2a][KEYWORD]) AS count]] + * \_EsRelation[airports][abbrev{f}#7, location{f}#11, name{f}#8, scalerank{f..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#11) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,58] + * \_ExchangeExec[[xVal{r}#12, xDel{r}#13, yVal{r}#14, yDel{r}#15, count{r}#16, count{r}#17, seen{r}#18],true] + * \_AggregateExec[[],[COUNT([2a][KEYWORD]) AS count, SPATIALCENTROID(location{f}#11) AS centroid],PARTIAL,58] + * \_FieldExtractExec[location{f}#11][location{f}#11] + * \_EsQueryExec[airports], query[][_doc{f}#33], limit[], sort[] estimatedRowSize[54] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesMultiAggregations() { + var plan = physicalPlanAirports(""" + from airports + | stats centroid = st_centroid(location), count = COUNT() + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + as(fAgg.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + // below the exchange (in data node) the aggregation is using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, true); + var extract = as(agg.child(), FieldExtractExec.class); + source(extract.child()); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#14) AS airports, SPATIALCENTROID(city_location{f}#17) AS cities, COUNT([2a][KEY + * WORD]) AS count],FINAL,null] + * \_ExchangeExec[[xVal{r}#18, xDel{r}#19, yVal{r}#20, yDel{r}#21, count{r}#22, xVal{r}#23, xDel{r}#24, yVal{r}#25, yDel{r}#26, + * count{r}#27, count{r}#28, seen{r}#29],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[],[SPATIALCENTROID(location{f}#14) AS airports, SPATIALCENTROID(city_location{f}#17) AS cities, COUNT([2a][KEY + * WORD]) AS count]] + * \_EsRelation[airports][abbrev{f}#10, city{f}#16, city_location{f}#17, coun..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#14) AS airports, SPATIALCENTROID(city_location{f}#17) AS cities, COUNT([2a][KEY + * WORD]) AS count],FINAL,108] + * \_ExchangeExec[[xVal{r}#18, xDel{r}#19, yVal{r}#20, yDel{r}#21, count{r}#22, xVal{r}#23, xDel{r}#24, yVal{r}#25, yDel{r}#26, + * count{r}#27, count{r}#28, seen{r}#29],true] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#14) AS airports, SPATIALCENTROID(city_location{f}#17) AS cities, COUNT([2a][KEY + * WORD]) AS count],PARTIAL,108] + * \_FieldExtractExec[location{f}#14, city_location{f}#17][location{f}#14, city_location{f}#17] + * \_EsQueryExec[airports], query[][_doc{f}#53], limit[], sort[] estimatedRowSize[104] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesMultiSpatialAggregations() { + var plan = physicalPlanAirports(""" + FROM airports + | STATS airports=ST_CENTROID(location), cities=ST_CENTROID(city_location), count=COUNT() + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "airports", SpatialCentroid.class, GEO_POINT, false); + assertAggregation(agg, "cities", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + as(fAgg.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "airports", SpatialCentroid.class, GEO_POINT, false); + assertAggregation(agg, "cities", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + // below the exchange (in data node) the aggregation is using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "airports", SpatialCentroid.class, GEO_POINT, true); + assertAggregation(agg, "cities", SpatialCentroid.class, GEO_POINT, true); + var extract = as(agg.child(), FieldExtractExec.class); + source(extract.child()); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,null] + * \_ExchangeExec[[xVal{r}#13, xDel{r}#14, yVal{r}#15, yDel{r}#16, count{r}#17, count{r}#18, seen{r}#19],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count]] + * \_Filter[scalerank{f}#10 == 9[INTEGER]] + * \_EsRelation[airports][abbrev{f}#8, location{f}#12, name{f}#9, scalerank{f..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(location{f}#11) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,58] + * \_ExchangeExec[[xVal{r}#12, xDel{r}#13, yVal{r}#14, yDel{r}#15, count{r}#16, count{r}#17, seen{r}#18],true] + * \_AggregateExec[[],[COUNT([2a][KEYWORD]) AS count, SPATIALCENTROID(location{f}#11) AS centroid],PARTIAL,58] + * \_FieldExtractExec[location{f}#11][location{f}#11] + * \_EsQueryExec[airports], query[{"esql_single_value":{"field":"scalerank","next":{"term":{"scalerank":{"value":9}}}, + * "source":"scalerank == 9@2:9"}}][_doc{f}#34], limit[], sort[] estimatedRowSize[54] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesMultiAggregationsFiltered() { + var plan = physicalPlanAirports(""" + FROM airports + | WHERE scalerank == 9 + | STATS centroid=ST_CENTROID(location), count=COUNT() + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + var filter = as(fAgg.child(), Filter.class); + assertFilterCondition(filter, Equals.class, "scalerank", 9); + as(filter.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + // below the exchange (in data node) the aggregation is using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, true); + var extract = as(agg.child(), FieldExtractExec.class); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + var source = source(extract.child()); + var qb = as(source.query(), SingleValueQuery.Builder.class); + assertThat("Expected predicate to be passed to Lucene query", qb.source().text(), equalTo("scalerank == 9")); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[scalerank{f}#10],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count, scalerank{f}#10], + * FINAL,null] + * \_ExchangeExec[[scalerank{f}#10, xVal{r}#13, xDel{r}#14, yVal{r}#15, yDel{r}#16, count{r}#17, count{r}#18, seen{r}#19],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[scalerank{f}#10],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count, scalerank{f}#10]] + * \_EsRelation[airports][abbrev{f}#8, location{f}#12, name{f}#9, scalerank{f..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[scalerank{f}#10],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count, scalerank{f}#10], + * FINAL,62] + * \_ExchangeExec[[scalerank{f}#10, xVal{r}#13, xDel{r}#14, yVal{r}#15, yDel{r}#16, count{r}#17, count{r}#18, seen{r}#19],true] + * \_AggregateExec[[scalerank{f}#10],[SPATIALCENTROID(location{f}#12) AS centroid, COUNT([2a][KEYWORD]) AS count, scalerank{f}#10], + * PARTIAL,62] + * \_FieldExtractExec[location{f}#12][location{f}#12] + * \_EsQueryExec[airports], query[][_doc{f}#34], limit[], sort[] estimatedRowSize[54] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesMultiAggregationsGrouped() { + var plan = physicalPlanAirports(""" + FROM airports + | STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("One grouping in aggregation", agg.groupings().size(), equalTo(1)); + var att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + // Before optimization the aggregation does not use doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + as(fAgg.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + // Above the exchange (in coordinator) the aggregation is not using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + // below the exchange (in data node) the aggregation is using doc-values + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, true); + var extract = as(agg.child(), FieldExtractExec.class); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + source(extract.child()); + } + + /** + * Before local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(centroid{r}#4) AS centroid, SUM(count{r}#6) AS count],FINAL,null] + * \_AggregateExec[[],[SPATIALCENTROID(centroid{r}#4) AS centroid, SUM(count{r}#6) AS count],PARTIAL,null] + * \_AggregateExec[[scalerank{f}#16],[SPATIALCENTROID(location{f}#18) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,null] + * \_ExchangeExec[[scalerank{f}#16, xVal{r}#19, xDel{r}#20, yVal{r}#21, yDel{r}#22, count{r}#23, count{r}#24, seen{r}#25],true] + * \_FragmentExec[filter=null, estimatedRowSize=0, fragment=[ + * Aggregate[[scalerank{f}#16],[SPATIALCENTROID(location{f}#18) AS centroid, COUNT([2a][KEYWORD]) AS count]] + * \_EsRelation[airports][abbrev{f}#14, location{f}#18, name{f}#15, scalerank..]]] + * + * After local optimizations: + * + * LimitExec[500[INTEGER]] + * \_AggregateExec[[],[SPATIALCENTROID(centroid{r}#4) AS centroid, SUM(count{r}#6) AS count],FINAL,58] + * \_AggregateExec[[],[SPATIALCENTROID(centroid{r}#4) AS centroid, SUM(count{r}#6) AS count],PARTIAL,58] + * \_AggregateExec[[scalerank{f}#16],[SPATIALCENTROID(location{f}#18) AS centroid, COUNT([2a][KEYWORD]) AS count],FINAL,58] + * \_ExchangeExec[[scalerank{f}#16, xVal{r}#19, xDel{r}#20, yVal{r}#21, yDel{r}#22, count{r}#23, count{r}#24, seen{r}#25],true] + * \_AggregateExec[[scalerank{f}#16],[SPATIALCENTROID(location{f}#18) AS centroid, COUNT([2a][KEYWORD]) AS count],PARTIAL,58] + * \_FieldExtractExec[location{f}#18][location{f}#18] + * \_EsQueryExec[airports], query[][_doc{f}#42], limit[], sort[] estimatedRowSize[54] + * + * Note the FieldExtractExec has 'location' set for stats: FieldExtractExec[location{f}#9][location{f}#9] + */ + public void testSpatialTypesAndStatsUseDocValuesMultiAggregationsGroupedAggregated() { + var plan = physicalPlanAirports(""" + FROM airports + | STATS centroid=ST_CENTROID(location), count=COUNT() BY scalerank + | STATS centroid=ST_CENTROID(centroid), count=SUM(count) + """); + + var limit = as(plan, LimitExec.class); + var agg = as(limit.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "count", Sum.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "count", Sum.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("One grouping in aggregation", agg.groupings().size(), equalTo(1)); + var att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + + var exchange = as(agg.child(), ExchangeExec.class); + var fragment = as(exchange.child(), FragmentExec.class); + var fAgg = as(fragment.fragment(), Aggregate.class); + as(fAgg.child(), EsRelation.class); + + // Now optimize the plan and assert the aggregation uses doc-values + var optimized = optimizedPlan(plan); + limit = as(optimized, LimitExec.class); + agg = as(limit.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "count", Sum.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + assertThat("No groupings in aggregation", agg.groupings().size(), equalTo(0)); + assertAggregation(agg, "count", Sum.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + agg = as(agg.child(), AggregateExec.class); + assertThat("Aggregation is FINAL", agg.getMode(), equalTo(FINAL)); + assertThat("One grouping in aggregation", agg.groupings().size(), equalTo(1)); + att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, false); + exchange = as(agg.child(), ExchangeExec.class); + agg = as(exchange.child(), AggregateExec.class); + assertThat("One grouping in aggregation", agg.groupings().size(), equalTo(1)); + att = as(agg.groupings().get(0), Attribute.class); + assertThat(att.name(), equalTo("scalerank")); + // below the exchange (in data node) the aggregation is using doc-values + assertThat("Aggregation is PARTIAL", agg.getMode(), equalTo(PARTIAL)); + assertAggregation(agg, "count", Count.class); + assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, true); + var extract = as(agg.child(), FieldExtractExec.class); + assertTrue("Expect attributes field extract preference to be DOC_VALUES", extract.attributesToExtract().stream().allMatch(attr -> { + MappedFieldType.FieldExtractPreference extractPreference = extract.extractPreference(attr); + return extractPreference == DOC_VALUES && attr.dataType() == GEO_POINT; + })); + source(extract.child()); + } + + @SuppressWarnings("SameParameterValue") + private static void assertFilterCondition( + Filter filter, + Class conditionClass, + String fieldName, + Object expected + ) { + var condition = as(filter.condition(), conditionClass); + var field = as(condition.left(), FieldAttribute.class); + assertThat("Expected filter field", field.name(), equalTo(fieldName)); + var value = as(condition.right(), Literal.class); + assertThat("Expected filter value", value.value(), equalTo(expected)); + } + + private static void assertAggregation( + PhysicalPlan plan, + String aliasName, + Class aggClass, + DataType fieldType, + boolean useDocValues + ) { + var aggFunc = assertAggregation(plan, aliasName, aggClass); + var aggField = as(aggFunc.field(), Attribute.class); + var spatialAgg = as(aggFunc, SpatialAggregateFunction.class); + assertThat("Expected spatial aggregation to use doc-values", spatialAgg.useDocValues(), equalTo(useDocValues)); + assertThat("", aggField.dataType(), equalTo(fieldType)); + } + + private static AggregateFunction assertAggregation(PhysicalPlan plan, String aliasName, Class aggClass) { + var agg = as(plan, AggregateExec.class); + var aggExp = agg.aggregates().stream().filter(a -> { + var alias = as(a, Alias.class); + return alias.name().equals(aliasName); + }).findFirst().orElseThrow(() -> new AssertionError("Expected aggregation " + aliasName + " not found")); + var alias = as(aggExp, Alias.class); + assertThat(alias.name(), is(aliasName)); + var aggFunc = as(alias.child(), AggregateFunction.class); + assertThat(aggFunc, instanceOf(aggClass)); + return aggFunc; + } + private static EsQueryExec source(PhysicalPlan plan) { if (plan instanceof ExchangeExec exchange) { plan = exchange.child(); @@ -2106,6 +2707,15 @@ private PhysicalPlan physicalPlan(String query) { return physical; } + private PhysicalPlan physicalPlanAirports(String query) { + var logical = logicalOptimizer.optimize(analyzerAirports.analyze(parser.createStatement(query))); + // System.out.println("Logical\n" + logical); + var physical = mapper.map(logical); + // System.out.println(physical); + assertSerialization(physical); + return physical; + } + private List sorts(List orders) { return orders.stream().map(o -> new FieldSort((FieldAttribute) o.child(), o.direction(), o.nullsPosition())).toList(); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java index 8377530b9fbc2..ee8eaf139ea18 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/TestPhysicalOperationProviders.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.esql.planner; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Randomness; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.Describable; @@ -14,10 +15,12 @@ import org.elasticsearch.compute.aggregation.blockhash.BlockHash; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; +import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.DocBlock; import org.elasticsearch.compute.data.DocVector; import org.elasticsearch.compute.data.ElementType; import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.data.Page; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.HashAggregationOperator; @@ -25,6 +28,7 @@ import org.elasticsearch.compute.operator.OrdinalsGroupingOperator; import org.elasticsearch.compute.operator.SourceOperator; import org.elasticsearch.compute.operator.SourceOperator.SourceOperatorFactory; +import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.TestBlockFactory; import org.elasticsearch.xpack.esql.plan.physical.AggregateExec; @@ -32,15 +36,21 @@ import org.elasticsearch.xpack.esql.plan.physical.FieldExtractExec; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner.LocalExecutionPlannerContext; import org.elasticsearch.xpack.esql.planner.LocalExecutionPlanner.PhysicalOperation; +import org.elasticsearch.xpack.esql.type.EsqlDataTypes; import org.elasticsearch.xpack.ql.expression.Attribute; +import org.elasticsearch.xpack.ql.type.DataType; +import org.elasticsearch.xpack.ql.util.SpatialCoordinateTypes; import java.util.List; import java.util.Random; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.IntStream; import static com.carrotsearch.randomizedtesting.generators.RandomNumbers.randomIntBetween; import static java.util.stream.Collectors.joining; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE; public class TestPhysicalOperationProviders extends AbstractPhysicalOperationProviders { @@ -58,7 +68,7 @@ public PhysicalOperation fieldExtractPhysicalOperation(FieldExtractExec fieldExt PhysicalOperation op = source; for (Attribute attr : fieldExtractExec.attributesToExtract()) { layout.append(attr); - op = op.with(new TestFieldExtractOperatorFactory(attr.name()), layout.build()); + op = op.with(new TestFieldExtractOperatorFactory(attr, fieldExtractExec.extractPreference(attr)), layout.build()); } return op; } @@ -148,14 +158,18 @@ private class TestFieldExtractOperator implements Operator { private Page lastPage; boolean finished; String columnName; + private final DataType dataType; + private final MappedFieldType.FieldExtractPreference extractPreference; - TestFieldExtractOperator(String columnName) { + TestFieldExtractOperator(String columnName, DataType dataType, MappedFieldType.FieldExtractPreference extractPreference) { this.columnName = columnName; + this.dataType = dataType; + this.extractPreference = extractPreference; } @Override public void addInput(Page page) { - Block block = extractBlockForColumn(page, columnName); + Block block = extractBlockForColumn(page, columnName, dataType, extractPreference); lastPage = page.appendBlock(block); } @@ -188,13 +202,10 @@ public void close() { } private class TestFieldExtractOperatorFactory implements Operator.OperatorFactory { - - final String columnName; final Operator op; - TestFieldExtractOperatorFactory(String columnName) { - this.columnName = columnName; - this.op = new TestFieldExtractOperator(columnName); + TestFieldExtractOperatorFactory(Attribute attr, MappedFieldType.FieldExtractPreference extractPreference) { + this.op = new TestFieldExtractOperator(attr.name(), attr.dataType(), extractPreference); } @Override @@ -224,7 +235,7 @@ private class TestHashAggregationOperator extends HashAggregationOperator { @Override protected Page wrapPage(Page page) { - return page.appendBlock(extractBlockForColumn(page, columnName)); + return page.appendBlock(extractBlockForColumn(page, columnName, null, NONE)); } } @@ -280,7 +291,12 @@ public String describe() { } } - private Block extractBlockForColumn(Page page, String columnName) { + private Block extractBlockForColumn( + Page page, + String columnName, + DataType dataType, + MappedFieldType.FieldExtractPreference extractPreference + ) { var columnIndex = -1; // locate the block index corresponding to "columnName" for (int i = 0, size = columnNames.size(); i < size && columnIndex < 0; i++) { @@ -294,12 +310,84 @@ private Block extractBlockForColumn(Page page, String columnName) { DocBlock docBlock = page.getBlock(0); IntVector docIndices = docBlock.asVector().docs(); Block originalData = testData.getBlock(columnIndex); - Block.Builder builder = originalData.elementType() - .newBlockBuilder(docIndices.getPositionCount(), TestBlockFactory.getNonBreakingInstance()); - for (int c = 0; c < docIndices.getPositionCount(); c++) { - int doc = docIndices.getInt(c); - builder.copyFrom(originalData, doc, doc + 1); + var blockCopier = shouldMapToDocValues(dataType, extractPreference) + ? TestSpatialPointStatsBlockCopier.create(docIndices, dataType) + : new TestBlockCopier(docIndices); + return blockCopier.copyBlock(originalData); + } + + private boolean shouldMapToDocValues(DataType dataType, MappedFieldType.FieldExtractPreference extractPreference) { + return extractPreference == DOC_VALUES && EsqlDataTypes.isSpatialPoint(dataType); + } + + private static class TestBlockCopier { + + protected final IntVector docIndices; + + private TestBlockCopier(IntVector docIndices) { + this.docIndices = docIndices; + } + + protected Block copyBlock(Block originalData) { + try ( + Block.Builder builder = originalData.elementType() + .newBlockBuilder(docIndices.getPositionCount(), TestBlockFactory.getNonBreakingInstance()) + ) { + for (int c = 0; c < docIndices.getPositionCount(); c++) { + int doc = docIndices.getInt(c); + builder.copyFrom(originalData, doc, doc + 1); + } + return builder.build(); + } + } + } + + /** + * geo_point and cartesian_point are normally loaded as WKT from source, but for aggregations we can load them as doc-values + * which are encoded Long values. This class is used to convert the test loaded WKB into encoded longs for the aggregators. + * TODO: We need a different solution to support geo_shape and cartesian_shape + */ + private abstract static class TestSpatialPointStatsBlockCopier extends TestBlockCopier { + + private TestSpatialPointStatsBlockCopier(IntVector docIndices) { + super(docIndices); + } + + protected abstract long encode(BytesRef wkb); + + @Override + protected Block copyBlock(Block originalData) { + BytesRef scratch = new BytesRef(100); + BytesRefBlock bytesRefBlock = (BytesRefBlock) originalData; + try (LongBlock.Builder builder = bytesRefBlock.blockFactory().newLongBlockBuilder(docIndices.getPositionCount())) { + for (int c = 0; c < docIndices.getPositionCount(); c++) { + int doc = docIndices.getInt(c); + int count = bytesRefBlock.getValueCount(doc); + int i = bytesRefBlock.getFirstValueIndex(doc); + if (count == 0) { + builder.appendNull(); + } else { + for (int v = 0; v < count; v++) { + builder.appendLong(encode(bytesRefBlock.getBytesRef(i, scratch))); + } + } + } + return builder.build(); + } + } + + private static TestSpatialPointStatsBlockCopier create(IntVector docIndices, DataType dataType) { + Function encoder = switch (dataType.esType()) { + case "geo_point" -> SpatialCoordinateTypes.GEO::wkbAsLong; + case "cartesian_point" -> SpatialCoordinateTypes.CARTESIAN::wkbAsLong; + default -> throw new IllegalArgumentException("Unsupported spatial data type: " + dataType); + }; + return new TestSpatialPointStatsBlockCopier(docIndices) { + @Override + protected long encode(BytesRef wkb) { + return encoder.apply(wkb); + } + }; } - return builder.build(); } } diff --git a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java index e2cf8c3014604..1152d93f66b38 100644 --- a/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java +++ b/x-pack/plugin/mapper-constant-keyword/src/test/java/org/elasticsearch/xpack/constantkeyword/mapper/ConstantKeywordFieldMapperTests.java @@ -240,6 +240,11 @@ public String indexName() { throw new UnsupportedOperationException(); } + @Override + public MappedFieldType.FieldExtractPreference fieldExtractPreference() { + return MappedFieldType.FieldExtractPreference.NONE; + } + @Override public SearchLookup lookup() { throw new UnsupportedOperationException(); @@ -309,7 +314,7 @@ protected IngestScriptSupport ingestScriptSupport() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> ((BytesRef) v).utf8ToString(); } diff --git a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java index cd8fdf7f89fbd..95fe8f0a530ba 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java +++ b/x-pack/plugin/mapper-unsigned-long/src/test/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongFieldMapperTests.java @@ -367,7 +367,7 @@ protected IngestScriptSupport ingestScriptSupport() { } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> { // Numbers are in the block as a long but the test needs to compare them to their BigInteger value parsed from xcontent. if (v instanceof BigInteger ul) { diff --git a/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java b/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java index 376263d5cfc99..5653ed7f4302f 100644 --- a/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java +++ b/x-pack/plugin/mapper-version/src/test/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapperTests.java @@ -18,7 +18,6 @@ import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentParsingException; import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperTestCase; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; @@ -189,7 +188,7 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> new Version((BytesRef) v).toString(); } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/aggregate/SpatialAggregateFunction.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/aggregate/SpatialAggregateFunction.java new file mode 100644 index 0000000000000..e73d0e71eb246 --- /dev/null +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/aggregate/SpatialAggregateFunction.java @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.ql.expression.function.aggregate; + +import org.elasticsearch.xpack.ql.expression.Expression; +import org.elasticsearch.xpack.ql.tree.Source; + +import java.util.Objects; + +/** + * All spatial aggregate functions extend this class to enable the planning of reading from doc values for higher performance. + * The AggregateMapper class will generate multiple aggregation functions for each combination, allowing the planner to + * select the best one. + */ +public abstract class SpatialAggregateFunction extends AggregateFunction { + protected final boolean useDocValues; + + protected SpatialAggregateFunction(Source source, Expression field, boolean useDocValues) { + super(source, field); + this.useDocValues = useDocValues; + } + + public abstract SpatialAggregateFunction withDocValues(); + + @Override + public int hashCode() { + // NB: the hashcode is currently used for key generation so + // to avoid clashes between aggs with the same arguments, add the class name as variation + return Objects.hash(getClass(), children(), useDocValues); + } + + @Override + public boolean equals(Object obj) { + if (super.equals(obj)) { + SpatialAggregateFunction other = (SpatialAggregateFunction) obj; + return Objects.equals(other.field(), field()) + && Objects.equals(other.parameters(), parameters()) + && Objects.equals(other.useDocValues, useDocValues); + } + return false; + } + + public boolean useDocValues() { + return useDocValues; + } +} diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/tree/NodeSubclassTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/tree/NodeSubclassTests.java index a62acab36bdff..45c2d591724cc 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/tree/NodeSubclassTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/tree/NodeSubclassTests.java @@ -52,6 +52,7 @@ import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -187,16 +188,15 @@ public void testReplaceChildren() throws Exception { continue; } - List originalList = (List) originalArgValue; - - if (node.children().equals(originalList)) { + if (col instanceof List originalList && node.children().equals(originalList)) { // The arg we're looking at *is* the children @SuppressWarnings("unchecked") // we pass a reasonable type so get reasonable results List newChildren = (List) makeListOfSameSizeOtherThan(changedArgType, originalList); B transformed = node.replaceChildren(newChildren); assertTransformedOrReplacedChildren(node, transformed, ctor, nodeCtorArgs, changedArgOffset, newChildren); - } else if (false == originalList.isEmpty() && node.children().containsAll(originalList)) { + } else if (false == col.isEmpty() && node.children().containsAll(col)) { // The arg we're looking at is a collection contained within the children + List originalList = (List) originalArgValue; // First make the new children @SuppressWarnings("unchecked") // we pass a reasonable type so get reasonable results @@ -369,6 +369,9 @@ private Object makeArg(Class> toBuildClass, Type argType) thro if (pt.getRawType() == List.class) { return makeList(toBuildClass, pt); } + if (pt.getRawType() == Set.class) { + return makeSet(toBuildClass, pt); + } if (pt.getRawType() == EnumSet.class) { @SuppressWarnings("rawtypes") Enum enm = (Enum) makeArg(toBuildClass, pt.getActualTypeArguments()[0]); @@ -565,6 +568,18 @@ private List makeList(Class> toBuildClass, ParameterizedTyp return list; } + private Set makeSet(Class> toBuildClass, ParameterizedType listType) throws Exception { + return makeSet(toBuildClass, listType, randomSizeForCollection(toBuildClass)); + } + + private Set makeSet(Class> toBuildClass, ParameterizedType listType, int size) throws Exception { + Set list = new HashSet<>(); + for (int i = 0; i < size; i++) { + list.add(makeArg(toBuildClass, listType.getActualTypeArguments()[0])); + } + return list; + } + private Object makeMap(Class> toBuildClass, ParameterizedType pt) throws Exception { Map map = new HashMap<>(); int size = randomSizeForCollection(toBuildClass); diff --git a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/PointFieldMapperTests.java b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/PointFieldMapperTests.java index d9309cfb16a4c..b2b250c6d81bd 100644 --- a/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/PointFieldMapperTests.java +++ b/x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/index/mapper/PointFieldMapperTests.java @@ -428,8 +428,10 @@ protected IngestScriptSupport ingestScriptSupport() { } @Override - protected boolean supportsColumnAtATimeReader(MapperService mapper, MappedFieldType ft) { - // Currently ESQL support for cartesian_point is limited to source values - return false; + protected BlockReaderSupport getSupportedReaders(MapperService mapper, String loaderFieldName) { + // TODO: Support testing both reading from source as well as reading from doc-values + MappedFieldType ft = mapper.fieldType(loaderFieldName); + PointFieldMapper.PointFieldType point = (PointFieldMapper.PointFieldType) ft; + return new BlockReaderSupport(point.isIndexed() == false && ft.hasDocValues(), false, mapper, loaderFieldName); } } diff --git a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java index cdb14c348bbf8..a17cb7474a681 100644 --- a/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java +++ b/x-pack/plugin/wildcard/src/test/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapperTests.java @@ -60,7 +60,6 @@ import org.elasticsearch.index.mapper.LuceneDocument; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperBuilderContext; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperTestCase; import org.elasticsearch.index.mapper.Mapping; import org.elasticsearch.index.mapper.MappingLookup; @@ -1218,7 +1217,7 @@ protected SyntheticSourceSupport syntheticSourceSupport(boolean ignoreMalformed) } @Override - protected Function loadBlockExpected(MapperService mapper, String loaderFieldName) { + protected Function loadBlockExpected() { return v -> ((BytesRef) v).utf8ToString(); }