1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.index.index.s2
10 
11 import org.geotools.api.feature.simple.SimpleFeatureType
12 import org.geotools.api.filter.Filter
13 import org.geotools.util.factory.Hints
14 import org.locationtech.geomesa.curve.S2SFC
15 import org.locationtech.geomesa.filter.{FilterHelper, FilterValues}
16 import org.locationtech.geomesa.index.api
17 import org.locationtech.geomesa.index.api.IndexKeySpace.IndexKeySpaceFactory
18 import org.locationtech.geomesa.index.api.ShardStrategy.{NoShardStrategy, Z2ShardStrategy}
19 import org.locationtech.geomesa.index.api._
20 import org.locationtech.geomesa.index.conf.QueryHints.LOOSE_BBOX
21 import org.locationtech.geomesa.index.conf.QueryProperties
22 import org.locationtech.geomesa.index.geotools.GeoMesaDataStoreFactory
23 import org.locationtech.geomesa.index.utils.Explainer
24 import org.locationtech.geomesa.utils.geotools.{GeometryUtils, WholeWorldPolygon}
25 import org.locationtech.geomesa.utils.index.{ByteArrays, VisibilityLevel}
26 import org.locationtech.jts.geom.{Geometry, Point}
27 
28 import scala.util.control.NonFatal
29 
30 /**
31   * @author sunyabo 2019年07月24日 14:42
32   * @version V1.0
33   */
34 class S2IndexKeySpace(val sft: SimpleFeatureType, val sharding: ShardStrategy, geomField: String)
35   extends IndexKeySpace[S2IndexValues, Long] {
36 
37   import org.locationtech.geomesa.utils.geotools.RichSimpleFeatureType.RichSimpleFeatureType
38 
39   require(classOf[Point].isAssignableFrom(sft.getDescriptor(geomField).getType.getBinding),
40     s"Expected field $geomField to have a point binding, but instead it has: " +
41       sft.getDescriptor(geomField).getType.getBinding.getSimpleName)
42 
43   private val sfc: S2SFC =
44     S2SFC(
45       QueryProperties.S2MinLevel,
46       QueryProperties.S2MaxLevel,
47       QueryProperties.S2LevelMod,
48       QueryProperties.S2MaxCells
49     )
50 
51   private val geomIndex: Int = sft.indexOf(geomField)
52 
53   /**
54     * The attributes used to create the index keys
55     *
56     * @return
57     */
58   override val attributes: Seq[String] = Seq(geomField)
59 
60   /**
61     * Length of an index key. If static (general case), will return a Right with the length. If dynamic,
62     * will return Left with a function to determine the length from a given (row, offset, length)
63     *
64     * @return
65     */
66   override val indexKeyByteLength: Right[(Array[Byte], Int, Int) => Int, Int] = Right(8 + sharding.length)
67 
68   /**
69     * Table sharing
70     *
71     * @return
72     */
73   override val sharing: Array[Byte] = Array.empty
74 
75   /**
76     * Index key from the attributes of a simple feature
77     *
78     * @param writable simple feature with cached values
79     * @param tier    tier bytes
80     * @param id      feature id bytes
81     * @param lenient if input values should be strictly checked, default false
82     * @return
83     */
84   override def toIndexKey(writable: WritableFeature,
85                           tier: Array[Byte],
86                           id: Array[Byte],
87                           lenient: Boolean): RowKeyValue[Long] = {
88     val geom = writable.getAttribute[Point](geomIndex)
89     if (geom == null) {
90       throw new IllegalArgumentException(s"Null geometry in feature ${writable.feature.getID}")
91     }
92     val s = try { sfc.index(geom.getX, geom.getY, lenient) } catch {
93       case NonFatal(e) => throw new IllegalArgumentException(s"Invalid s value from geometry: $geom", e)
94     }
95     val shard = sharding(writable)
96 
97     // create the byte array - allocate a single array up front to contain everything
98     // ignore tier, not used here
99     val bytes = Array.ofDim[Byte](shard.length + 8 + id.length)
100 
101     if (shard.isEmpty) {
102       ByteArrays.writeLong(s, bytes)
103       System.arraycopy(id, 0, bytes, 8, id.length)
104     } else {
105       bytes(0) = shard.head // shard is only a single byte
106       ByteArrays.writeLong(s, bytes, 1)
107       System.arraycopy(id, 0, bytes, 9, id.length)
108     }
109 
110     SingleRowKeyValue(bytes, sharing, shard, s, tier, id, writable.values)
111   }
112 
113   /**
114     * Extracts values out of the filter used for range and push-down predicate creation
115     *
116     * @param filter  query filter
117     * @param explain explainer
118     * @return
119     */
120   override def getIndexValues(filter: Filter, explain: Explainer): S2IndexValues = {
121 
122     val geometries: FilterValues[Geometry] = {
123       val extracted = FilterHelper.extractGeometries(filter, geomField) // intersect since we have points
124       if (extracted.nonEmpty) { extracted } else { FilterValues(Seq(WholeWorldPolygon)) }
125     }
126 
127     explain(s"Geometries: $geometries")
128 
129     if (geometries.disjoint) {
130       explain("Non-intersecting geometries extracted, short-circuiting to empty query")
131       return S2IndexValues(sfc, geometries, Seq.empty)
132     }
133 
134     // compute our ranges based on the coarse bounds for our query
135     val xy: Seq[(Double, Double, Double, Double)] = {
136       val multiplier = QueryProperties.PolygonDecompMultiplier.toInt.get
137       val bits = QueryProperties.PolygonDecompBits.toInt.get
138       geometries.values.flatMap(GeometryUtils.bounds(_, multiplier, bits))
139     }
140 
141     S2IndexValues(sfc, geometries, xy)
142   }
143 
144   /**
145     * Creates ranges over the index keys
146     *
147     * @param values     index values @see getIndexValues
148     * @param multiplier hint for how many times the ranges will be multiplied. can be used to
149     *                   inform the number of ranges generated
150     * @return
151     */
152   override def getRanges(values: S2IndexValues, multiplier: Int): Iterator[ScanRange[Long]] = {
153     val S2IndexValues(_, geoms, xy) = values
154     if (geoms.disjoint) {
155       Iterator.empty
156     } else {
157       // note: `target` will always be Some, as ScanRangesTarget has a default value
158       val target = QueryProperties.ScanRangesTarget.option.map(t => math.max(1, t.toInt / multiplier))
159       sfc.ranges(xy, -1, target).iterator.map(r => BoundedRange(r.lower, r.upper))
160     }
161   }
162 
163   /**
164     * Creates bytes from ranges
165     *
166     * @param ranges typed scan ranges. @see `getRanges`
167     * @param tier   will the ranges have tiered ranges appended, or not
168     * @return
169     */
170   override def getRangeBytes(ranges: Iterator[ScanRange[Long]], tier: Boolean): Iterator[api.ByteRange] = {
171     if (sharding.length == 0) {
172       ranges.map {
173         case BoundedRange(lo, hi) => BoundedByteRange(ByteArrays.toBytes(lo), ByteArrays.toBytesFollowingPrefix(hi))
174         case r => throw new IllegalArgumentException(s"Unexpected range type $r")
175       }
176     } else {
177       ranges.flatMap {
178         case BoundedRange(lo, hi) =>
179           val lower = ByteArrays.toBytes(lo)
180           val upper = ByteArrays.toBytesFollowingPrefix(hi)
181           sharding.shards.map(p => BoundedByteRange(ByteArrays.concat(p, lower), ByteArrays.concat(p, upper)))
182 
183         case r => throw new IllegalArgumentException(s"Unexpected range type $r")
184       }
185     }
186   }
187 
188   /**
189     * Determines if the ranges generated by `getRanges` are sufficient to fulfill the query,
190     * or if additional filtering needs to be done
191     *
192     * @param config data store config
193     * @param values index values @see getIndexValues
194     * @param hints  query hints
195     * @return
196     */
197   override def useFullFilter(values: Option[S2IndexValues],
198                              config: Option[GeoMesaDataStoreFactory.GeoMesaDataStoreConfig],
199                              hints: Hints): Boolean = {
200     // if the user has requested strict bounding boxes, we apply the full filter
201     !Option(hints.get(LOOSE_BBOX)).map(Boolean.unbox).getOrElse(config.forall(_.queries.looseBBox)) ||
202       // if the spatial predicate is rectangular (e.g. a bbox), the index is fine enough that we
203       // don't need to apply the filter on top of it. this may cause some minor errors at extremely
204       // fine resolutions, but the performance is worth it
205       // if we have a complicated geometry predicate, we need to pass it through to be evaluated
206       values.exists(_.geometries.values.exists(g => !GeometryUtils.isRectangular(g))) ||
207       // for attribute-level vis, we need to re-evaluate the filter to account for visibility of the row key
208       (sft.getVisibilityLevel == VisibilityLevel.Attribute && values.exists(_.geometries.nonEmpty))
209   }
210 
211 }
212 
213 object S2IndexKeySpace extends IndexKeySpaceFactory[S2IndexValues, Long] {
214 
215   override def supports(sft: SimpleFeatureType, attributes: Seq[String]): Boolean =
216     attributes.lengthCompare(1) == 0 && sft.indexOf(attributes.head) != -1 &&
217       classOf[Point].isAssignableFrom(sft.getDescriptor(attributes.head).getType.getBinding)
218 
219   override def apply(sft: SimpleFeatureType, attributes: Seq[String], tier: Boolean): S2IndexKeySpace = {
220     val shards = if (tier) { NoShardStrategy } else { Z2ShardStrategy(sft) }
221     new S2IndexKeySpace(sft, shards, attributes.head)
222   }
223 }
Line Stmt Id Pos Tree Symbol Tests Code
39 37068 1822 - 1836 Literal <nosymbol> classOf[org.locationtech.jts.geom.Point]
39 37069 1854 - 1901 Apply org.geotools.api.feature.type.PropertyType.getBinding S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding()
39 37070 1822 - 1902 Apply java.lang.Class.isAssignableFrom classOf[org.locationtech.jts.geom.Point].isAssignableFrom(S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding())
39 37076 1814 - 2053 Apply scala.Predef.require scala.Predef.require(classOf[org.locationtech.jts.geom.Point].isAssignableFrom(S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding()), scala.StringContext.apply("Expected field ", " to have a point binding, but instead it has: ").s(S2IndexKeySpace.this.geomField).+(S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding().getSimpleName()))
40 37071 1910 - 1926 Literal <nosymbol> "Expected field "
40 37072 1935 - 1982 Literal <nosymbol> " to have a point binding, but instead it has: "
40 37073 1926 - 1935 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.geomField S2IndexKeySpace.this.geomField
40 37075 1908 - 2052 Apply java.lang.String.+ scala.StringContext.apply("Expected field ", " to have a point binding, but instead it has: ").s(S2IndexKeySpace.this.geomField).+(S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding().getSimpleName())
41 37074 1991 - 2052 Apply java.lang.Class.getSimpleName S2IndexKeySpace.this.sft.getDescriptor(S2IndexKeySpace.this.geomField).getType().getBinding().getSimpleName()
44 37081 2086 - 2233 Apply org.locationtech.geomesa.curve.S2SFC.apply org.locationtech.geomesa.curve.S2SFC.apply(org.locationtech.geomesa.index.conf.QueryProperties.S2MinLevel, org.locationtech.geomesa.index.conf.QueryProperties.S2MaxLevel, org.locationtech.geomesa.index.conf.QueryProperties.S2LevelMod, org.locationtech.geomesa.index.conf.QueryProperties.S2MaxCells)
45 37077 2099 - 2125 Select org.locationtech.geomesa.index.conf.QueryProperties.S2MinLevel org.locationtech.geomesa.index.conf.QueryProperties.S2MinLevel
46 37078 2133 - 2159 Select org.locationtech.geomesa.index.conf.QueryProperties.S2MaxLevel org.locationtech.geomesa.index.conf.QueryProperties.S2MaxLevel
47 37079 2167 - 2193 Select org.locationtech.geomesa.index.conf.QueryProperties.S2LevelMod org.locationtech.geomesa.index.conf.QueryProperties.S2LevelMod
48 37080 2201 - 2227 Select org.locationtech.geomesa.index.conf.QueryProperties.S2MaxCells org.locationtech.geomesa.index.conf.QueryProperties.S2MaxCells
51 37082 2278 - 2287 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.geomField S2IndexKeySpace.this.geomField
51 37083 2266 - 2288 Apply org.geotools.api.feature.simple.SimpleFeatureType.indexOf S2IndexKeySpace.this.sft.indexOf(S2IndexKeySpace.this.geomField)
58 37084 2419 - 2428 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.geomField S2IndexKeySpace.this.geomField
58 37085 2415 - 2429 Apply scala.collection.generic.GenericCompanion.apply scala.collection.Seq.apply[String](S2IndexKeySpace.this.geomField)
66 37086 2753 - 2754 Literal <nosymbol> 8
66 37087 2757 - 2772 Select org.locationtech.geomesa.index.api.ShardStrategy.length S2IndexKeySpace.this.sharding.length
66 37088 2753 - 2772 Apply scala.Int.+ 8.+(S2IndexKeySpace.this.sharding.length)
66 37089 2747 - 2773 Apply scala.util.Right.apply scala.`package`.Right.apply[Nothing, Int](8.+(S2IndexKeySpace.this.sharding.length))
73 37090 2866 - 2877 ApplyToImplicitArgs scala.Array.empty scala.Array.empty[Byte]((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
88 37091 3425 - 3434 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.geomIndex S2IndexKeySpace.this.geomIndex
88 37092 3396 - 3435 Apply org.locationtech.geomesa.index.api.WritableFeature.getAttribute writable.getAttribute[org.locationtech.jts.geom.Point](S2IndexKeySpace.this.geomIndex)
89 37093 3444 - 3456 Apply java.lang.Object.== geom.==(null)
89 37096 3440 - 3440 Literal <nosymbol> ()
89 37097 3440 - 3440 Block <nosymbol> ()
90 37094 3466 - 3555 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Null geometry in feature ", "").s(writable.feature.getID()))
90 37095 3466 - 3555 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Null geometry in feature ", "").s(writable.feature.getID()))
92 37098 3590 - 3599 Apply org.locationtech.jts.geom.Point.getX geom.getX()
92 37099 3601 - 3610 Apply org.locationtech.jts.geom.Point.getY geom.getY()
92 37100 3580 - 3620 Apply org.locationtech.geomesa.curve.S2SFC.index S2IndexKeySpace.this.sfc.index(geom.getX(), geom.getY(), lenient)
92 37101 3580 - 3620 Block org.locationtech.geomesa.curve.S2SFC.index S2IndexKeySpace.this.sfc.index(geom.getX(), geom.getY(), lenient)
93 37102 3657 - 3735 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Invalid s value from geometry: ", "").s(geom), e)
93 37103 3657 - 3735 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Invalid s value from geometry: ", "").s(geom), e)
95 37104 3758 - 3776 Apply org.locationtech.geomesa.index.api.ShardStrategy.apply S2IndexKeySpace.this.sharding.apply(writable)
99 37105 3947 - 3948 Literal <nosymbol> 8
99 37106 3951 - 3960 Select scala.Array.length id.length
99 37107 3932 - 3960 Apply scala.Int.+ shard.length.+(8).+(id.length)
99 37108 3914 - 3961 ApplyToImplicitArgs scala.Array.ofDim scala.Array.ofDim[Byte](shard.length.+(8).+(id.length))((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
101 37109 3971 - 3984 Select scala.collection.IndexedSeqOptimized.isEmpty scala.Predef.byteArrayOps(shard).isEmpty
101 37115 3986 - 4081 Block <nosymbol> { org.locationtech.geomesa.utils.index.ByteArrays.writeLong(s, bytes, org.locationtech.geomesa.utils.index.ByteArrays.writeLong$default$3); java.lang.System.arraycopy(id, 0, bytes, 8, id.length) }
102 37110 3994 - 4024 Apply org.locationtech.geomesa.utils.index.ByteArrays.writeLong org.locationtech.geomesa.utils.index.ByteArrays.writeLong(s, bytes, org.locationtech.geomesa.utils.index.ByteArrays.writeLong$default$3)
103 37111 4052 - 4053 Literal <nosymbol> 0
103 37112 4062 - 4063 Literal <nosymbol> 8
103 37113 4065 - 4074 Select scala.Array.length id.length
103 37114 4031 - 4075 Apply java.lang.System.arraycopy java.lang.System.arraycopy(id, 0, bytes, 8, id.length)
104 37124 4087 - 4244 Block <nosymbol> { bytes.update(0, scala.Predef.byteArrayOps(shard).head); org.locationtech.geomesa.utils.index.ByteArrays.writeLong(s, bytes, 1); java.lang.System.arraycopy(id, 0, bytes, 9, id.length) }
105 37116 4101 - 4102 Literal <nosymbol> 0
105 37117 4106 - 4116 Select scala.collection.IndexedSeqOptimized.head scala.Predef.byteArrayOps(shard).head
105 37118 4095 - 4116 Apply scala.Array.update bytes.update(0, scala.Predef.byteArrayOps(shard).head)
106 37119 4154 - 4187 Apply org.locationtech.geomesa.utils.index.ByteArrays.writeLong org.locationtech.geomesa.utils.index.ByteArrays.writeLong(s, bytes, 1)
107 37120 4215 - 4216 Literal <nosymbol> 0
107 37121 4225 - 4226 Literal <nosymbol> 9
107 37122 4228 - 4237 Select scala.Array.length id.length
107 37123 4194 - 4238 Apply java.lang.System.arraycopy java.lang.System.arraycopy(id, 0, bytes, 9, id.length)
110 37125 4275 - 4282 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.sharing S2IndexKeySpace.this.sharing
110 37126 4304 - 4319 Select org.locationtech.geomesa.index.api.WritableFeature.values writable.values
110 37127 4250 - 4320 Apply org.locationtech.geomesa.index.api.SingleRowKeyValue.apply org.locationtech.geomesa.index.api.`package`.SingleRowKeyValue.apply[Long](bytes, S2IndexKeySpace.this.sharing, shard, s, tier, id, writable.values)
123 37128 4706 - 4715 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.geomField S2IndexKeySpace.this.geomField
123 37129 4667 - 4716 Apply org.locationtech.geomesa.filter.FilterHelper.extractGeometries org.locationtech.geomesa.filter.FilterHelper.extractGeometries(filter, S2IndexKeySpace.this.geomField, org.locationtech.geomesa.filter.FilterHelper.extractGeometries$default$3)
124 37130 4761 - 4779 Select org.locationtech.geomesa.filter.FilterValues.nonEmpty extracted.nonEmpty
124 37131 4783 - 4792 Ident org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.extracted extracted
124 37132 4819 - 4836 Select org.locationtech.geomesa.utils.geotools.WholeWorldPolygon org.locationtech.geomesa.utils.geotools.`package`.WholeWorldPolygon
124 37133 4815 - 4837 Apply scala.collection.generic.GenericCompanion.apply scala.collection.Seq.apply[org.locationtech.jts.geom.Polygon](org.locationtech.geomesa.utils.geotools.`package`.WholeWorldPolygon)
124 37134 4802 - 4802 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$2 org.locationtech.geomesa.filter.FilterValues.apply$default$2[Nothing]
124 37135 4802 - 4802 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$3 org.locationtech.geomesa.filter.FilterValues.apply$default$3[Nothing]
124 37136 4802 - 4838 Apply org.locationtech.geomesa.filter.FilterValues.apply org.locationtech.geomesa.filter.FilterValues.apply[org.locationtech.jts.geom.Polygon](scala.collection.Seq.apply[org.locationtech.jts.geom.Polygon](org.locationtech.geomesa.utils.geotools.`package`.WholeWorldPolygon), org.locationtech.geomesa.filter.FilterValues.apply$default$2[Nothing], org.locationtech.geomesa.filter.FilterValues.apply$default$3[Nothing])
124 37137 4802 - 4838 Block org.locationtech.geomesa.filter.FilterValues.apply org.locationtech.geomesa.filter.FilterValues.apply[org.locationtech.jts.geom.Polygon](scala.collection.Seq.apply[org.locationtech.jts.geom.Polygon](org.locationtech.geomesa.utils.geotools.`package`.WholeWorldPolygon), org.locationtech.geomesa.filter.FilterValues.apply$default$2[Nothing], org.locationtech.geomesa.filter.FilterValues.apply$default$3[Nothing])
127 37138 4860 - 4886 Apply scala.StringContext.s scala.StringContext.apply("Geometries: ", "").s(geometries)
127 37139 4852 - 4887 Apply org.locationtech.geomesa.index.utils.Explainer.apply explain.apply(scala.StringContext.apply("Geometries: ", "").s(geometries))
129 37140 4897 - 4916 Select org.locationtech.geomesa.filter.FilterValues.disjoint geometries.disjoint
129 37145 4918 - 5068 Block <nosymbol> { explain.apply("Non-intersecting geometries extracted, short-circuiting to empty query"); return s2.this.`package`.S2IndexValues.apply(S2IndexKeySpace.this.sfc, geometries, scala.collection.Seq.empty[Nothing]) }
129 37146 4893 - 4893 Literal <nosymbol> ()
129 37147 4893 - 4893 Block <nosymbol> ()
130 37141 4926 - 5007 Apply org.locationtech.geomesa.index.utils.Explainer.apply explain.apply("Non-intersecting geometries extracted, short-circuiting to empty query")
131 37142 5035 - 5038 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.sfc S2IndexKeySpace.this.sfc
131 37143 5052 - 5061 TypeApply scala.collection.generic.GenericCompanion.empty scala.collection.Seq.empty[Nothing]
131 37144 5021 - 5062 Apply org.locationtech.geomesa.index.index.s2.S2IndexValues.apply s2.this.`package`.S2IndexValues.apply(S2IndexKeySpace.this.sfc, geometries, scala.collection.Seq.empty[Nothing])
136 37148 5214 - 5263 Select scala.Option.get org.locationtech.geomesa.index.conf.QueryProperties.PolygonDecompMultiplier.toInt.get
137 37149 5281 - 5324 Select scala.Option.get org.locationtech.geomesa.index.conf.QueryProperties.PolygonDecompBits.toInt.get
138 37150 5357 - 5398 Apply org.locationtech.geomesa.utils.geotools.GeometryUtils.bounds org.locationtech.geomesa.utils.geotools.GeometryUtils.bounds(x$1, multiplier, bits)
138 37151 5356 - 5356 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[(Double, Double, Double, Double)]
138 37152 5331 - 5399 ApplyToImplicitArgs scala.collection.TraversableLike.flatMap geometries.values.flatMap[(Double, Double, Double, Double), Seq[(Double, Double, Double, Double)]](((x$1: org.locationtech.jts.geom.Geometry) => org.locationtech.geomesa.utils.geotools.GeometryUtils.bounds(x$1, multiplier, bits)))(collection.this.Seq.canBuildFrom[(Double, Double, Double, Double)])
141 37153 5425 - 5428 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.sfc S2IndexKeySpace.this.sfc
141 37154 5411 - 5445 Apply org.locationtech.geomesa.index.index.s2.S2IndexValues.apply s2.this.`package`.S2IndexValues.apply(S2IndexKeySpace.this.sfc, geometries, xy)
153 37155 5859 - 5859 Select scala.Tuple2._1 x$2._1
153 37156 5866 - 5866 Select scala.Tuple2._2 x$2._2
154 37157 5887 - 5901 Select org.locationtech.geomesa.filter.FilterValues.disjoint geoms.disjoint
155 37158 5911 - 5925 Select scala.collection.Iterator.empty scala.`package`.Iterator.empty
155 37159 5911 - 5925 Block scala.collection.Iterator.empty scala.`package`.Iterator.empty
156 37169 5937 - 6215 Block <nosymbol> { val target: Option[Int] = org.locationtech.geomesa.index.conf.QueryProperties.ScanRangesTarget.option.map[Int](((t: String) => scala.math.`package`.max(1, scala.Predef.augmentString(t).toInt./(multiplier)))); S2IndexKeySpace.this.sfc.ranges(xy, -1, target).iterator.map[org.locationtech.geomesa.index.api.BoundedRange[Long]](((r: org.locationtech.geomesa.zorder.sfcurve.IndexRange) => org.locationtech.geomesa.index.api.`package`.BoundedRange.apply[Long](r.lower, r.upper))) }
158 37160 6101 - 6102 Literal <nosymbol> 1
158 37161 6104 - 6124 Apply scala.Int./ scala.Predef.augmentString(t).toInt./(multiplier)
158 37162 6092 - 6125 Apply scala.math.max scala.math.`package`.max(1, scala.Predef.augmentString(t).toInt./(multiplier))
158 37163 6043 - 6126 Apply scala.Option.map org.locationtech.geomesa.index.conf.QueryProperties.ScanRangesTarget.option.map[Int](((t: String) => scala.math.`package`.max(1, scala.Predef.augmentString(t).toInt./(multiplier))))
159 37164 6148 - 6150 Literal <nosymbol> -1
159 37165 6191 - 6198 Select org.locationtech.geomesa.zorder.sfcurve.IndexRange.lower r.lower
159 37166 6200 - 6207 Select org.locationtech.geomesa.zorder.sfcurve.IndexRange.upper r.upper
159 37167 6178 - 6208 Apply org.locationtech.geomesa.index.api.BoundedRange.apply org.locationtech.geomesa.index.api.`package`.BoundedRange.apply[Long](r.lower, r.upper)
159 37168 6133 - 6209 Apply scala.collection.Iterator.map S2IndexKeySpace.this.sfc.ranges(xy, -1, target).iterator.map[org.locationtech.geomesa.index.api.BoundedRange[Long]](((r: org.locationtech.geomesa.zorder.sfcurve.IndexRange) => org.locationtech.geomesa.index.api.`package`.BoundedRange.apply[Long](r.lower, r.upper)))
171 37170 6530 - 6550 Apply scala.Int.== S2IndexKeySpace.this.sharding.length.==(0)
172 37177 6560 - 6779 Apply scala.collection.Iterator.map ranges.map[org.locationtech.geomesa.index.api.ByteRange](((x0$1: org.locationtech.geomesa.index.api.ScanRange[Long]) => x0$1 match { case (lower: Long, upper: Long)org.locationtech.geomesa.index.api.BoundedRange[Long]((lo @ _), (hi @ _)) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo), org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi)) case (r @ _) => throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r)) }))
172 37178 6560 - 6779 Block scala.collection.Iterator.map ranges.map[org.locationtech.geomesa.index.api.ByteRange](((x0$1: org.locationtech.geomesa.index.api.ScanRange[Long]) => x0$1 match { case (lower: Long, upper: Long)org.locationtech.geomesa.index.api.BoundedRange[Long]((lo @ _), (hi @ _)) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo), org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi)) case (r @ _) => throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r)) }))
173 37171 6627 - 6649 Apply org.locationtech.geomesa.utils.index.ByteArrays.toBytes org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo)
173 37172 6651 - 6688 Apply org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi)
173 37173 6610 - 6689 Apply org.locationtech.geomesa.index.api.BoundedByteRange.apply org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo), org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi))
173 37174 6610 - 6689 Block org.locationtech.geomesa.index.api.BoundedByteRange.apply org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo), org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi))
174 37175 6708 - 6771 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r))
174 37176 6708 - 6771 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r))
177 37189 6799 - 7159 Apply scala.collection.Iterator.flatMap ranges.flatMap[org.locationtech.geomesa.index.api.ByteRange](((x0$2: org.locationtech.geomesa.index.api.ScanRange[Long]) => x0$2 match { case (lower: Long, upper: Long)org.locationtech.geomesa.index.api.BoundedRange[Long]((lo @ _), (hi @ _)) => { val lower: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo); val upper: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi); S2IndexKeySpace.this.sharding.shards.map[org.locationtech.geomesa.index.api.BoundedByteRange, scala.collection.GenTraversableOnce[org.locationtech.geomesa.index.api.ByteRange]](((p: Array[Byte]) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower), org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper))))(collection.this.Seq.canBuildFrom[org.locationtech.geomesa.index.api.BoundedByteRange]) } case (r @ _) => throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r)) }))
177 37190 6799 - 7159 Block scala.collection.Iterator.flatMap ranges.flatMap[org.locationtech.geomesa.index.api.ByteRange](((x0$2: org.locationtech.geomesa.index.api.ScanRange[Long]) => x0$2 match { case (lower: Long, upper: Long)org.locationtech.geomesa.index.api.BoundedRange[Long]((lo @ _), (hi @ _)) => { val lower: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo); val upper: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi); S2IndexKeySpace.this.sharding.shards.map[org.locationtech.geomesa.index.api.BoundedByteRange, scala.collection.GenTraversableOnce[org.locationtech.geomesa.index.api.ByteRange]](((p: Array[Byte]) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower), org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper))))(collection.this.Seq.canBuildFrom[org.locationtech.geomesa.index.api.BoundedByteRange]) } case (r @ _) => throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r)) }))
178 37186 6850 - 7068 Block <nosymbol> { val lower: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo); val upper: Array[Byte] = org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi); S2IndexKeySpace.this.sharding.shards.map[org.locationtech.geomesa.index.api.BoundedByteRange, scala.collection.GenTraversableOnce[org.locationtech.geomesa.index.api.ByteRange]](((p: Array[Byte]) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower), org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper))))(collection.this.Seq.canBuildFrom[org.locationtech.geomesa.index.api.BoundedByteRange]) }
179 37179 6875 - 6897 Apply org.locationtech.geomesa.utils.index.ByteArrays.toBytes org.locationtech.geomesa.utils.index.ByteArrays.toBytes(lo)
180 37180 6920 - 6957 Apply org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix org.locationtech.geomesa.utils.index.ByteArrays.toBytesFollowingPrefix(hi)
181 37181 7010 - 7037 Apply org.locationtech.geomesa.utils.index.ByteArrays.concat org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower)
181 37182 7039 - 7066 Apply org.locationtech.geomesa.utils.index.ByteArrays.concat org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper)
181 37183 6993 - 7067 Apply org.locationtech.geomesa.index.api.BoundedByteRange.apply org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower), org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper))
181 37184 6987 - 6987 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[org.locationtech.geomesa.index.api.BoundedByteRange]
181 37185 6968 - 7068 ApplyToImplicitArgs scala.collection.TraversableLike.map S2IndexKeySpace.this.sharding.shards.map[org.locationtech.geomesa.index.api.BoundedByteRange, scala.collection.GenTraversableOnce[org.locationtech.geomesa.index.api.ByteRange]](((p: Array[Byte]) => org.locationtech.geomesa.index.api.`package`.BoundedByteRange.apply(org.locationtech.geomesa.utils.index.ByteArrays.concat(p, lower), org.locationtech.geomesa.utils.index.ByteArrays.concat(p, upper))))(collection.this.Seq.canBuildFrom[org.locationtech.geomesa.index.api.BoundedByteRange])
183 37187 7088 - 7151 Throw <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r))
183 37188 7088 - 7151 Block <nosymbol> throw new scala.`package`.IllegalArgumentException(scala.StringContext.apply("Unexpected range type ", "").s(r))
201 37191 7782 - 7792 Select org.locationtech.geomesa.index.conf.QueryHints.LOOSE_BBOX org.locationtech.geomesa.index.conf.QueryHints.LOOSE_BBOX
201 37192 7772 - 7793 Apply java.awt.RenderingHints.get hints.get(org.locationtech.geomesa.index.conf.QueryHints.LOOSE_BBOX)
201 37193 7799 - 7812 Apply scala.Boolean.unbox scala.Boolean.unbox(x)
201 37194 7838 - 7857 Select org.locationtech.geomesa.index.geotools.GeoMesaDataStoreFactory.DataStoreQueryConfig.looseBBox x$3.queries.looseBBox
201 37195 7824 - 7858 Apply scala.Option.forall config.forall(((x$3: org.locationtech.geomesa.index.geotools.GeoMesaDataStoreFactory.GeoMesaDataStoreConfig) => x$3.queries.looseBBox))
206 37196 8268 - 8299 Select scala.Boolean.unary_! org.locationtech.geomesa.utils.geotools.GeometryUtils.isRectangular(g).unary_!
206 37197 8236 - 8300 Apply scala.collection.IterableLike.exists x$4.geometries.values.exists(((g: org.locationtech.jts.geom.Geometry) => org.locationtech.geomesa.utils.geotools.GeometryUtils.isRectangular(g).unary_!))
206 37198 8222 - 8301 Apply scala.Option.exists values.exists(((x$4: org.locationtech.geomesa.index.index.s2.S2IndexValues) => x$4.geometries.values.exists(((g: org.locationtech.jts.geom.Geometry) => org.locationtech.geomesa.utils.geotools.GeometryUtils.isRectangular(g).unary_!))))
206 37204 7764 - 8513 Apply scala.Boolean.|| scala.Option.apply[Object](hints.get(org.locationtech.geomesa.index.conf.QueryHints.LOOSE_BBOX)).map[Boolean]({ ((x: Object) => scala.Boolean.unbox(x)) }).getOrElse[Boolean](config.forall(((x$3: org.locationtech.geomesa.index.geotools.GeoMesaDataStoreFactory.GeoMesaDataStoreConfig) => x$3.queries.looseBBox))).unary_!.||(values.exists(((x$4: org.locationtech.geomesa.index.index.s2.S2IndexValues) => x$4.geometries.values.exists(((g: org.locationtech.jts.geom.Geometry) => org.locationtech.geomesa.utils.geotools.GeometryUtils.isRectangular(g).unary_!))))).||(org.locationtech.geomesa.utils.geotools.RichSimpleFeatureType.RichSimpleFeatureType(S2IndexKeySpace.this.sft).getVisibilityLevel.==(org.locationtech.geomesa.utils.index.VisibilityLevel.Attribute).&&(values.exists(((x$5: org.locationtech.geomesa.index.index.s2.S2IndexValues) => x$5.geometries.nonEmpty))))
208 37199 8421 - 8424 Select org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.sft S2IndexKeySpace.this.sft
208 37200 8447 - 8472 Select org.locationtech.geomesa.utils.index.VisibilityLevel.Attribute org.locationtech.geomesa.utils.index.VisibilityLevel.Attribute
208 37201 8490 - 8511 Select org.locationtech.geomesa.filter.FilterValues.nonEmpty x$5.geometries.nonEmpty
208 37202 8476 - 8512 Apply scala.Option.exists values.exists(((x$5: org.locationtech.geomesa.index.index.s2.S2IndexValues) => x$5.geometries.nonEmpty))
208 37203 8421 - 8512 Apply scala.Boolean.&& org.locationtech.geomesa.utils.geotools.RichSimpleFeatureType.RichSimpleFeatureType(S2IndexKeySpace.this.sft).getVisibilityLevel.==(org.locationtech.geomesa.utils.index.VisibilityLevel.Attribute).&&(values.exists(((x$5: org.locationtech.geomesa.index.index.s2.S2IndexValues) => x$5.geometries.nonEmpty)))
216 37205 8711 - 8712 Literal <nosymbol> 1
216 37206 8717 - 8718 Literal <nosymbol> 0
216 37207 8722 - 8756 Apply scala.Int.!= sft.indexOf(attributes.head).!=(-1)
216 37211 8686 - 8852 Apply scala.Boolean.&& attributes.lengthCompare(1).==(0).&&(sft.indexOf(attributes.head).!=(-1)).&&(classOf[org.locationtech.jts.geom.Point].isAssignableFrom(sft.getDescriptor(attributes.head).getType().getBinding()))
217 37208 8766 - 8780 Literal <nosymbol> classOf[org.locationtech.jts.geom.Point]
217 37209 8798 - 8851 Apply org.geotools.api.feature.type.PropertyType.getBinding sft.getDescriptor(attributes.head).getType().getBinding()
217 37210 8766 - 8852 Apply java.lang.Class.isAssignableFrom classOf[org.locationtech.jts.geom.Point].isAssignableFrom(sft.getDescriptor(attributes.head).getType().getBinding())
220 37212 8989 - 9004 Select org.locationtech.geomesa.index.api.ShardStrategy.NoShardStrategy org.locationtech.geomesa.index.api.ShardStrategy.NoShardStrategy
220 37213 8989 - 9004 Block org.locationtech.geomesa.index.api.ShardStrategy.NoShardStrategy org.locationtech.geomesa.index.api.ShardStrategy.NoShardStrategy
220 37214 9014 - 9034 Apply org.locationtech.geomesa.index.api.ShardStrategy.Z2ShardStrategy.apply org.locationtech.geomesa.index.api.ShardStrategy.Z2ShardStrategy.apply(sft)
220 37215 9014 - 9034 Block org.locationtech.geomesa.index.api.ShardStrategy.Z2ShardStrategy.apply org.locationtech.geomesa.index.api.ShardStrategy.Z2ShardStrategy.apply(sft)
221 37216 9074 - 9089 Select scala.collection.IterableLike.head attributes.head
221 37217 9041 - 9090 Apply org.locationtech.geomesa.index.index.s2.S2IndexKeySpace.<init> new S2IndexKeySpace(sft, shards, attributes.head)