or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-geometry.mdformat-conversion.mdindex.mdmulti-geometry.mdsimplification.mdspatial-utilities.mdvalidation.mdvisitor-pattern.md

spatial-utilities.mddocs/

0

# Spatial Utilities

1

2

Additional spatial utility functions for envelope calculation, circle conversion, and bit manipulation operations. These utilities provide advanced spatial operations and low-level optimization functions for geometric computations.

3

4

## Capabilities

5

6

### SpatialEnvelopeVisitor

7

8

Determines the spatial envelope (bounding box) of geometries with support for both Cartesian and geographic coordinate systems.

9

10

```java { .api }

11

/**

12

* Calculates the spatial envelope (bounding box) of a geometry

13

* @param geometry the geometry to analyze

14

* @return Optional containing the bounding rectangle, or empty if geometry is empty

15

*/

16

public static Optional<Rectangle> visit(Geometry geometry)

17

18

/**

19

* Visitor pattern implementation for envelope calculation

20

* @param <E> exception type that may be thrown during processing

21

*/

22

public class SpatialEnvelopeVisitor<E extends Exception> implements GeometryVisitor<Optional<Rectangle>, E> {

23

24

/**

25

* Creates a cartesian envelope visitor

26

* @return visitor for cartesian coordinate systems

27

*/

28

public static SpatialEnvelopeVisitor<RuntimeException> cartesian()

29

30

/**

31

* Creates a geographic envelope visitor with dateline wrapping

32

* @return visitor for geographic coordinate systems

33

*/

34

public static SpatialEnvelopeVisitor<RuntimeException> geographic()

35

36

/**

37

* Creates a geographic envelope visitor with optional dateline wrapping

38

* @param wrapLongitude whether to consider longitude wrapping around dateline

39

* @return visitor for geographic coordinate systems

40

*/

41

public static SpatialEnvelopeVisitor<RuntimeException> geographic(boolean wrapLongitude)

42

}

43

```

44

45

**Usage Examples:**

46

47

```java

48

import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;

49

import java.util.Optional;

50

51

// Calculate envelope for any geometry

52

Polygon complexPolygon = createComplexPolygon();

53

Optional<Rectangle> envelope = SpatialEnvelopeVisitor.visit(complexPolygon);

54

55

if (envelope.isPresent()) {

56

Rectangle bbox = envelope.get();

57

double width = bbox.getMaxX() - bbox.getMinX();

58

double height = bbox.getMaxY() - bbox.getMinY();

59

System.out.println("Bounding box: " + width + " x " + height);

60

}

61

62

// Use specific coordinate system visitor

63

SpatialEnvelopeVisitor<RuntimeException> cartesianVisitor = SpatialEnvelopeVisitor.cartesian();

64

Optional<Rectangle> cartesianEnvelope = complexPolygon.visit(cartesianVisitor);

65

66

// Geographic visitor with dateline wrapping consideration

67

SpatialEnvelopeVisitor<RuntimeException> geoVisitor = SpatialEnvelopeVisitor.geographic(true);

68

Optional<Rectangle> geoEnvelope = complexPolygon.visit(geoVisitor);

69

```

70

71

### CircleUtils

72

73

Utilities for converting circles to polygon approximations and performing spatial calculations.

74

75

```java { .api }

76

/**

77

* Minimum number of sides for circle-to-polygon conversion

78

*/

79

public static final int CIRCLE_TO_POLYGON_MINIMUM_NUMBER_OF_SIDES = 4;

80

81

/**

82

* Maximum number of sides for circle-to-polygon conversion

83

*/

84

public static final int CIRCLE_TO_POLYGON_MAXIMUM_NUMBER_OF_SIDES = 1000;

85

86

/**

87

* Converts a circle to a regular polygon approximation

88

* @param circle the circle to convert

89

* @param gons number of sides for the polygon (4-1000)

90

* @return polygon approximating the circle

91

* @throws IllegalArgumentException if circle contains a pole or gons is invalid

92

*/

93

public static Polygon createRegularGeoShapePolygon(Circle circle, int gons)

94

95

/**

96

* Calculates haversine distance between two points (internal utility)

97

* @param lat1 latitude of first point

98

* @param lon1 longitude of first point

99

* @param lat2 latitude of second point

100

* @param lon2 longitude of second point

101

* @return distance in meters

102

*/

103

private static double slowHaversin(double lat1, double lon1, double lat2, double lon2)

104

```

105

106

**Usage Examples:**

107

108

```java

109

import org.elasticsearch.geometry.utils.CircleUtils;

110

111

// Convert circle to polygon with 64 sides

112

Circle circle = new Circle(-73.935, 40.730, 1000.0); // 1km radius

113

Polygon circlePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 64);

114

115

// Lower precision for performance (minimum 4 sides)

116

Polygon simplePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 8);

117

118

// High precision approximation (maximum 1000 sides)

119

Polygon precisePolygon = CircleUtils.createRegularGeoShapePolygon(circle, 1000);

120

121

// Error handling for polar circles

122

try {

123

Circle polarCircle = new Circle(0, 89, 200000); // Large circle near north pole

124

Polygon result = CircleUtils.createRegularGeoShapePolygon(polarCircle, 32);

125

} catch (IllegalArgumentException e) {

126

System.err.println("Circle contains pole: " + e.getMessage());

127

}

128

```

129

130

### BitUtil

131

132

Low-level bit manipulation utilities for spatial indexing and Morton encoding operations.

133

134

```java { .api }

135

/**

136

* Interleaves the first 32 bits of two integer values

137

* @param even the even-positioned bits (usually x coordinate)

138

* @param odd the odd-positioned bits (usually y coordinate)

139

* @return interleaved long value with bits from both inputs

140

*/

141

public static long interleave(int even, int odd)

142

143

/**

144

* Deinterleaves a long value to extract even-positioned bits

145

* @param interleaved the interleaved long value

146

* @return integer containing the even-positioned bits

147

*/

148

public static int deinterleave(long interleaved)

149

150

/**

151

* Deinterleaves a long value to extract odd-positioned bits

152

* @param interleaved the interleaved long value

153

* @return integer containing the odd-positioned bits

154

*/

155

public static int deinterleaveOdd(long interleaved)

156

157

/**

158

* Flips all bits in an integer value

159

* @param value the integer to flip

160

* @return integer with all bits flipped

161

*/

162

public static int flipBits(int value)

163

164

/**

165

* Counts the number of set bits in an integer

166

* @param value the integer to analyze

167

* @return number of bits set to 1

168

*/

169

public static int popCount(int value)

170

```

171

172

**Usage Examples:**

173

174

```java

175

import org.elasticsearch.geometry.utils.BitUtil;

176

177

// Spatial indexing with Morton encoding

178

int xCoord = 12345;

179

int yCoord = 67890;

180

181

// Interleave coordinates for Z-order curve

182

long mortonCode = BitUtil.interleave(xCoord, yCoord);

183

184

// Extract coordinates back from Morton code

185

int extractedX = BitUtil.deinterleave(mortonCode);

186

int extractedY = BitUtil.deinterleaveOdd(mortonCode);

187

188

assert extractedX == xCoord;

189

assert extractedY == yCoord;

190

191

// Bit manipulation operations

192

int value = 0b10110010;

193

int flipped = BitUtil.flipBits(value); // 0b01001101

194

int setBits = BitUtil.popCount(value); // 4 (number of 1s)

195

196

// Geospatial indexing example

197

double longitude = -73.935242;

198

double latitude = 40.730610;

199

200

// Convert to integer coordinates (simplified example)

201

int lonBits = (int) ((longitude + 180.0) * 1000000);

202

int latBits = (int) ((latitude + 90.0) * 1000000);

203

204

// Create spatial index key

205

long spatialKey = BitUtil.interleave(lonBits, latBits);

206

```

207

208

## Advanced Usage Patterns

209

210

### Combined Envelope and Circle Conversion

211

212

```java

213

// Get envelope, convert to circle, then to polygon

214

MultiPolygon complexGeometry = createComplexMultiPolygon();

215

216

// Calculate bounding envelope

217

Optional<Rectangle> envelope = SpatialEnvelopeVisitor.visit(complexGeometry);

218

219

if (envelope.isPresent()) {

220

Rectangle bbox = envelope.get();

221

222

// Create circle that covers the envelope

223

double centerLon = (bbox.getMinX() + bbox.getMaxX()) / 2;

224

double centerLat = (bbox.getMinY() + bbox.getMaxY()) / 2;

225

double width = bbox.getMaxX() - bbox.getMinX();

226

double height = bbox.getMaxY() - bbox.getMinY();

227

double radius = Math.sqrt(width * width + height * height) * 111320 / 2; // approx meters

228

229

Circle boundingCircle = new Circle(centerLon, centerLat, radius);

230

231

// Convert to polygon approximation

232

Polygon approximation = CircleUtils.createRegularGeoShapePolygon(boundingCircle, 32);

233

}

234

```

235

236

### Spatial Grid Indexing with BitUtil

237

238

```java

239

// Create spatial grid index using Morton encoding

240

public class SpatialGrid {

241

private static final double WORLD_WIDTH = 360.0;

242

private static final double WORLD_HEIGHT = 180.0;

243

private final int resolution;

244

245

public SpatialGrid(int resolution) {

246

this.resolution = resolution;

247

}

248

249

public long encode(double longitude, double latitude) {

250

// Normalize coordinates to grid

251

int x = (int) ((longitude + 180.0) / WORLD_WIDTH * resolution);

252

int y = (int) ((latitude + 90.0) / WORLD_HEIGHT * resolution);

253

254

// Clamp to bounds

255

x = Math.max(0, Math.min(resolution - 1, x));

256

y = Math.max(0, Math.min(resolution - 1, y));

257

258

return BitUtil.interleave(x, y);

259

}

260

261

public Point decode(long mortonCode) {

262

int x = BitUtil.deinterleave(mortonCode);

263

int y = BitUtil.deinterleaveOdd(mortonCode);

264

265

double longitude = (double) x / resolution * WORLD_WIDTH - 180.0;

266

double latitude = (double) y / resolution * WORLD_HEIGHT - 90.0;

267

268

return new Point(longitude, latitude);

269

}

270

}

271

272

// Usage

273

SpatialGrid grid = new SpatialGrid(1024);

274

long gridKey = grid.encode(-73.935242, 40.730610);

275

Point gridCenter = grid.decode(gridKey);

276

```

277

278

### Geographic vs Cartesian Envelope Calculation

279

280

```java

281

// Different envelope calculations for different coordinate systems

282

Polygon antimeridianPolygon = createAntimeridianSpanningPolygon();

283

284

// Cartesian envelope (treats coordinates as planar)

285

Optional<Rectangle> cartesianEnv = antimeridianPolygon.visit(

286

SpatialEnvelopeVisitor.cartesian()

287

);

288

289

// Geographic envelope without dateline wrapping

290

Optional<Rectangle> geoEnvNoWrap = antimeridianPolygon.visit(

291

SpatialEnvelopeVisitor.geographic(false)

292

);

293

294

// Geographic envelope with dateline wrapping (smaller bbox)

295

Optional<Rectangle> geoEnvWrap = antimeridianPolygon.visit(

296

SpatialEnvelopeVisitor.geographic(true)

297

);

298

299

// Compare envelope sizes to choose optimal representation

300

if (geoEnvWrap.isPresent() && geoEnvNoWrap.isPresent()) {

301

Rectangle wrapped = geoEnvWrap.get();

302

Rectangle unwrapped = geoEnvNoWrap.get();

303

304

double wrappedArea = (wrapped.getMaxX() - wrapped.getMinX()) *

305

(wrapped.getMaxY() - wrapped.getMinY());

306

double unwrappedArea = (unwrapped.getMaxX() - unwrapped.getMinX()) *

307

(unwrapped.getMaxY() - unwrapped.getMinY());

308

309

Rectangle optimal = wrappedArea < unwrappedArea ? wrapped : unwrapped;

310

}

311

```