0
# Advanced Operations
1
2
Specialized functions for advanced H3 operations including local coordinate systems, vertex operations, and system introspection. These functions enable sophisticated spatial analysis and provide access to H3's internal geometric structures.
3
4
## Capabilities
5
6
### Local Coordinate Systems
7
8
Convert between H3 cells and local (i,j) coordinate systems for efficient spatial operations.
9
10
```python { .api }
11
def cell_to_local_ij(origin: str, h: str) -> tuple[int, int]:
12
"""
13
Convert H3 cell to local (i,j) coordinates relative to an origin cell.
14
15
The local coordinate system is anchored to the origin cell's base cell,
16
not the origin cell itself. This provides consistent coordinates across
17
operations without recomputing the origin's position.
18
19
Args:
20
origin: Origin H3 cell identifier defining the coordinate system
21
h: Target H3 cell identifier to convert to local coordinates
22
23
Returns:
24
Tuple of (i, j) integer coordinates
25
26
Raises:
27
H3CellInvalidError: If either cell identifier is invalid
28
H3ResMismatchError: If cells have different resolutions
29
H3GridNavigationError: If cells are too far apart
30
31
Note:
32
Both cells must be at the same resolution.
33
The (0,0) coordinate represents the center of the base cell
34
containing the origin, not the origin cell itself.
35
"""
36
37
def local_ij_to_cell(origin: str, i: int, j: int) -> str:
38
"""
39
Convert local (i,j) coordinates to H3 cell relative to an origin cell.
40
41
This is the inverse operation of cell_to_local_ij().
42
43
Args:
44
origin: Origin H3 cell identifier defining the coordinate system
45
i: Integer i-coordinate
46
j: Integer j-coordinate
47
48
Returns:
49
H3 cell identifier at the specified local coordinates
50
51
Raises:
52
H3CellInvalidError: If origin is not a valid H3 cell
53
H3GridNavigationError: If coordinates are outside valid range
54
55
Note:
56
The coordinate system is anchored to the origin cell's base cell.
57
Large coordinate values may be outside the valid range.
58
"""
59
```
60
61
### Vertex Operations
62
63
Work with H3 cell vertices and their geometric properties.
64
65
```python { .api }
66
def cell_to_vertex(h: str, vertex_num: int) -> str:
67
"""
68
Get a specific vertex of an H3 cell by vertex number.
69
70
Args:
71
h: H3 cell identifier
72
vertex_num: Vertex number (0-5 for hexagons, 0-4 for pentagons)
73
74
Returns:
75
H3 vertex identifier
76
77
Raises:
78
H3CellInvalidError: If h is not a valid H3 cell
79
ValueError: If vertex_num is outside valid range for the cell
80
81
Note:
82
Hexagons have vertices 0-5, pentagons have vertices 0-4.
83
Vertex numbering follows a consistent orientation.
84
"""
85
86
def cell_to_vertexes(h: str) -> list[str]:
87
"""
88
Get all vertices of an H3 cell.
89
90
Args:
91
h: H3 cell identifier
92
93
Returns:
94
List of H3 vertex identifiers (5 for pentagons, 6 for hexagons)
95
96
Raises:
97
H3CellInvalidError: If h is not a valid H3 cell
98
99
Note:
100
Vertices are returned in consistent order around the cell boundary.
101
Pentagon cells return 5 vertices, hexagon cells return 6 vertices.
102
"""
103
104
def vertex_to_latlng(v: str) -> tuple[float, float]:
105
"""
106
Get the latitude and longitude coordinates of an H3 vertex.
107
108
Args:
109
v: H3 vertex identifier
110
111
Returns:
112
Tuple of (latitude, longitude) in degrees
113
114
Raises:
115
H3VertexInvalidError: If v is not a valid H3 vertex
116
117
Note:
118
Vertices represent the corner points where cell boundaries meet.
119
Multiple cells may share the same vertex.
120
"""
121
122
def is_valid_vertex(v: str) -> bool:
123
"""
124
Check if an H3 vertex identifier is valid.
125
126
Args:
127
v: H3 vertex identifier (string or int)
128
129
Returns:
130
True if valid H3 vertex, False otherwise
131
132
Note:
133
Returns False for any input that cannot be parsed as H3 vertex,
134
including H3 cells or edges.
135
"""
136
```
137
138
### System Introspection
139
140
Access specialized cells and system structure information.
141
142
```python { .api }
143
def get_pentagons(res: int) -> list[str]:
144
"""
145
Get all pentagon H3 cells at a given resolution.
146
147
Args:
148
res: H3 resolution (0-15)
149
150
Returns:
151
List of all 12 pentagon H3 cell identifiers at the specified resolution
152
153
Raises:
154
H3ResDomainError: If res < 0 or res > 15
155
156
Note:
157
There are exactly 12 pentagons at every resolution level.
158
Pentagons occur at the vertices of the underlying icosahedron.
159
Order is not guaranteed.
160
"""
161
162
def get_res0_cells() -> list[str]:
163
"""
164
Get all H3 cells at resolution 0 (base cells).
165
166
Returns:
167
List of all 122 base H3 cell identifiers
168
169
Note:
170
These are the coarsest cells in the H3 system.
171
Includes 12 pentagons and 110 hexagons.
172
All higher-resolution cells are descendants of these base cells.
173
Order is not guaranteed.
174
"""
175
176
def get_icosahedron_faces(h: str) -> set[int]:
177
"""
178
Get the icosahedron faces that intersect with an H3 cell.
179
180
H3 is based on an icosahedron projection, which has 20 triangular faces.
181
This function returns which faces are intersected by the given cell.
182
183
Args:
184
h: H3 cell identifier
185
186
Returns:
187
Python set of integers representing face numbers (0-19)
188
189
Raises:
190
H3CellInvalidError: If h is not a valid H3 cell
191
192
Note:
193
Most cells intersect 1 face, but cells near face boundaries
194
may intersect 2 or 3 faces. Face numbers range from 0 to 19.
195
"""
196
```
197
198
## Usage Examples
199
200
### Local Coordinate System Operations
201
202
```python
203
import h3
204
205
# Set up a local coordinate system
206
origin = h3.latlng_to_cell(37.7749, -122.4194, 9) # San Francisco
207
print(f"Origin cell: {origin}")
208
209
# Get some nearby cells
210
nearby_cells = h3.grid_disk(origin, k=2)
211
print(f"Working with {len(nearby_cells)} nearby cells")
212
213
# Convert cells to local coordinates
214
local_coords = {}
215
for cell in nearby_cells:
216
i, j = h3.cell_to_local_ij(origin, cell)
217
local_coords[cell] = (i, j)
218
219
print(f"\nLocal coordinates (first 10 cells):")
220
for cell, (i, j) in list(local_coords.items())[:10]:
221
print(f" {cell}: ({i:3d}, {j:3d})")
222
223
# Find origin's coordinates (should be relative to its base cell, not (0,0))
224
origin_i, origin_j = h3.cell_to_local_ij(origin, origin)
225
print(f"\nOrigin coordinates: ({origin_i}, {origin_j})")
226
print("Note: Origin coordinates are relative to base cell center, not (0,0)")
227
228
# Test round-trip conversion
229
test_cell = list(nearby_cells)[5]
230
i, j = h3.cell_to_local_ij(origin, test_cell)
231
recovered_cell = h3.local_ij_to_cell(origin, i, j)
232
233
print(f"\nRound-trip test:")
234
print(f" Original: {test_cell}")
235
print(f" Local coords: ({i}, {j})")
236
print(f" Recovered: {recovered_cell}")
237
print(f" Match: {test_cell == recovered_cell}")
238
```
239
240
### Grid Pattern Analysis with Local Coordinates
241
242
```python
243
import h3
244
import matplotlib.pyplot as plt
245
246
# Create a coordinate grid pattern
247
center = h3.latlng_to_cell(40.7589, -73.9851, 8) # NYC
248
region = h3.grid_disk(center, k=4)
249
250
# Convert to local coordinates for analysis
251
coords = []
252
cells = []
253
254
for cell in region:
255
i, j = h3.cell_to_local_ij(center, cell)
256
coords.append((i, j))
257
cells.append(cell)
258
259
print(f"Analyzing {len(region)} cells in local coordinate space")
260
261
# Find coordinate ranges
262
i_coords = [i for i, j in coords]
263
j_coords = [j for i, j in coords]
264
265
print(f"I coordinate range: {min(i_coords)} to {max(i_coords)}")
266
print(f"J coordinate range: {min(j_coords)} to {max(j_coords)}")
267
268
# Analyze coordinate distribution
269
i_span = max(i_coords) - min(i_coords)
270
j_span = max(j_coords) - min(j_coords)
271
print(f"Coordinate space spans: {i_span} x {j_span}")
272
273
# Find cells along coordinate axes
274
origin_i, origin_j = h3.cell_to_local_ij(center, center)
275
print(f"\nCells along coordinate axes (relative to origin at {origin_i}, {origin_j}):")
276
277
# Find cells with same i-coordinate as origin
278
same_i_cells = [(cell, i, j) for cell, (i, j) in zip(cells, coords) if i == origin_i]
279
print(f" Same I-coordinate ({origin_i}): {len(same_i_cells)} cells")
280
281
# Find cells with same j-coordinate as origin
282
same_j_cells = [(cell, i, j) for cell, (i, j) in zip(cells, coords) if j == origin_j]
283
print(f" Same J-coordinate ({origin_j}): {len(same_j_cells)} cells")
284
285
# Optional: Create a simple plot if matplotlib is available
286
try:
287
plt.figure(figsize=(10, 8))
288
plt.scatter(i_coords, j_coords, alpha=0.6)
289
plt.scatter([origin_i], [origin_j], color='red', s=100, label='Origin')
290
plt.xlabel('I Coordinate')
291
plt.ylabel('J Coordinate')
292
plt.title('H3 Cells in Local Coordinate Space')
293
plt.legend()
294
plt.grid(True, alpha=0.3)
295
plt.axis('equal')
296
plt.savefig('h3_local_coords.png', dpi=150, bbox_inches='tight')
297
print(f"\nCoordinate plot saved as 'h3_local_coords.png'")
298
except ImportError:
299
print(f"\nMatplotlib not available - skipping coordinate plot")
300
```
301
302
### Vertex Analysis
303
304
```python
305
import h3
306
307
# Analyze vertices for different cell types
308
test_cells = [
309
h3.latlng_to_cell(37.7749, -122.4194, 8), # Regular hexagon
310
h3.get_pentagons(8)[0] # Pentagon at same resolution
311
]
312
313
print("Vertex analysis:")
314
315
for i, cell in enumerate(test_cells):
316
cell_type = "pentagon" if h3.is_pentagon(cell) else "hexagon"
317
print(f"\nCell {i+1} ({cell_type}): {cell}")
318
319
# Get all vertices
320
vertices = h3.cell_to_vertexes(cell)
321
print(f" Vertices: {len(vertices)}")
322
323
# Show each vertex with coordinates
324
vertex_coords = []
325
for j, vertex in enumerate(vertices):
326
lat, lng = h3.vertex_to_latlng(vertex)
327
vertex_coords.append((lat, lng))
328
is_valid = h3.is_valid_vertex(vertex)
329
print(f" Vertex {j}: {vertex} -> {lat:.6f}, {lng:.6f} (valid: {is_valid})")
330
331
# Test individual vertex access
332
print(f" Individual vertex access:")
333
for vertex_num in range(len(vertices)):
334
individual_vertex = h3.cell_to_vertex(cell, vertex_num)
335
matches_list = individual_vertex == vertices[vertex_num]
336
print(f" Vertex {vertex_num}: {individual_vertex} (matches list: {matches_list})")
337
338
# Calculate vertex distances (perimeter)
339
perimeter = 0.0
340
for j in range(len(vertex_coords)):
341
start = vertex_coords[j]
342
end = vertex_coords[(j + 1) % len(vertex_coords)] # Wrap around
343
distance = h3.great_circle_distance(start, end, 'km')
344
perimeter += distance
345
print(f" Edge {j}-{(j+1) % len(vertex_coords)}: {distance:.6f} km")
346
347
print(f" Total perimeter: {perimeter:.6f} km")
348
349
# Compare to expected edge count
350
expected_vertices = 5 if h3.is_pentagon(cell) else 6
351
assert len(vertices) == expected_vertices
352
```
353
354
### Pentagon Location Analysis
355
356
```python
357
import h3
358
359
# Analyze pentagon distribution across resolutions
360
print("Pentagon analysis across resolutions:")
361
362
for res in range(0, 8):
363
pentagons = h3.get_pentagons(res)
364
print(f"\nResolution {res}: {len(pentagons)} pentagons")
365
366
# Analyze pentagon locations
367
pentagon_coords = []
368
for pentagon in pentagons:
369
lat, lng = h3.cell_to_latlng(pentagon)
370
pentagon_coords.append((lat, lng, pentagon))
371
372
# Sort by latitude to see distribution
373
pentagon_coords.sort(key=lambda x: x[0], reverse=True) # North to South
374
375
print(" Pentagon locations (North to South):")
376
for i, (lat, lng, pentagon) in enumerate(pentagon_coords[:5]): # Show first 5
377
base_cell = h3.get_base_cell_number(pentagon)
378
print(f" {i+1}: {pentagon} -> {lat:7.2f}°, {lng:8.2f}° (base cell {base_cell})")
379
380
if len(pentagon_coords) > 5:
381
print(f" ... and {len(pentagon_coords) - 5} more")
382
383
# Analyze pentagon neighbors
384
print(f"\nPentagon neighbor analysis at resolution 6:")
385
pentagon = h3.get_pentagons(6)[0]
386
neighbors = h3.grid_ring(pentagon, k=1)
387
388
print(f"Pentagon {pentagon}:")
389
print(f" Neighbors: {len(neighbors)} (expected 5 for pentagon)")
390
391
# Check if neighbors are hexagons
392
neighbor_types = {}
393
for neighbor in neighbors:
394
is_pent = h3.is_pentagon(neighbor)
395
neighbor_type = "pentagon" if is_pent else "hexagon"
396
neighbor_types[neighbor_type] = neighbor_types.get(neighbor_type, 0) + 1
397
398
print(f" Neighbor types: {dict(neighbor_types)}")
399
```
400
401
### Icosahedron Face Analysis
402
403
```python
404
import h3
405
406
# Analyze icosahedron face distribution
407
print("Icosahedron face analysis:")
408
409
# Test cells at different locations and resolutions
410
test_cases = [
411
("Equator", 0, 0, 5),
412
("North Pole region", 85, 0, 5),
413
("South Pole region", -85, 0, 5),
414
("Pacific", 0, -180, 5),
415
("Face boundary", 0, 0, 3), # Lower resolution to potentially cross faces
416
]
417
418
for name, lat, lng, res in test_cases:
419
cell = h3.latlng_to_cell(lat, lng, res)
420
faces = h3.get_icosahedron_faces(cell)
421
422
print(f"\n{name} (res {res}): {cell}")
423
print(f" Intersects faces: {sorted(faces)} ({len(faces)} faces)")
424
425
# Get neighbors and analyze their faces
426
neighbors = h3.grid_ring(cell, k=1)
427
neighbor_faces = set()
428
429
for neighbor in neighbors:
430
n_faces = h3.get_icosahedron_faces(neighbor)
431
neighbor_faces.update(n_faces)
432
433
all_faces = faces.union(neighbor_faces)
434
print(f" Cell + neighbors span faces: {sorted(all_faces)} ({len(all_faces)} total)")
435
436
# Find cells that cross multiple faces
437
print(f"\nSearching for multi-face cells at resolution 2:")
438
multi_face_count = 0
439
face_distribution = {}
440
441
# Sample some cells at low resolution
442
for base_cell in h3.get_res0_cells()[:20]: # Check first 20 base cells
443
children = h3.cell_to_children(base_cell, res=2)
444
445
for cell in children:
446
faces = h3.get_icosahedron_faces(cell)
447
face_count = len(faces)
448
face_distribution[face_count] = face_distribution.get(face_count, 0) + 1
449
450
if face_count > 1:
451
multi_face_count += 1
452
if multi_face_count <= 3: # Show first few examples
453
lat, lng = h3.cell_to_latlng(cell)
454
print(f" Multi-face cell: {cell} -> {sorted(faces)} at {lat:.2f}, {lng:.2f}")
455
456
print(f"\nFace intersection distribution (sample of {sum(face_distribution.values())} cells):")
457
for face_count, count in sorted(face_distribution.items()):
458
print(f" {face_count} face(s): {count} cells ({count/sum(face_distribution.values()):.1%})")
459
```
460
461
### System Structure Overview
462
463
```python
464
import h3
465
466
# Comprehensive system structure analysis
467
print("H3 System Structure Overview:")
468
469
# Base cell analysis
470
base_cells = h3.get_res0_cells()
471
pentagons_res0 = h3.get_pentagons(0)
472
473
print(f"\nBase structure (Resolution 0):")
474
print(f" Total base cells: {len(base_cells)}")
475
print(f" Pentagons: {len(pentagons_res0)}")
476
print(f" Hexagons: {len(base_cells) - len(pentagons_res0)}")
477
478
# Verify pentagon consistency
479
pentagon_base_cells = [cell for cell in base_cells if h3.is_pentagon(cell)]
480
print(f" Pentagon count verification: {len(pentagon_base_cells)} (should match {len(pentagons_res0)})")
481
482
# Show base cell number range
483
base_numbers = [h3.get_base_cell_number(cell) for cell in base_cells]
484
print(f" Base cell numbers: {min(base_numbers)} to {max(base_numbers)}")
485
486
# Child count analysis
487
print(f"\nHierarchy analysis (Resolution 0 -> 1):")
488
hex_child_counts = []
489
pent_child_counts = []
490
491
for cell in base_cells[:10]: # Sample first 10 base cells
492
children = h3.cell_to_children(cell, res=1)
493
child_count = len(children)
494
495
if h3.is_pentagon(cell):
496
pent_child_counts.append(child_count)
497
else:
498
hex_child_counts.append(child_count)
499
500
if hex_child_counts:
501
print(f" Hexagon children: {hex_child_counts[0]} (sample)")
502
if pent_child_counts:
503
print(f" Pentagon children: {pent_child_counts[0]} (sample)")
504
505
# Face coverage analysis
506
print(f"\nIcosahedron face coverage:")
507
face_cells = {}
508
509
for face_num in range(20):
510
# Find base cells that intersect this face
511
face_base_cells = []
512
for cell in base_cells:
513
faces = h3.get_icosahedron_faces(cell)
514
if face_num in faces:
515
face_base_cells.append(cell)
516
517
face_cells[face_num] = len(face_base_cells)
518
519
face_counts = list(face_cells.values())
520
print(f" Cells per face - min: {min(face_counts)}, max: {max(face_counts)}, avg: {sum(face_counts)/len(face_counts):.1f}")
521
522
# System capacity
523
print(f"\nSystem capacity:")
524
for res in [0, 5, 10, 15]:
525
total_cells = h3.get_num_cells(res)
526
pentagons = 12 # Always 12 pentagons
527
hexagons = total_cells - pentagons
528
529
print(f" Resolution {res:2d}: {total_cells:15,} total ({hexagons:15,} hex + {pentagons:2,} pent)")
530
531
print(f"\nMaximum theoretical capacity: {h3.get_num_cells(15):,} cells at resolution 15")
532
```