Skip to content

DGGS Generator

Generator module for vgrid.

This module provides functions to generate discrete global grid systems (DGGS) for various coordinate systems and geographic areas.

a5grid_cli()

CLI interface for generating A5 DGGS.

Source code in vgrid/generator/a5grid.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def a5grid_cli():
    """CLI interface for generating A5 DGGS."""
    parser = argparse.ArgumentParser(description="Generate A5 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..29]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    args = parser.parse_args()
    try:
        result = a5grid(args.resolution, args.bbox, args.output_format)
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

digipingrid_cli()

Command-line interface for DIGIPIN grid generation.

Source code in vgrid/generator/digipingrid.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def digipingrid_cli():
    """
    Command-line interface for DIGIPIN grid generation.
    """
    parser = argparse.ArgumentParser(
        description="Generate DIGIPIN DGGS grid for India region."
    )
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [1..10]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the format: min_lon min_lat max_lon max_lat (default is India region)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    args = parser.parse_args()

    try:
        result = digipingrid(args.resolution, args.bbox, args.output_format)
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

h3grid_cli()

CLI interface for generating H3 grid.

Source code in vgrid/generator/h3grid.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def h3grid_cli():
    """CLI interface for generating H3 grid."""
    parser = argparse.ArgumentParser(description="Generate H3 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..15]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box: <min_lon> <min_lat> <max_lon> <max_lat> (default is the whole world)",
    )
    parser.add_argument(
        "-f", "--output_format", type=str, choices=OUTPUT_FORMATS, default="gpd"
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )

    args = parser.parse_args()
    try:
        result = h3grid(
            args.resolution,
            args.bbox,
            args.output_format,
            fix_antimeridian=args.fix_antimeridian,
        )
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

rhealpixgrid_cli()

CLI interface for generating rHEALPix grid.

Source code in vgrid/generator/rhealpixgrid.py
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
def rhealpixgrid_cli():
    """CLI interface for generating rHEALPix grid."""
    parser = argparse.ArgumentParser(description="Generate rHEALPix DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..15]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )

    args = parser.parse_args()
    resolution = args.resolution
    bbox = args.bbox if args.bbox else [-180, -90, 180, 90]
    output_format = args.output_format
    fix_antimeridian = args.fix_antimeridian
    try:
        result = rhealpixgrid(
            resolution, bbox, output_format, fix_antimeridian=fix_antimeridian
        )
        if output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

s2grid_cli()

CLI interface for generating S2 grid.

Source code in vgrid/generator/s2grid.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def s2grid_cli():
    """CLI interface for generating S2 grid."""
    parser = argparse.ArgumentParser(description="Generate S2 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..30]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )
    args = parser.parse_args()
    try:
        result = s2grid(
            args.resolution,
            args.bbox,
            args.output_format,
            fix_antimeridian=args.fix_antimeridian,
        )
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

h3_grid_ids(resolution, fix_antimeridian=None)

Generate a list of H3 cell IDs for the whole world at a given resolution.

Parameters:

Name Type Description Default
resolution int

H3 resolution [0..15]

required

Returns:

Type Description

list[str]: List of H3 cell IDs as strings

Source code in vgrid/generator/h3grid.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def h3_grid_ids(resolution, fix_antimeridian=None):
    """
    Generate a list of H3 cell IDs for the whole world at a given resolution.

    Args:
        resolution (int): H3 resolution [0..15]

    Returns:
        list[str]: List of H3 cell IDs as strings
    """
    resolution = validate_h3_resolution(resolution)
    total_cells = h3.get_num_cells(resolution)
    base_cells = h3.get_res0_cells()
    h3_ids = []
    with tqdm(total=total_cells, desc="Generating H3 IDs", unit=" cells") as pbar:
        for cell in base_cells:
            child_cells = h3.cell_to_children(cell, resolution)
            for child_cell in child_cells:
                h3_ids.append(str(child_cell))
                pbar.update(1)

    return h3_ids

h3_grid_within_bbox_ids(resolution, bbox, fix_antimeridian=None)

Generate a list of H3 cell IDs that intersect a bounding box.

Parameters:

Name Type Description Default
resolution int

H3 resolution [0..15]

required
bbox list[float]

[min_lon, min_lat, max_lon, max_lat]

required

Returns:

Type Description

list[str]: List of H3 cell IDs as strings that intersect the bbox

Source code in vgrid/generator/h3grid.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def h3_grid_within_bbox_ids(resolution, bbox, fix_antimeridian=None):
    """
    Generate a list of H3 cell IDs that intersect a bounding box.

    Args:
        resolution (int): H3 resolution [0..15]
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]

    Returns:
        list[str]: List of H3 cell IDs as strings that intersect the bbox
    """
    resolution = validate_h3_resolution(resolution)
    bbox_polygon = box(*bbox)
    distance = h3.average_hexagon_edge_length(resolution, unit="m") * 2
    bbox_buffer = geodesic_buffer(bbox_polygon, distance)
    bbox_buffer_cells = h3.geo_to_cells(bbox_buffer, resolution)
    total_cells = len(bbox_buffer_cells)
    h3_ids = []
    for bbox_buffer_cell in tqdm(
        bbox_buffer_cells, total=total_cells, desc="Generating H3 IDs"
    ):
        cell_polygon = h32geo(bbox_buffer_cell, fix_antimeridian=fix_antimeridian)
        if cell_polygon.intersects(bbox_polygon):
            h3_ids.append(str(bbox_buffer_cell))

    return h3_ids

h3grid(resolution, bbox=None, output_format='gpd', fix_antimeridian=None)

Generate H3 grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

H3 resolution [0..15]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format handled entirely by convert_to_output_format

'gpd'

Returns:

Type Description

Delegated to convert_to_output_format

Source code in vgrid/generator/h3grid.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def h3grid(resolution, bbox=None, output_format="gpd", fix_antimeridian=None):
    """
    Generate H3 grid for pure Python usage.

    Args:
        resolution (int): H3 resolution [0..15]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format handled entirely by convert_to_output_format

    Returns:
        Delegated to convert_to_output_format
    """
    if bbox is None:
        h3_gdf = h3_grid(resolution, fix_antimeridian=fix_antimeridian)
    else:
        h3_gdf = h3_grid_within_bbox(
            resolution, bbox, fix_antimeridian=fix_antimeridian
        )
    output_name = f"h3_grid_{resolution}"
    return convert_to_output_format(h3_gdf, output_format, output_name)

h3grid_cli()

CLI interface for generating H3 grid.

Source code in vgrid/generator/h3grid.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def h3grid_cli():
    """CLI interface for generating H3 grid."""
    parser = argparse.ArgumentParser(description="Generate H3 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..15]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box: <min_lon> <min_lat> <max_lon> <max_lat> (default is the whole world)",
    )
    parser.add_argument(
        "-f", "--output_format", type=str, choices=OUTPUT_FORMATS, default="gpd"
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )

    args = parser.parse_args()
    try:
        result = h3grid(
            args.resolution,
            args.bbox,
            args.output_format,
            fix_antimeridian=args.fix_antimeridian,
        )
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

S2 Grid Generator Module

Generates S2 DGGS grids for specified resolutions and bounding boxes with automatic cell generation and validation.

Key Functions: - s2_grid(): Main grid generation function with bounding box support - s2_grid_ids(): Returns list of S2 cell tokens for given resolution and bbox - s2grid(): User-facing function with multiple output formats - s2grid_cli(): Command-line interface for grid generation

Reference

s2_grid(resolution, bbox, fix_antimeridian=None)

Generate an S2 DGGS grid for a given resolution and bounding box. fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Args: resolution (int): S2 level [0..30] bbox (list[float]): [min_lon, min_lat, max_lon, max_lat] fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Returns: geopandas.GeoDataFrame: GeoDataFrame containing the S2 DGGS grid

Source code in vgrid/generator/s2grid.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def s2_grid(resolution, bbox, fix_antimeridian=None):
    """
    Generate an S2 DGGS grid for a given resolution and bounding box.
    fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Args:
        resolution (int): S2 level [0..30]
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]
        fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Returns:
        geopandas.GeoDataFrame: GeoDataFrame containing the S2 DGGS grid
    """
    resolution = validate_s2_resolution(resolution)
    min_lng, min_lat, max_lng, max_lat = bbox
    level = resolution
    cell_ids = []
    coverer = s2.RegionCoverer()
    coverer.min_level = level
    coverer.max_level = level
    # coverer.max_cells = 1000_000  # Adjust as needed
    # coverer.max_cells = 0  # Adjust as needed

    # Define the region to cover (in this example, we'll use the entire world)
    region = s2.LatLngRect(
        s2.LatLng.from_degrees(min_lat, min_lng),
        s2.LatLng.from_degrees(max_lat, max_lng),
    )

    # Get the covering cells
    covering = coverer.get_covering(region)

    # Convert the covering cells to S2 cell IDs
    for cell_id in covering:
        cell_ids.append(cell_id)

    s2_rows = []
    num_edges = 4

    for cell_id in tqdm(cell_ids, desc="Generating DGGS", unit=" cells"):
        # Generate a Shapely Polygon
        cell_polygon = s22geo(cell_id.to_token(), fix_antimeridian=fix_antimeridian)
        s2_token = cell_id.to_token()
        row = geodesic_dggs_to_geoseries(
            "s2", s2_token, resolution, cell_polygon, num_edges
        )
        s2_rows.append(row)

    return gpd.GeoDataFrame(s2_rows, geometry="geometry", crs="EPSG:4326")

s2_grid_ids(resolution, bbox, fix_antimeridian=None)

Return a list of S2 cell tokens for a given resolution and bounding box. fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Args: resolution (int): S2 level [0..30] bbox (list[float]): [min_lon, min_lat, max_lon, max_lat] fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Returns: list[str]: List of S2 cell tokens

Source code in vgrid/generator/s2grid.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def s2_grid_ids(resolution, bbox, fix_antimeridian=None):
    """
    Return a list of S2 cell tokens for a given resolution and bounding box.
    fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Args:
        resolution (int): S2 level [0..30]
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]
        fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Returns:
        list[str]: List of S2 cell tokens
    """
    resolution = validate_s2_resolution(resolution)
    min_lng, min_lat, max_lng, max_lat = bbox
    level = resolution
    coverer = s2.RegionCoverer()
    coverer.min_level = level
    coverer.max_level = level
    region = s2.LatLngRect(
        s2.LatLng.from_degrees(min_lat, min_lng),
        s2.LatLng.from_degrees(max_lat, max_lng),
    )
    covering = coverer.get_covering(region)
    return [cell_id.to_token(fix_antimeridian=fix_antimeridian) for cell_id in covering]

s2_grid_resample(resolution, geojson_features, fix_antimeridian=None)

Generate an S2 DGGS grid for a given resolution and GeoJSON features. fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Args: resolution (int): S2 level [0..30] geojson_features (dict): GeoJSON features fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Returns: geopandas.GeoDataFrame: GeoDataFrame containing the S2 DGGS grid

Source code in vgrid/generator/s2grid.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def s2_grid_resample(resolution, geojson_features, fix_antimeridian=None):
    """
    Generate an S2 DGGS grid for a given resolution and GeoJSON features.
    fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Args:
        resolution (int): S2 level [0..30]
        geojson_features (dict): GeoJSON features
        fix_antimeridian : Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Returns:
        geopandas.GeoDataFrame: GeoDataFrame containing the S2 DGGS grid
    """
    resolution = validate_s2_resolution(resolution)
    geometries = [
        shape(feature["geometry"]) for feature in geojson_features["features"]
    ]
    unified_geom = unary_union(geometries)

    # Step 2: Get bounding box from unified geometry
    min_lng, min_lat, max_lng, max_lat = unified_geom.bounds

    # Step 3: Configure the S2 coverer
    level = resolution
    coverer = s2.RegionCoverer()
    coverer.min_level = level
    coverer.max_level = level

    # Step 4: Create a LatLngRect from the bounding box
    region = s2.LatLngRect(
        s2.LatLng.from_degrees(min_lat, min_lng),
        s2.LatLng.from_degrees(max_lat, max_lng),
    )

    # Step 5: Get the covering cells
    covering = coverer.get_covering(region)

    s2_rows = []
    for cell_id in tqdm(covering, desc="Generating S2 DGGS", unit=" cells"):
        cell_polygon = s22geo(cell_id.to_token(), fix_antimeridian=fix_antimeridian)
        if cell_polygon.intersects(unified_geom):
            s2_token = cell_id.to_token(fix_antimeridian=fix_antimeridian)
            num_edges = 4
            row = geodesic_dggs_to_geoseries(
                "s2", s2_token, resolution, cell_polygon, num_edges
            )
            s2_rows.append(row)

    return gpd.GeoDataFrame(s2_rows, geometry="geometry", crs="EPSG:4326")

s2grid(resolution, bbox=None, output_format='gpd', fix_antimeridian=None)

Generate S2 grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

S2 resolution [0..30]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output output_format ('geojson', 'csv', etc.). Defaults to None (list of S2 tokens).

'gpd'
fix_antimeridian str

Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none. Defaults to None.

None
Source code in vgrid/generator/s2grid.py
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def s2grid(resolution, bbox=None, output_format="gpd", fix_antimeridian=None):
    """
    Generate S2 grid for pure Python usage.

    Args:
        resolution (int): S2 resolution [0..30]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output output_format ('geojson', 'csv', etc.). Defaults to None (list of S2 tokens).
        fix_antimeridian (str, optional): Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none. Defaults to None.
    Returns:
        dict or list: GeoJSON FeatureCollection, list of S2 tokens, or file path depending on output_format
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        num_cells = 6 * (4**resolution)
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
    gdf = s2_grid(resolution, bbox, fix_antimeridian=fix_antimeridian)
    output_name = f"s2_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

s2grid_cli()

CLI interface for generating S2 grid.

Source code in vgrid/generator/s2grid.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
def s2grid_cli():
    """CLI interface for generating S2 grid."""
    parser = argparse.ArgumentParser(description="Generate S2 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..30]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )
    args = parser.parse_args()
    try:
        result = s2grid(
            args.resolution,
            args.bbox,
            args.output_format,
            fix_antimeridian=args.fix_antimeridian,
        )
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

A5 Grid Generator Module

Generates A5 (Adaptive 5) DGGS grids for specified resolutions and bounding boxes with automatic cell generation and validation.

Key Functions: - a5_grid(): Main grid generation function with bounding box support - a5grid(): User-facing function with multiple output formats - a5grid_cli(): Command-line interface for grid generation

a5_grid_ids(resolution, bbox)

Generate a list of unique A5 cell IDs intersecting the given bounding box.

Note: Intentionally does not enforce MAX_CELLS limit for ID generation.

Parameters:

Name Type Description Default
resolution int

A5 resolution [0..29]

required
bbox list

[min_lon, min_lat, max_lon, max_lat]

required

Returns:

Type Description

list[str]: List of A5 cell IDs

Source code in vgrid/generator/a5grid.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def a5_grid_ids(resolution, bbox):
    """
    Generate a list of unique A5 cell IDs intersecting the given bounding box.

    Note: Intentionally does not enforce MAX_CELLS limit for ID generation.

    Args:
        resolution (int): A5 resolution [0..29]
        bbox (list): [min_lon, min_lat, max_lon, max_lat]

    Returns:
        list[str]: List of A5 cell IDs
    """
    resolution = validate_a5_resolution(resolution)

    min_lng, min_lat, max_lng, max_lat = bbox

    if resolution == 0:
        lon_width = 35
        lat_width = 35
    elif resolution == 1:
        lon_width = 18
        lat_width = 18
    elif resolution == 2:
        lon_width = 10
        lat_width = 10
    elif resolution == 3:
        lon_width = 5
        lat_width = 5
    elif resolution > 3:
        base_width = 5
        factor = 0.5 ** (resolution - 3)
        lon_width = base_width * factor
        lat_width = base_width * factor

    longitudes = []
    latitudes = []

    lon = min_lng
    while lon < max_lng:
        longitudes.append(lon)
        lon += lon_width

    lat = min_lat
    while lat < max_lat:
        latitudes.append(lat)
        lat += lat_width

    seen_ids = set()
    ids = []
    total_cells = len(longitudes) * len(latitudes)
    with tqdm(total=total_cells, desc="Generating A5 IDs", unit=" cells") as pbar:
        for lon in longitudes:
            for lat in latitudes:
                min_lon = lon
                min_lat = lat
                max_lon = lon + lon_width
                max_lat = lat + lat_width

                centroid_lat = (min_lat + max_lat) / 2
                centroid_lon = (min_lon + max_lon) / 2

                try:
                    a5_hex = latlon2a5(centroid_lat, centroid_lon, resolution)
                    if a5_hex and a5_hex not in seen_ids:
                        seen_ids.add(a5_hex)
                        ids.append(a5_hex)
                except Exception:
                    pass
                finally:
                    pbar.update(1)

    return ids

a5grid(resolution, bbox=None, output_format='gpd', options=None, split_antimeridian=False)

Generate A5 grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

A5 resolution [0..30]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format (gpd, gdf, geojson_dict/json_dict, geojson/json, csv, shp/shapefile, gpkg/geopackage, parquet/geoparquet, or None)

'gpd'
options dict

Options for a52geo.

None
split_antimeridian bool

When True, apply antimeridian fixing to the resulting polygons.

False
Source code in vgrid/generator/a5grid.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def a5grid(
    resolution, bbox=None, output_format="gpd", options=None, split_antimeridian=False
):
    """
    Generate A5 grid for pure Python usage.

    Args:
        resolution (int): A5 resolution [0..30]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format (gpd, gdf, geojson_dict/json_dict, geojson/json, csv, shp/shapefile, gpkg/geopackage, parquet/geoparquet, or None)
        options (dict, optional): Options for a52geo.
        split_antimeridian (bool, optional): When True, apply antimeridian fixing to the resulting polygons.
    Returns:
        Depends on output_format. If None, returns a GeoDataFrame (gpd)
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        num_cells = get_num_cells(resolution)
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )

    a5_gdf = a5_grid(resolution, bbox, options, split_antimeridian=split_antimeridian)
    output_name = f"a5_grid_{resolution}"
    return convert_to_output_format(a5_gdf, output_format, output_name)

a5grid_cli()

CLI interface for generating A5 DGGS.

Source code in vgrid/generator/a5grid.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def a5grid_cli():
    """CLI interface for generating A5 DGGS."""
    parser = argparse.ArgumentParser(description="Generate A5 DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..29]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    args = parser.parse_args()
    try:
        result = a5grid(args.resolution, args.bbox, args.output_format)
        if args.output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

rHEALPix Grid Generator Module

Generates rHEALPix DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical equal-area grid system.

Key Functions: - rhealpix_grid(): Main grid generation function for whole world - rhealpix_grid_within_bbox(): Grid generation within bounding box - rhealpixgrid(): User-facing function with multiple output formats - rhealpixgrid_cli(): Command-line interface for grid generation

rhealpix_grid_ids(resolution)

Return a list of rHEALPix cell IDs for the whole world at a given resolution.

Source code in vgrid/generator/rhealpixgrid.py
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def rhealpix_grid_ids(resolution):
    """
    Return a list of rHEALPix cell IDs for the whole world at a given resolution.
    """
    resolution = validate_rhealpix_resolution(resolution)
    ids = []
    total_cells = rhealpix_dggs.num_cells(resolution)
    for rhealpix_cell in tqdm(
        rhealpix_dggs.grid(resolution),
        total=total_cells,
        desc="Generating rHEALPix IDs",
        unit=" cells",
    ):
        ids.append(str(rhealpix_cell))
    return ids

rhealpix_grid_within_bbox_ids(resolution, bbox)

Return a list of rHEALPix cell IDs intersecting the given bounding box at a given resolution.

Source code in vgrid/generator/rhealpixgrid.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
def rhealpix_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of rHEALPix cell IDs intersecting the given bounding box at a given resolution.
    """
    resolution = validate_rhealpix_resolution(resolution)
    bbox_polygon = box(*bbox)
    bbox_center_lon = bbox_polygon.centroid.x
    bbox_center_lat = bbox_polygon.centroid.y
    seed_point = (bbox_center_lon, bbox_center_lat)
    seed_cell = rhealpix_dggs.cell_from_point(resolution, seed_point, plane=False)
    seed_cell_id = str(seed_cell)
    seed_cell_polygon = rhealpix2geo(seed_cell_id)
    if seed_cell_polygon.contains(bbox_polygon):
        return [seed_cell_id]

    # Store only intersecting cell IDs (avoid double processing)
    intersecting_ids = []
    covered_cells = set()
    queue = deque([seed_cell])  # Use deque for BFS

    while queue:
        current_cell = queue.popleft()  # BFS: FIFO
        current_cell_id = str(current_cell)
        if current_cell_id in covered_cells:
            continue
        covered_cells.add(current_cell_id)

        # Convert polygon once and check intersection
        cell_polygon = rhealpix2geo(current_cell_id)
        if cell_polygon.intersects(bbox_polygon):
            # Add to results immediately (no need to reconstruct later)
            intersecting_ids.append(current_cell_id)

            # Add neighbors to queue
            neighbors = current_cell.neighbors(plane=False)
            for _, neighbor in neighbors.items():
                neighbor_id = str(neighbor)
                if neighbor_id not in covered_cells:
                    queue.append(neighbor)

    return intersecting_ids

rhealpixgrid(resolution, bbox=None, output_format='gpd', fix_antimeridian=None)

Generate rHEALPix grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

rHEALPix resolution [0..15]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output output_format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of rHEALPix IDs). Defaults to None.

'gpd'
fix_antimeridian (Antimeridian fixing method

shift, shift_balanced, shift_west, shift_east, split, none, optional): When True, apply antimeridian fixing to the resulting polygons. Defaults to False when None or omitted.

required

Returns:

Type Description

dict, list, or str: Output in the requested output_format (GeoJSON FeatureCollection, list of IDs, file path, etc.)

Source code in vgrid/generator/rhealpixgrid.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def rhealpixgrid(resolution, bbox=None, output_format="gpd", fix_antimeridian=None):
    """
    Generate rHEALPix grid for pure Python usage.

    Args:
        resolution (int): rHEALPix resolution [0..15]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output output_format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of rHEALPix IDs). Defaults to None.
        fix_antimeridian (Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none, optional): When True, apply antimeridian fixing to the resulting polygons.
            Defaults to False when None or omitted.

    Returns:
        dict, list, or str: Output in the requested output_format (GeoJSON FeatureCollection, list of IDs, file path, etc.)
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        num_cells = rhealpix_dggs.num_cells(resolution)
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = rhealpix_grid(resolution, fix_antimeridian=fix_antimeridian)
    else:
        gdf = rhealpix_grid_within_bbox(
            resolution, bbox, fix_antimeridian=fix_antimeridian
        )
    output_name = f"rhealpix_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

rhealpixgrid_cli()

CLI interface for generating rHEALPix grid.

Source code in vgrid/generator/rhealpixgrid.py
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
def rhealpixgrid_cli():
    """CLI interface for generating rHEALPix grid."""
    parser = argparse.ArgumentParser(description="Generate rHEALPix DGGS.")
    parser.add_argument(
        "-r", "--resolution", type=int, required=True, help="Resolution [0..15]"
    )
    parser.add_argument(
        "-b",
        "--bbox",
        type=float,
        nargs=4,
        help="Bounding box in the output_format: min_lon min_lat max_lon max_lat (default is the whole world)",
    )
    parser.add_argument(
        "-f",
        "--output_format",
        type=str,
        choices=OUTPUT_FORMATS,
        default="gpd",
    )
    parser.add_argument(
        "-fix",
        "--fix_antimeridian",
        type=str,
        choices=[
            "shift",
            "shift_balanced",
            "shift_west",
            "shift_east",
            "split",
            "none",
        ],
        default=None,
        help="Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none",
    )

    args = parser.parse_args()
    resolution = args.resolution
    bbox = args.bbox if args.bbox else [-180, -90, 180, 90]
    output_format = args.output_format
    fix_antimeridian = args.fix_antimeridian
    try:
        result = rhealpixgrid(
            resolution, bbox, output_format, fix_antimeridian=fix_antimeridian
        )
        if output_format in STRUCTURED_FORMATS:
            print(result)
    except ValueError as e:
        print(f"Error: {str(e)}")
        return

ISEA4T Grid Generator Module

Generates ISEA4T DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical triangular grid system.

Key Functions: - isea4t_grid(): Main grid generation function for whole world - isea4t_grid_within_bbox(): Grid generation within bounding box - isea4tgrid(): User-facing function with multiple output formats - isea4tgrid_cli(): Command-line interface for grid generation

get_isea4t_children_cells(base_cells, target_resolution)

Recursively generate DGGS cells for the desired resolution.

Source code in vgrid/generator/isea4tgrid.py
40
41
42
43
44
45
46
47
48
49
50
51
def get_isea4t_children_cells(base_cells, target_resolution):
    """
    Recursively generate DGGS cells for the desired resolution.
    """
    current_cells = base_cells
    for res in range(target_resolution):
        next_cells = []
        for cell in current_cells:
            children = isea4t_dggs.get_dggs_cell_children(DggsCell(cell))
            next_cells.extend([child._cell_id for child in children])
        current_cells = next_cells
    return current_cells

isea4t_grid_ids(resolution)

Return a list of ISEA4T cell IDs for the whole world at a given resolution.

Source code in vgrid/generator/isea4tgrid.py
123
124
125
126
127
128
129
def isea4t_grid_ids(resolution):
    """
    Return a list of ISEA4T cell IDs for the whole world at a given resolution.
    """
    resolution = validate_isea4t_resolution(resolution)
    children = get_isea4t_children_cells(ISEA4T_BASE_CELLS, resolution)
    return [str(cid) for cid in children]

isea4t_grid_within_bbox_ids(resolution, bbox)

Return a list of ISEA4T cell IDs intersecting the given bounding box at a given resolution.

Source code in vgrid/generator/isea4tgrid.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def isea4t_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of ISEA4T cell IDs intersecting the given bounding box at a given resolution.
    """
    resolution = validate_isea4t_resolution(resolution)
    accuracy = ISEA4T_RES_ACCURACY_DICT.get(resolution)
    bounding_box = box(*bbox)
    bounding_box_wkt = bounding_box.wkt
    isea4t_shapes = isea4t_dggs.convert_shape_string_to_dggs_shapes(
        bounding_box_wkt, ShapeStringFormat.WKT, accuracy
    )
    isea4t_shape = isea4t_shapes[0]
    bbox_cells = isea4t_shape.get_shape().get_outer_ring().get_cells()
    bounding_cell = isea4t_dggs.get_bounding_dggs_cell(bbox_cells)
    bounding_children = get_isea4t_children_cells_within_bbox(
        bounding_cell.get_cell_id(), bounding_box, resolution
    )
    return list(bounding_children or [])

isea4tgrid(resolution, bbox=None, output_format='gpd', fix_antimeridian=None)

Generate ISEA4T DGGS grid for pure Python usage. Args: resolution (int): ISEA4T resolution [0..39] bbox (list[float]): [min_lon, min_lat, max_lon, max_lat] output_format (str): Output output_format ('geojson', 'csv', etc.) fix_antimeridian (str): Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Returns: dict or list: GeoJSON FeatureCollection, list of ISEA4T cell IDs, or file path depending on output_format

Source code in vgrid/generator/isea4tgrid.py
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def isea4tgrid(resolution, bbox=None, output_format="gpd", fix_antimeridian=None):
    """
    Generate ISEA4T DGGS grid for pure Python usage.
    Args:
        resolution (int): ISEA4T resolution [0..39]
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]
        output_format (str): Output output_format ('geojson', 'csv', etc.)
        fix_antimeridian (str): Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
    Returns:
        dict or list: GeoJSON FeatureCollection, list of ISEA4T cell IDs, or file path depending on output_format
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        total_cells = 20 * (4**resolution)
        if total_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {total_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = isea4t_grid(resolution, fix_antimeridian=fix_antimeridian)
    else:
        gdf = isea4t_grid_within_bbox(
            resolution, bbox, fix_antimeridian=fix_antimeridian
        )
    output_name = f"isea4t_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

ISEA3H Grid Generator Module

Generates ISEA3H DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical hexagonal grid system.

Key Functions: - isea3h_grid(): Main grid generation function for whole world - isea3h_grid_within_bbox(): Grid generation within bounding box - isea3hgrid(): User-facing function with multiple output formats - isea3hgrid_cli(): Command-line interface for grid generation

get_isea3h_children_cells(base_cells, target_resolution)

Recursively generate DGGS cells for the desired resolution, returning only the cells at the target resolution.

Source code in vgrid/generator/isea3hgrid.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def get_isea3h_children_cells(base_cells, target_resolution):
    """
    Recursively generate DGGS cells for the desired resolution, returning only the cells at the target resolution.
    """
    current_cells = base_cells
    for res in range(target_resolution):
        next_cells = []
        seen_cells = set()
        for cell in current_cells:
            children = isea3h_dggs.get_dggs_cell_children(DggsCell(cell))
            for child in children:
                if child._cell_id not in seen_cells:
                    seen_cells.add(child._cell_id)
                    next_cells.append(child._cell_id)
        current_cells = next_cells
    return current_cells

get_isea3h_children_cells_within_bbox(bounding_cell, bbox, target_resolution)

Recursively generate DGGS cells within a bounding box, returning only the cells at the target resolution.

Source code in vgrid/generator/isea3hgrid.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def get_isea3h_children_cells_within_bbox(bounding_cell, bbox, target_resolution):
    """
    Recursively generate DGGS cells within a bounding box, returning only the cells at the target resolution.
    """
    current_cells = [
        bounding_cell
    ]  # Start with a list containing the single bounding cell
    bounding_cell2point = isea3h_dggs.convert_dggs_cell_to_point(
        DggsCell(bounding_cell)
    )
    accuracy = bounding_cell2point._accuracy
    bounding_resolution = ISEA3H_ACCURACY_RES_DICT.get(accuracy)

    if bounding_resolution <= target_resolution:
        for res in range(bounding_resolution, target_resolution):
            next_cells = []
            seen_cells = set()
            for cell in current_cells:
                # Get the child cells for the current cell
                children = isea3h_dggs.get_dggs_cell_children(DggsCell(cell))
                for child in children:
                    if child._cell_id not in seen_cells:
                        child_shape = isea3h2geo(child._cell_id)
                        if child_shape.intersects(bbox):
                            seen_cells.add(child._cell_id)
                            next_cells.append(child._cell_id)
            if not next_cells:  # Break early if no cells remain
                break
            current_cells = (
                next_cells  # Update current_cells to process the next level of children
            )

        return current_cells
    else:
        # print('Bounding box area is < 0.028 square meters. Please select a bigger bounding box')
        return None

isea3h_grid(resolution, fix_antimeridian=None)

Generate DGGS cells and convert them to GeoJSON features.

Source code in vgrid/generator/isea3hgrid.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def isea3h_grid(resolution, fix_antimeridian=None):
    """
    Generate DGGS cells and convert them to GeoJSON features.
    """
    resolution = validate_isea3h_resolution(resolution)
    children = get_isea3h_children_cells(ISEA3H_BASE_CELLS, resolution)
    records = []
    for child in tqdm(children, desc="Generating ISEA3H DGGS", unit=" cells"):
        try:
            isea3h_cell = DggsCell(child)
            isea3h_id = isea3h_cell.get_cell_id()
            cell_polygon = isea3h2geo(isea3h_id, fix_antimeridian=fix_antimeridian)
            num_edges = 6 if resolution > 0 else 3
            record = geodesic_dggs_to_geoseries(
                "isea3h", isea3h_id, resolution, cell_polygon, num_edges
            )
            records.append(record)
        except Exception as e:
            print(f"Error generating ISEA3H DGGS cell {child}: {e}")
            continue
    return gpd.GeoDataFrame(records, geometry="geometry", crs="EPSG:4326")

isea3h_grid_ids(resolution)

Return a list of ISEA3H cell IDs for the whole world at a given resolution.

Source code in vgrid/generator/isea3hgrid.py
152
153
154
155
156
157
158
def isea3h_grid_ids(resolution):
    """
    Return a list of ISEA3H cell IDs for the whole world at a given resolution.
    """
    resolution = validate_isea3h_resolution(resolution)
    children = get_isea3h_children_cells(ISEA3H_BASE_CELLS, resolution)
    return [str(cid) for cid in children]

isea3h_grid_within_bbox_ids(resolution, bbox)

Return a list of ISEA3H cell IDs intersecting the given bounding box at a given resolution.

Source code in vgrid/generator/isea3hgrid.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def isea3h_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of ISEA3H cell IDs intersecting the given bounding box at a given resolution.
    """
    resolution = validate_isea3h_resolution(resolution)
    accuracy = ISEA3H_RES_ACCURACY_DICT.get(resolution)
    bounding_box = box(*bbox)
    bounding_box_wkt = bounding_box.wkt
    shapes = isea3h_dggs.convert_shape_string_to_dggs_shapes(
        bounding_box_wkt, ShapeStringFormat.WKT, accuracy
    )
    shape = shapes[0]
    bbox_cells = shape.get_shape().get_outer_ring().get_cells()
    bounding_cell = isea3h_dggs.get_bounding_dggs_cell(bbox_cells)
    bounding_children_cells = get_isea3h_children_cells_within_bbox(
        bounding_cell.get_cell_id(), bounding_box, resolution
    )
    return list(bounding_children_cells or [])

isea3hgrid(resolution, bbox=None, output_format='gpd', fix_antimeridian=None)

Generate ISEA3H grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

ISEA3H resolution [0..40]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output output_format ('geojson', 'csv', etc). Defaults to None (list of IDs).

'gpd'
fix_antimeridian str

Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none Defaults to False when None or omitted.

None

Returns:

Type Description

dict or list: GeoJSON FeatureCollection, file path, or list of IDs depending on output_format

Source code in vgrid/generator/isea3hgrid.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def isea3hgrid(resolution, bbox=None, output_format="gpd", fix_antimeridian=None):
    """
    Generate ISEA3H grid for pure Python usage.

    Args:
        resolution (int): ISEA3H resolution [0..40]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output output_format ('geojson', 'csv', etc). Defaults to None (list of IDs).
        fix_antimeridian (str, optional): Antimeridian fixing method: shift, shift_balanced, shift_west, shift_east, split, none
            Defaults to False when None or omitted.

    Returns:
        dict or list: GeoJSON FeatureCollection, file path, or list of IDs depending on output_format
    """
    # Allow running on all platforms

    if bbox is None:
        bbox = [-180, -90, 180, 90]
        total_cells = 20 * (7**resolution)
        if total_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {total_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = isea3h_grid(resolution, fix_antimeridian=fix_antimeridian)
    else:
        gdf = isea3h_grid_within_bbox(
            resolution, bbox, fix_antimeridian=fix_antimeridian
        )

    output_name = f"isea3h_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

EASE Grid Generator Module

Generates EASE (Equal-Area Scalable Earth) DGGS grids for specified resolutions with automatic cell generation and validation using equal-area projection system.

Key Functions: - ease_grid(): Main grid generation function for whole world - ease_grid_within_bbox(): Grid generation within bounding box - easegrid(): User-facing function with multiple output formats - easegrid_cli(): Command-line interface for grid generation

ease_grid_ids(resolution)

Return a list of EASE-DGGS cell IDs for the whole world at a given resolution.

Parameters:

Name Type Description Default
resolution int

EASE resolution [0..6]

required

Returns:

Type Description

list[str]: List of EASE cell IDs

Source code in vgrid/generator/easegrid.py
76
77
78
79
80
81
82
83
84
85
86
87
def ease_grid_ids(resolution):
    """
    Return a list of EASE-DGGS cell IDs for the whole world at a given resolution.

    Args:
        resolution (int): EASE resolution [0..6]

    Returns:
        list[str]: List of EASE cell IDs
    """
    resolution = validate_ease_resolution(resolution)
    return get_ease_cells(resolution)

ease_grid_within_bbox_ids(resolution, bbox)

Return a list of EASE-DGGS cell IDs that intersect a bounding box.

Parameters:

Name Type Description Default
resolution int

EASE resolution [0..6]

required
bbox list[float]

[min_lon, min_lat, max_lon, max_lat]

required

Returns:

Type Description

list[str]: List of EASE cell IDs intersecting the bbox

Source code in vgrid/generator/easegrid.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def ease_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of EASE-DGGS cell IDs that intersect a bounding box.

    Args:
        resolution (int): EASE resolution [0..6]
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]

    Returns:
        list[str]: List of EASE cell IDs intersecting the bbox
    """
    resolution = validate_ease_resolution(resolution)
    cells_result = get_ease_cells_bbox(resolution, bbox)
    cells = (cells_result or {}).get("result", {}).get("data", [])
    return cells

get_ease_cells(resolution)

Generate a list of cell IDs based on the resolution, row, and column.

Source code in vgrid/generator/easegrid.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def get_ease_cells(resolution):
    """
    Generate a list of cell IDs based on the resolution, row, and column.
    """
    n_row = levels_specs[resolution]["n_row"]
    n_col = levels_specs[resolution]["n_col"]

    # Generate list of cell IDs
    cell_ids = []

    # Loop through all rows and columns at the specified resolution
    for row in range(n_row):
        for col in range(n_col):
            # Generate base ID (e.g., L0.RRRCCC for res=0)
            base_id = f"L{resolution}.{row:03d}{col:03d}"

            # Add additional ".RC" for each higher resolution
            cell_id = base_id
            for i in range(1, resolution + 1):
                cell_id += f".{row:1d}{col:1d}"  # For res=1: L0.RRRCCC.RC, res=2: L0.RRRCCC.RC.RC, etc.

            # Append the generated cell ID to the list
            cell_ids.append(cell_id)

    return cell_ids

DGGAL Grid Generator Module

Generates DGGAL (Discrete Global Grids with Adaptive Localization) grids for multiple grid types with automatic cell generation and validation.

Key Functions: - dggalgen(): User-facing function with multiple output formats - dggalgen_cli(): Command-line interface for grid generation

dggalgen(dggs_type='gnosis', resolution=1, bbox=None, compact=False, output_format=None, split_antimeridian=False)

Generate a DGGAL grid using the dggal library directly.

When output_format is provided, save to the current folder using a predefined name (e.g., "grid.*"), mirroring h3grid behavior. Returns either a GeoDataFrame, a path/string, a dict, or a list depending on output_format.

Parameters

dggs_type : str, default "gnosis" DGGAL DGGS type. resolution : int, default 1 Resolution level. bbox : tuple[float, float, float, float] | None, optional Bounding box as (min_lon, min_lat, max_lon, max_lat). compact : bool, default False Whether to compact zones. output_format : str | None, optional Output format. split_antimeridian : bool, default False When True, apply antimeridian fixing to the resulting polygons.

Source code in vgrid/generator/dggalgen.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def dggalgen(
    dggs_type: str = "gnosis",
    resolution: int = 1,
    bbox: tuple[float, float, float, float] | None = None,
    compact: bool = False,
    output_format: str | None = None,
    split_antimeridian: bool = False,
):
    """
    Generate a DGGAL grid using the dggal library directly.

    When output_format is provided, save to the current folder using a predefined
    name (e.g., "<dggs_type>_grid_<resolution>.*"), mirroring h3grid behavior.
    Returns either a GeoDataFrame, a path/string, a dict, or a list depending on output_format.

    Parameters
    ----------
    dggs_type : str, default "gnosis"
        DGGAL DGGS type.
    resolution : int, default 1
        Resolution level.
    bbox : tuple[float, float, float, float] | None, optional
        Bounding box as (min_lon, min_lat, max_lon, max_lat).
    compact : bool, default False
        Whether to compact zones.
    output_format : str | None, optional
        Output format.
    split_antimeridian : bool, default False
        When True, apply antimeridian fixing to the resulting polygons.
    """

    # Validate resolution against per-type bounds
    dggs_type = validate_dggal_type(dggs_type)
    resolution = validate_dggal_resolution(dggs_type, resolution)

    # Create the appropriate DGGS instance
    dggs_class_name = DGGAL_TYPES[dggs_type]["class_name"]
    dggrs = globals()[dggs_class_name]()

    # Set up bbox for listZones if provided
    geo_extent = wholeWorld  # Default to whole world
    if bbox:
        # bbox should be (min_lon, min_lat, max_lon, max_lat)
        min_lon, min_lat, max_lon, max_lat = bbox
        # Validate bbox coordinates
        valid_lat = (-90 < min_lat < 90) and (-90 < max_lat < 90)
        if valid_lat:
            ll = GeoPoint(min_lat, min_lon)
            ur = GeoPoint(max_lat, max_lon)
            geo_extent = GeoExtent(ll, ur)
        else:
            print(
                "Invalid bounding box coordinates, using whole world", file=sys.stderr
            )
            geo_extent = wholeWorld
    # Call listZones to get all zones at the specified resolution
    zones = dggrs.listZones(resolution, geo_extent)
    # Check if zones is None or empty before compacting
    if zones is None or len(zones) == 0:
        print("No zones found", file=sys.stderr)
        return None

    # Only compact if requested and zones exist
    if compact:
        compacted_zones = dggrs.compactZones(zones)
        if compacted_zones is not None:
            zones = compacted_zones

    # Zones found successfully, proceed with processing

    # Process zones directly with tqdm progress bar
    dggal_records = []
    options = {}

    for zone in tqdm(zones, desc=f"Generating {dggs_type.upper()} DGGS"):
        try:
            zone_id = dggrs.getZoneTextID(zone)
            zone_resolution = dggrs.getZoneLevel(zone)
            num_edges = dggrs.countZoneEdges(zone)

            # Convert zone to geometry using dggal2geo
            cell_polygon = dggal2geo(
                dggs_type, zone_id, options, split_antimeridian=split_antimeridian
            )

            # Create record using geodesic_dggs_to_geoseries
            record = geodesic_dggs_to_geoseries(
                f"dggal_{dggs_type}", zone_id, zone_resolution, cell_polygon, num_edges
            )
            dggal_records.append(record)

        except Exception as e:
            print(f"Error processing zone: {e}", file=sys.stderr)
            continue

    if dggal_records:
        # Create GeoDataFrame from records
        gdf = gpd.GeoDataFrame(dggal_records, geometry="geometry", crs="EPSG:4326")
        base_name = f"{dggs_type}_grid_{resolution}"
        return dggal_convert_to_output_format(
            gdf, output_format, output_name=base_name
        )
    else:
        print("No valid zones found for the specified parameters.", file=sys.stderr)
        return None

DGGRID Grid Generator Module

Generates DGGRID grids for multiple grid types with automatic cell generation and validation using the DGGRID library.

Key Functions: - generate_grid(): Core grid generation function with DGGRID instance - dggridgen(): User-facing function with multiple output formats - dggridgen_cli(): Command-line interface for grid generation

dggridgen(dggrid_instance, dggs_type, resolution, bbox=None, output_address_type=None, output_format='gpd', split_antimeridian=False, aggregate=False)

Generate DGGRID grid for pure Python usage.

Parameters:

Name Type Description Default
dggrid_instance

DGGRID instance for grid operations

required
dggs_type str

DGGS type from dggs_types

required
resolution int

Resolution level

required
bbox list

Bounding box [min_lat, min_lon, max_lat, max_lon]. Defaults to None (whole world).

None
output_address_type str

Address type for output. Defaults to None.

None
output_format str

Output format handled entirely by convert_to_output_format

'gpd'
split_antimeridian bool

When True, apply antimeridian fixing to the resulting polygons. Defaults to False when None or omitted.

False

Returns:

Type Description

Delegated to convert_to_output_format

Source code in vgrid/generator/dggridgen.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def dggridgen(
    dggrid_instance,
    dggs_type,
    resolution,
    bbox=None,
    output_address_type=None,
    output_format="gpd",
    split_antimeridian=False,
    aggregate=False,
):
    """
    Generate DGGRID grid for pure Python usage.

    Args:
        dggrid_instance: DGGRID instance for grid operations
        dggs_type (str): DGGS type from dggs_types
        resolution (int): Resolution level
        bbox (list, optional): Bounding box [min_lat, min_lon, max_lat, max_lon]. Defaults to None (whole world).
        output_address_type (str, optional): Address type for output. Defaults to None.
        output_format (str, optional): Output format handled entirely by convert_to_output_format
        split_antimeridian (bool, optional): When True, apply antimeridian fixing to the resulting polygons.
            Defaults to False when None or omitted.

    Returns:
        Delegated to convert_to_output_format
    """
    gdf = generate_grid(
        dggrid_instance,
        dggs_type,
        resolution,
        bbox,
        output_address_type,
        split_antimeridian=split_antimeridian,
        aggregate=aggregate,
    )
    output_name = f"dggrid_{dggs_type}_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

QTM Grid Generator Module

Generates QTM (Quaternary Triangular Mesh) DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical triangular grid system.

Key Functions: - qtm_grid(): Main grid generation function for whole world - qtm_grid_within_bbox(): Grid generation within bounding box - qtmgrid(): User-facing function with multiple output formats - qtmgrid_cli(): Command-line interface for grid generation

OLC Grid Generator Module

Generates OLC (Open Location Code) DGGS grids for specified resolutions with automatic cell generation and validation using human-readable location codes.

Key Functions: - olc_grid(): Main grid generation function for whole world - olc_grid_within_bbox(): Grid generation within bounding box - olcgrid(): User-facing function with multiple output formats - olcgrid_cli(): Command-line interface for grid generation

olc_grid_ids(resolution)

Return a list of OLC (Plus Code) IDs for the whole world at the given resolution.

Source code in vgrid/generator/olcgrid.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
def olc_grid_ids(resolution):
    """
    Return a list of OLC (Plus Code) IDs for the whole world at the given resolution.
    """
    resolution = validate_olc_resolution(resolution)
    sw_lat, sw_lng = -90, -180
    ne_lat, ne_lng = 90, 180

    area = olc.decode(olc.encode(sw_lat, sw_lng, resolution))
    lat_step = area.latitudeHi - area.latitudeLo
    lng_step = area.longitudeHi - area.longitudeLo

    ids = []
    total_lat_steps = int((ne_lat - sw_lat) / lat_step)
    total_lng_steps = int((ne_lng - sw_lng) / lng_step)
    total_steps = total_lat_steps * total_lng_steps

    with tqdm(total=total_steps, desc="Generating OLC IDs", unit=" cells") as pbar:
        lat = sw_lat
        while lat < ne_lat:
            lng = sw_lng
            while lng < ne_lng:
                center_lat = lat + lat_step / 2
                center_lon = lng + lng_step / 2
                olc_id = olc.encode(center_lat, center_lon, resolution)
                ids.append(olc_id)
                lng += lng_step
                pbar.update(1)
            lat += lat_step

    return ids

olc_grid_resample(resolution, geojson_features)

Generate a grid of Open Location Codes (Plus Codes) within the specified GeoJSON features.

Source code in vgrid/generator/olcgrid.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def olc_grid_resample(resolution, geojson_features):
    """
    Generate a grid of Open Location Codes (Plus Codes) within the specified GeoJSON features.
    """
    resolution = validate_olc_resolution(resolution)
    # Step 1: Union all input geometries
    geometries = [
        shape(feature["geometry"]) for feature in geojson_features["features"]
    ]
    unified_geom = unary_union(geometries)

    # Step 2: Generate base cells at the lowest resolution (e.g., resolution 2)
    base_resolution = 2
    base_gdf = olc_grid(base_resolution, verbose=True)

    # Step 3: Identify seed cells that intersect with the unified geometry
    seed_cells = []
    for idx, base_cell in base_gdf.iterrows():
        base_cell_poly = base_cell["geometry"]
        if unified_geom.intersects(base_cell_poly):
            seed_cells.append(base_cell)

    refined_records = []

    # Step 4: Refine seed cells to the desired resolution
    for seed_cell in seed_cells:
        seed_cell_poly = seed_cell["geometry"]

        if seed_cell_poly.contains(unified_geom) and resolution == base_resolution:
            refined_records.append(seed_cell)
        else:
            refined_records.extend(
                olc_refine_cell(
                    seed_cell_poly.bounds, base_resolution, resolution, unified_geom
                )
            )

    # Step 5: Filter features to keep only those at the desired resolution and remove duplicates
    gdf = gpd.GeoDataFrame(refined_records, geometry="geometry", crs="EPSG:4326")
    gdf = gdf[gdf["resolution"] == resolution]
    gdf = gdf.drop_duplicates(subset=["olc"])

    return gdf

olc_grid_within_bbox(resolution, bbox)

Generate a grid of Open Location Codes (Plus Codes) within the specified bounding box.

Source code in vgrid/generator/olcgrid.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
def olc_grid_within_bbox(resolution, bbox):
    """
    Generate a grid of Open Location Codes (Plus Codes) within the specified bounding box.
    """
    resolution = validate_olc_resolution(resolution)
    min_lon, min_lat, max_lon, max_lat = bbox
    bbox_poly = box(min_lon, min_lat, max_lon, max_lat)

    # Step 1: Generate base cells at the lowest resolution (e.g., resolution 2)
    base_resolution = 2
    base_gdf = olc_grid(base_resolution, verbose=False)

    # Step 2: Identify seed cells that intersect with the bounding box
    seed_cells = []
    for idx, base_cell in base_gdf.iterrows():
        base_cell_poly = base_cell["geometry"]
        if bbox_poly.intersects(base_cell_poly):
            seed_cells.append(base_cell)

    refined_records = []

    # Step 3: Iterate over seed cells and refine to the output resolution
    for seed_cell in seed_cells:
        seed_cell_poly = seed_cell["geometry"]

        if seed_cell_poly.contains(bbox_poly) and resolution == base_resolution:
            # Append the seed cell directly if fully contained and resolution matches
            refined_records.append(seed_cell)
        else:
            # Refine the seed cell to the output resolution and add it to the output
            refined_records.extend(
                olc_refine_cell(
                    seed_cell_poly.bounds, base_resolution, resolution, bbox_poly
                )
            )

    gdf = gpd.GeoDataFrame(refined_records, geometry="geometry", crs="EPSG:4326")
    gdf = gdf[gdf["resolution"] == resolution]
    gdf = gdf.drop_duplicates(subset=["olc"])

    return gdf

olc_grid_within_bbox_ids(resolution, bbox)

Return a list of OLC (Plus Code) IDs within a bounding box at the given resolution.

Source code in vgrid/generator/olcgrid.py
266
267
268
269
270
271
272
273
274
def olc_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of OLC (Plus Code) IDs within a bounding box at the given resolution.
    """
    resolution = validate_olc_resolution(resolution)
    gdf = olc_grid_within_bbox(resolution, bbox)
    if gdf is None or gdf.empty:
        return []
    return list(gdf["olc"].drop_duplicates())

olc_refine_cell(bounds, current_resolution, target_resolution, bbox_poly)

Refine a cell defined by bounds to the target resolution, recursively refining intersecting cells.

Source code in vgrid/generator/olcgrid.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def olc_refine_cell(bounds, current_resolution, target_resolution, bbox_poly):
    """
    Refine a cell defined by bounds to the target resolution, recursively refining intersecting cells.
    """
    min_lon, min_lat, max_lon, max_lat = bounds
    if current_resolution < 10:
        valid_resolution = current_resolution + 2
    else:
        valid_resolution = current_resolution + 1

    area = olc.decode(olc.encode(min_lat, min_lon, valid_resolution))
    lat_step = area.latitudeHi - area.latitudeLo
    lng_step = area.longitudeHi - area.longitudeLo

    olc_records = []
    lat = min_lat
    while lat < max_lat:
        lng = min_lon
        while lng < max_lon:
            # Define the bounds of the finer cell
            finer_cell_bounds = (lng, lat, lng + lng_step, lat + lat_step)
            finer_cell_poly = box(*finer_cell_bounds)

            if bbox_poly.intersects(finer_cell_poly):
                # Generate the Plus Code for the center of the finer cell
                center_lat = lat + lat_step / 2
                center_lon = lng + lng_step / 2
                olc_id = olc.encode(center_lat, center_lon, valid_resolution)
                resolution = olc.decode(olc_id).codeLength

                cell_polygon = Polygon(
                    [
                        [lng, lat],  # SW
                        [lng, lat + lat_step],  # NW
                        [lng + lng_step, lat + lat_step],  # NE
                        [lng + lng_step, lat],  # SE
                        [lng, lat],  # Close the polygon
                    ]
                )

                olc_record = graticule_dggs_to_geoseries(
                    "olc", olc_id, resolution, cell_polygon
                )
                olc_records.append(olc_record)

                # Recursively refine the cell if not at target resolution
                if valid_resolution < target_resolution:
                    olc_records.extend(
                        olc_refine_cell(
                            finer_cell_bounds,
                            valid_resolution,
                            target_resolution,
                            bbox_poly,
                        )
                    )

            lng += lng_step
            # pbar.update(1)
        lat += lat_step

    return olc_records

olcgrid(resolution, bbox=None, output_format='gpd')

Generate OLC grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

OLC resolution [2..15]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of OLC IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output in the requested format or file path.

Source code in vgrid/generator/olcgrid.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def olcgrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate OLC grid for pure Python usage.

    Args:
        resolution (int): OLC resolution [2..15]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of OLC IDs).

    Returns:
        dict, list, or str: Output in the requested format or file path.
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        gdf = olc_grid(resolution)
    else:
        gdf = olc_grid_within_bbox(resolution, bbox)

    output_name = f"olc_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

Geohash Grid Generator Module

Generates Geohash DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical geocoding system.

Key Functions: - geohash_grid(): Main grid generation function for whole world - geohash_grid_within_bbox(): Grid generation within bounding box - geohashgrid(): User-facing function with multiple output formats - geohashgrid_cli(): Command-line interface for grid generation

Reference: https://geohash.softeng.co/uekkn, https://github.com/vinsci/geohash, https://www.movable-type.co.uk/scripts/geohash.html?geohash=dp3

expand_geohash_bbox(gh, target_length, geohashes, bbox_polygon)

Expand geohash only if it intersects the bounding box.

Source code in vgrid/generator/geohashgrid.py
56
57
58
59
60
61
62
63
64
65
66
67
def expand_geohash_bbox(gh, target_length, geohashes, bbox_polygon):
    """Expand geohash only if it intersects the bounding box."""
    polygon = geohash2geo(gh)
    if not polygon.intersects(bbox_polygon):
        return

    if len(gh) == target_length:
        geohashes.add(gh)  # Add to the set if it reaches the target resolution
        return

    for char in "0123456789bcdefghjkmnpqrstuvwxyz":
        expand_geohash_bbox(gh + char, target_length, geohashes, bbox_polygon)

geohash_grid(resolution)

Generate GeoJSON for the entire world at the given geohash resolution.

Source code in vgrid/generator/geohashgrid.py
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def geohash_grid(resolution):
    """Generate GeoJSON for the entire world at the given geohash resolution."""
    resolution = validate_geohash_resolution(resolution)
    geohashes = set()
    for gh in INITIAL_GEOHASHES:
        expand_geohash(gh, resolution, geohashes)

    geohash_records = []
    for gh in tqdm(geohashes, desc="Generating Geohash DGGS", unit=" cells"):
        cell_polygon = geohash2geo(gh)
        geohash_record = graticule_dggs_to_geoseries(
            "geohash", gh, resolution, cell_polygon
        )
        geohash_records.append(geohash_record)
    return gpd.GeoDataFrame(geohash_records, geometry="geometry", crs="EPSG:4326")

geohash_grid_ids(resolution)

Return a list of Geohash IDs for the whole world at the given resolution.

Source code in vgrid/generator/geohashgrid.py
111
112
113
114
115
116
117
118
119
def geohash_grid_ids(resolution):
    """
    Return a list of Geohash IDs for the whole world at the given resolution.
    """
    resolution = validate_geohash_resolution(resolution)
    geohashes = set()
    for gh in INITIAL_GEOHASHES:
        expand_geohash(gh, resolution, geohashes)
    return list(geohashes)

geohash_grid_resample(resolution, geojson_features)

Generate GeoJSON for geohashes within a GeoJSON feature collection at the given resolution.

Source code in vgrid/generator/geohashgrid.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def geohash_grid_resample(resolution, geojson_features):
    """Generate GeoJSON for geohashes within a GeoJSON feature collection at the given resolution."""
    resolution = validate_geohash_resolution(resolution)
    geohash_records = []
    geometries = [
        shape(feature["geometry"]) for feature in geojson_features["features"]
    ]
    unified_geom = unary_union(geometries)
    intersected_geohashes = {
        gh for gh in INITIAL_GEOHASHES if geohash2geo(gh).intersects(unified_geom)
    }
    geohashes_geom = set()
    for gh in intersected_geohashes:
        expand_geohash_bbox(gh, resolution, geohashes_geom, unified_geom)
    for gh in tqdm(geohashes_geom, desc="Generating Geohash DGGS", unit="cells"):
        geohash_record = graticule_dggs_to_geoseries(
            "geohash", gh, resolution, geohash2geo(gh)
        )
        geohash_records.append(geohash_record)
    return gpd.GeoDataFrame(geohash_records, geometry="geometry", crs="EPSG:4326")

geohash_grid_within_bbox(resolution, bbox)

Generate GeoJSON for geohashes within a bounding box at the given resolution.

Source code in vgrid/generator/geohashgrid.py
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def geohash_grid_within_bbox(resolution, bbox):
    """Generate GeoJSON for geohashes within a bounding box at the given resolution."""
    resolution = validate_geohash_resolution(resolution)
    geohash_records = []
    bbox_polygon = Polygon.from_bounds(*bbox)
    intersected_geohashes = {
        gh for gh in INITIAL_GEOHASHES if geohash2geo(gh).intersects(bbox_polygon)
    }
    geohashes_bbox = set()
    for gh in intersected_geohashes:
        expand_geohash_bbox(gh, resolution, geohashes_bbox, bbox_polygon)
    for gh in tqdm(geohashes_bbox, desc="Generating Geohash DGGS", unit=" cells"):
        geohash_record = graticule_dggs_to_geoseries(
            "geohash", gh, resolution, geohash2geo(gh)
        )
        geohash_records.append(geohash_record)
    return gpd.GeoDataFrame(geohash_records, geometry="geometry", crs="EPSG:4326")

geohash_grid_within_bbox_ids(resolution, bbox)

Return a list of Geohash IDs intersecting the given bounding box at the given resolution.

Source code in vgrid/generator/geohashgrid.py
122
123
124
125
126
127
128
129
130
131
132
133
134
def geohash_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of Geohash IDs intersecting the given bounding box at the given resolution.
    """
    resolution = validate_geohash_resolution(resolution)
    bbox_polygon = Polygon.from_bounds(*bbox)
    intersected_geohashes = {
        gh for gh in INITIAL_GEOHASHES if geohash2geo(gh).intersects(bbox_polygon)
    }
    geohashes_bbox = set()
    for gh in intersected_geohashes:
        expand_geohash_bbox(gh, resolution, geohashes_bbox, bbox_polygon)
    return list(geohashes_bbox)

geohashgrid(resolution, bbox=None, output_format='gpd')

Generate Geohash grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

Geohash resolution [1..10]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of Geohash IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output in the requested format or file path.

Source code in vgrid/generator/geohashgrid.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
def geohashgrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate Geohash grid for pure Python usage.

    Args:
        resolution (int): Geohash resolution [1..10]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of Geohash IDs).

    Returns:
        dict, list, or str: Output in the requested format or file path.
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        total_cells = 32**resolution
        if total_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {total_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = geohash_grid(resolution)
    else:
        gdf = geohash_grid_within_bbox(resolution, bbox)
    output_name = f"geohash_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

GEOREF Grid Generator Module

Generates GEOREF DGGS grids for specified resolutions with automatic cell generation and validation using World Geographic Reference System.

Key Functions: - georef_grid(): Main grid generation function with bounding box support - georef_grid_ids(): Returns list of GEOREF IDs for given bbox and resolution - georefgrid(): User-facing function with multiple output formats - georefgrid_cli(): Command-line interface for grid generation

georef_grid_ids(resolution, bbox=None)

Return a list of GEOREF IDs for a given bounding box at the specified resolution.

Parameters:

Name Type Description Default
bbox list[float]

[min_lon, min_lat, max_lon, max_lat]

None
resolution int

GEOREF resolution [0..4]

required

Returns:

Type Description

list[str]: List of GEOREF IDs

Source code in vgrid/generator/georefgrid.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def georef_grid_ids(resolution, bbox=None):
    """
    Return a list of GEOREF IDs for a given bounding box at the specified resolution.

    Args:
        bbox (list[float]): [min_lon, min_lat, max_lon, max_lat]
        resolution (int): GEOREF resolution [0..4]

    Returns:
        list[str]: List of GEOREF IDs
    """
    resolution = validate_georef_resolution(resolution)
    if bbox is None:
        min_lon, min_lat, max_lon, max_lat = -180, -90, 180, 90
    else:
        min_lon, min_lat, max_lon, max_lat = bbox
    resolution_degrees = GEOREF_RESOLUTION_DEGREES.get(resolution)
    longitudes = np.arange(min_lon, max_lon, resolution_degrees)
    latitudes = np.arange(min_lat, max_lat, resolution_degrees)

    num_cells = len(longitudes) * len(latitudes)
    ids = []
    with tqdm(total=num_cells, desc="Generating GEOREF IDs", unit=" cells") as pbar:
        for lon in longitudes:
            for lat in latitudes:
                georef_id = latlon2georef(lat, lon, resolution)
                ids.append(georef_id)
                pbar.update(1)

    return ids

georefgrid(resolution, bbox=None, output_format='gpd')

Generate GEOREF grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

GEOREF resolution [0..4]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of GEOREF IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output in the requested format or file path.

Source code in vgrid/generator/georefgrid.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def georefgrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate GEOREF grid for pure Python usage.

    Args:
        resolution (int): GEOREF resolution [0..4]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of GEOREF IDs).

    Returns:
        dict, list, or str: Output in the requested format or file path.
    """
    if bbox is None:
        bbox = [-180, -90, 180, 90]
    gdf = georef_grid(resolution, bbox)
    output_name = f"georef_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

MGRS Grid Generator Module

Generates MGRS (Military Grid Reference System) DGGS grids for specified resolutions with automatic cell generation and validation using NATO military coordinate system.

Key Functions: - mgrs_grid(): Main grid generation function with GZD support - mgrsgrid(): User-facing function with multiple output formats - mgrsgrid_cli(): Command-line interface for grid generation

is_valid_gzd(gzd)

Check if a Grid Zone Designator (GZD) is valid.

Source code in vgrid/generator/mgrsgrid.py
28
29
30
31
def is_valid_gzd(gzd):
    """Check if a Grid Zone Designator (GZD) is valid."""
    pattern = r"^(?:0[1-9]|[1-5][0-9]|60)[C-HJ-NP-X]$"
    return bool(re.match(pattern, gzd))

mgrs_grid_ids(gzd, resolution)

Return a list of MGRS IDs for a given GZD and resolution.

Source code in vgrid/generator/mgrsgrid.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def mgrs_grid_ids(gzd, resolution):
    """
    Return a list of MGRS IDs for a given GZD and resolution.
    """
    if not is_valid_gzd(gzd):
        raise ValueError("Invalid GZD. Please input a valid GZD.")

    resolution = validate_mgrs_resolution(resolution)
    cell_size = 100_000 // (10**resolution)
    north_bands = "NPQRSTUVWX"
    south_bands = "MLKJHGFEDC"
    band_distance = 111_132 * 8
    gzd_band = gzd[2]

    if gzd_band >= "N":  # North Hemesphere
        epsg_code = int("326" + gzd[:2])
        min_x, min_y, max_x, max_y = 100000, 0, 900000, 9500000
        north_band_idx = north_bands.index(gzd_band)
        max_y = band_distance * (north_band_idx + 1)
        if gzd_band == "X":
            max_y += band_distance  # band X = 12 degrees instead of 8 degrees
    else:  # South Hemesphere
        epsg_code = int("327" + gzd[:2])
        min_x, min_y, max_x, max_y = 100000, 0, 900000, 10000000
        south_band_idx = south_bands.index(gzd_band)
        max_y = band_distance * (south_band_idx + 1)

    utm_crs = CRS.from_epsg(epsg_code)
    wgs84_crs = CRS.from_epsg(4326)
    transformer = Transformer.from_crs(utm_crs, wgs84_crs, always_xy=True).transform

    gzd_json_path = os.path.join(os.path.dirname(__file__), "gzd.geojson")
    with open(gzd_json_path, encoding="utf-8") as f:
        gzd_data = json.load(f)

    gzd_features = gzd_data["features"]
    gzd_feature = [
        feature for feature in gzd_features if feature["properties"].get("gzd") == gzd
    ][0]
    gzd_geom = shape(gzd_feature["geometry"])

    ids = []
    x_coords = np.arange(min_x, max_x, cell_size)
    y_coords = np.arange(min_y, max_y, cell_size)
    num_cells = len(x_coords) * len(y_coords)
    with tqdm(total=num_cells, desc="Generating MGRS IDs", unit=" cells") as pbar:
        for x in x_coords:
            for y in y_coords:
                cell_polygon_utm = Polygon(
                    [
                        (x, y),
                        (x + cell_size, y),
                        (x + cell_size, y + cell_size),
                        (x, y + cell_size),
                        (x, y),
                    ]
                )
                cell_polygon = transform(transformer, cell_polygon_utm)

                if cell_polygon.intersects(gzd_geom):
                    centroid_lat, centroid_lon = (
                        cell_polygon.centroid.y,
                        cell_polygon.centroid.x,
                    )
                    final_id = mgrs.toMgrs(centroid_lat, centroid_lon, resolution)
                    if not gzd_geom.contains(cell_polygon):
                        intersected_polygon = cell_polygon.intersection(gzd_geom)
                        if intersected_polygon:
                            intersected_centroid_lat, intersected_centroid_lon = (
                                intersected_polygon.centroid.y,
                                intersected_polygon.centroid.x,
                            )
                            final_id = mgrs.toMgrs(
                                intersected_centroid_lat,
                                intersected_centroid_lon,
                                resolution,
                            )
                    ids.append(final_id)
                pbar.update(1)

    return ids

mgrsgrid(gzd, resolution, output_format='gpd')

Generate MGRS grid for pure Python usage.

Parameters:

Name Type Description Default
gzd str

Grid Zone Designator, e.g. '48P'.

required
resolution int

MGRS resolution [0..5].

required
output_format str

Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of MGRS IDs). Defaults to None.

'gpd'

Returns:

Type Description

Depends on output_format: list, GeoDataFrame, file path, or GeoJSON FeatureCollection.

Source code in vgrid/generator/mgrsgrid.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def mgrsgrid(gzd, resolution, output_format="gpd"):
    """
    Generate MGRS grid for pure Python usage.

    Args:
        gzd (str): Grid Zone Designator, e.g. '48P'.
        resolution (int): MGRS resolution [0..5].
        output_format (str, optional): Output format ('geojson', 'csv', 'geo', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of MGRS IDs). Defaults to None.

    Returns:
        Depends on output_format: list, GeoDataFrame, file path, or GeoJSON FeatureCollection.
    """
    if not is_valid_gzd(gzd):
        raise ValueError("Invalid GZD. Please input a valid GZD.")
    gdf = mgrs_grid(gzd, resolution)

    output_name = f"mgrs_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

Tilecode Grid Generator Module

Generates Tilecode DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical geospatial indexing system.

Key Functions: - tilecode_grid(): Main grid generation function with bounding box support - tilecode_grid_resample(): Grid generation within GeoJSON features - tilecodegrid(): User-facing function with multiple output formats - tilecodegrid_cli(): Command-line interface for grid generation

tilecode_grid_ids(resolution)

Return a list of Tilecode IDs for the whole world at the given resolution.

Source code in vgrid/generator/tilecodegrid.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def tilecode_grid_ids(resolution):
    """
    Return a list of Tilecode IDs for the whole world at the given resolution.
    """
    resolution = validate_tilecode_resolution(resolution)
    bbox = [-180.0, -85.05112878, 180.0, 85.05112878]
    min_lon, min_lat, max_lon, max_lat = bbox
    tiles = mercantile.tiles(min_lon, min_lat, max_lon, max_lat, resolution)
    ids = []
    for tile in tqdm(tiles, desc="Generating Tilecode IDs", unit=" cells"):
        z, x, y = tile.z, tile.x, tile.y
        ids.append(f"z{z}x{x}y{y}")
    return ids

tilecode_grid_within_bbox_ids(resolution, bbox)

Return a list of Tilecode IDs intersecting the given bounding box at the given resolution.

Source code in vgrid/generator/tilecodegrid.py
112
113
114
115
116
117
118
119
120
121
122
123
def tilecode_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of Tilecode IDs intersecting the given bounding box at the given resolution.
    """
    resolution = validate_tilecode_resolution(resolution)
    min_lon, min_lat, max_lon, max_lat = bbox
    tiles = mercantile.tiles(min_lon, min_lat, max_lon, max_lat, resolution)
    ids = []
    for tile in tqdm(tiles, desc="Generating Tilecode IDs", unit=" cells"):
        z, x, y = tile.z, tile.x, tile.y
        ids.append(f"z{z}x{x}y{y}")
    return ids

tilecodegrid(resolution, bbox=None, output_format='gpd')

Generate Tilecode grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

Tilecode resolution [0..26]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', etc.). Defaults to None (list of Tilecode IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output depending on output_format

Source code in vgrid/generator/tilecodegrid.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def tilecodegrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate Tilecode grid for pure Python usage.

    Args:
        resolution (int): Tilecode resolution [0..26]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', etc.). Defaults to None (list of Tilecode IDs).

    Returns:
        dict, list, or str: Output depending on output_format
    """
    if bbox is None:
        bbox = [-180.0, -85.05112878, 180.0, 85.05112878]
        num_cells = 4**resolution
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = tilecode_grid(resolution, bbox)
    else:
        gdf = tilecode_grid(resolution, bbox)

    output_name = f"tilecode_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

Quadkey Grid Generator Module

Generates Quadkey DGGS grids for specified resolutions with automatic cell generation and validation using hierarchical geospatial indexing system.

Key Functions: - quadkey_grid(): Main grid generation function with bounding box support - quadkey_grid_resample(): Grid generation within GeoJSON features - quadkeygrid(): User-facing function with multiple output formats - quadkeygrid_cli(): Command-line interface for grid generation

quadkey_grid_ids(resolution)

Return a list of Quadkey IDs for the whole world at the given resolution.

Source code in vgrid/generator/quadkeygrid.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
def quadkey_grid_ids(resolution):
    """
    Return a list of Quadkey IDs for the whole world at the given resolution.
    """
    resolution = validate_quadkey_resolution(resolution)
    bbox = [-180.0, -85.05112878, 180.0, 85.05112878]
    min_lon, min_lat, max_lon, max_lat = bbox
    tiles = mercantile.tiles(min_lon, min_lat, max_lon, max_lat, resolution)
    ids = []
    for tile in tqdm(tiles, desc="Generating Quadkey IDs", unit=" cells"):
        ids.append(mercantile.quadkey(tile))
    return ids

quadkey_grid_within_bbox_ids(resolution, bbox)

Return a list of Quadkey IDs intersecting the given bounding box at the given resolution.

Source code in vgrid/generator/quadkeygrid.py
111
112
113
114
115
116
117
118
119
120
121
def quadkey_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of Quadkey IDs intersecting the given bounding box at the given resolution.
    """
    resolution = validate_quadkey_resolution(resolution)
    min_lon, min_lat, max_lon, max_lat = bbox
    tiles = mercantile.tiles(min_lon, min_lat, max_lon, max_lat, resolution)
    ids = []
    for tile in tqdm(tiles, desc="Generating Quadkey IDs", unit=" cells"):
        ids.append(mercantile.quadkey(tile))
    return ids

quadkeygrid(resolution, bbox=None, output_format='gpd')

Generate Quadkey grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

Quadkey resolution [0..26]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', etc.). Defaults to None (list of Quadkey IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output depending on output_format

Source code in vgrid/generator/quadkeygrid.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def quadkeygrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate Quadkey grid for pure Python usage.

    Args:
        resolution (int): Quadkey resolution [0..26]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', etc.). Defaults to None (list of Quadkey IDs).

    Returns:
        dict, list, or str: Output depending on output_format
    """
    if bbox is None:
        bbox = [-180.0, -85.05112878, 180.0, 85.05112878]
        num_cells = 4**resolution
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = quadkey_grid(resolution, bbox)
    else:
        gdf = quadkey_grid(resolution, bbox)

    output_name = f"quadkey_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

Maidenhead Grid Generator Module

Generates Maidenhead DGGS grids for specified resolutions with automatic cell generation and validation using amateur radio grid square system.

Key Functions: - maidenhead_grid(): Main grid generation function for whole world - maidenhead_grid_within_bbox(): Grid generation within bounding box - maidenheadgrid(): User-facing function with multiple output formats - maidenheadgrid_cli(): Command-line interface for grid generation

Reference

maidenhead_grid_ids(resolution)

Return a list of Maidenhead IDs for the whole world at the given resolution.

Source code in vgrid/generator/maidenheadgrid.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def maidenhead_grid_ids(resolution):
    """
    Return a list of Maidenhead IDs for the whole world at the given resolution.
    """
    resolution = validate_maidenhead_resolution(resolution)
    if resolution == 1:
        lon_width, lat_width = 20, 10
    elif resolution == 2:
        lon_width, lat_width = 2, 1
    elif resolution == 3:
        lon_width, lat_width = 0.083333, 0.041666
    elif resolution == 4:
        lon_width, lat_width = 0.008333, 0.004167
    else:
        raise ValueError("Unsupported resolution")

    min_lon, min_lat, max_lon, max_lat = [-180, -90, 180, 90]
    x_cells = int((max_lon - min_lon) / lon_width)
    y_cells = int((max_lat - min_lat) / lat_width)

    ids = []
    with tqdm(
        total=x_cells * y_cells, desc="Generating Maidenhead IDs", unit=" cells"
    ) as pbar:
        for i in range(x_cells):
            for j in range(y_cells):
                cell_min_lon = min_lon + i * lon_width
                cell_max_lon = cell_min_lon + lon_width
                cell_min_lat = min_lat + j * lat_width
                cell_max_lat = cell_min_lat + lat_width

                cell_center_lat = (cell_min_lat + cell_max_lat) / 2
                cell_center_lon = (cell_min_lon + cell_max_lon) / 2
                maidenhead_id = maidenhead.toMaiden(
                    cell_center_lat, cell_center_lon, resolution
                )
                ids.append(maidenhead_id)
                pbar.update(1)

    return ids

maidenhead_grid_within_bbox_ids(resolution, bbox)

Return a list of Maidenhead IDs intersecting the given bbox at the given resolution.

Source code in vgrid/generator/maidenheadgrid.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def maidenhead_grid_within_bbox_ids(resolution, bbox):
    """
    Return a list of Maidenhead IDs intersecting the given bbox at the given resolution.
    """
    resolution = validate_maidenhead_resolution(resolution)
    if resolution == 1:
        lon_width, lat_width = 20, 10
    elif resolution == 2:
        lon_width, lat_width = 2, 1
    elif resolution == 3:
        lon_width, lat_width = 0.083333, 0.041666
    elif resolution == 4:
        lon_width, lat_width = 0.008333, 0.004167
    else:
        raise ValueError("Unsupported resolution")

    min_lon, min_lat, max_lon, max_lat = bbox
    base_lat, base_lon = -90, -180
    start_x = math.floor((min_lon - base_lon) / lon_width)
    end_x = math.floor((max_lon - base_lon) / lon_width)
    start_y = math.floor((min_lat - base_lat) / lat_width)
    end_y = math.floor((max_lat - base_lat) / lat_width)

    ids = []
    total_cells = (end_x - start_x + 1) * (end_y - start_y + 1)
    with tqdm(total=total_cells, desc="Generating Maidenhead IDs") as pbar:
        for x in range(start_x, end_x + 1):
            for y in range(start_y, end_y + 1):
                cell_min_lon = base_lon + x * lon_width
                cell_max_lon = cell_min_lon + lon_width
                cell_min_lat = base_lat + y * lat_width
                cell_max_lat = cell_min_lat + lat_width

                if not (
                    cell_max_lon < min_lon
                    or cell_min_lon > max_lon
                    or cell_max_lat < min_lat
                    or cell_min_lat > max_lat
                ):
                    cell_center_lat = (cell_min_lat + cell_max_lat) / 2
                    cell_center_lon = (cell_min_lon + cell_max_lon) / 2
                    maidenhead_id = maidenhead.toMaiden(
                        cell_center_lat, cell_center_lon, resolution
                    )
                    ids.append(maidenhead_id)

                pbar.update(1)

    return ids

maidenheadgrid(resolution, bbox=None, output_format='gpd')

Generate Maidenhead grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

Maidenhead resolution [1..4]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', etc.). Defaults to None (list of Maidenhead IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output depending on output_format

Source code in vgrid/generator/maidenheadgrid.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def maidenheadgrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate Maidenhead grid for pure Python usage.

    Args:
        resolution (int): Maidenhead resolution [1..4]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', etc.). Defaults to None (list of Maidenhead IDs).

    Returns:
        dict, list, or str: Output depending on output_format
    """
    resolution = validate_maidenhead_resolution(resolution)
    if bbox is None:
        bbox = [-180, -90, 180, 90]
        num_cells = maidenhead.num_cells(resolution)
        if num_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution {resolution} will generate {num_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = maidenhead_grid(resolution)
    else:
        gdf = maidenhead_grid_within_bbox(resolution, bbox)

    output_name = f"maidenhead_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

GARS Grid Generator Module

Generates GARS (Global Area Reference System) DGGS grids for specified resolutions with automatic cell generation and validation using military grid reference system.

Key Functions: - gars_grid(): Main grid generation function for whole world - gars_grid_within_bbox(): Grid generation within bounding box - garsgrid(): User-facing function with multiple output formats - garsgrid_cli(): Command-line interface for grid generation

gars_grid_ids(resolution, bbox=None)

Return a list of GARS IDs for the whole world at the given resolution.

Source code in vgrid/generator/garsgrid.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def gars_grid_ids(resolution, bbox=None):
    """
    Return a list of GARS IDs for the whole world at the given resolution.
    """
    resolution = validate_gars_resolution(resolution)
    if bbox is None:
        min_lon, min_lat, max_lon, max_lat = -180, -90, 180, 90
    else:
        min_lon, min_lat, max_lon, max_lat = bbox
    resolution_minutes = GARS_RESOLUTION_MINUTES.get(resolution)
    resolution_degrees = resolution_minutes / 60.0

    longitudes = np.arange(min_lon, max_lon, resolution_degrees)
    latitudes = np.arange(min_lat, max_lat, resolution_degrees)

    total_cells = len(longitudes) * len(latitudes)
    ids = []
    with tqdm(total=total_cells, desc="Generating GARS IDs", unit=" cells") as pbar:
        for lon in longitudes:
            for lat in latitudes:
                cell = GARSGrid.from_latlon(lat, lon, resolution_minutes)
                ids.append(cell.gars_id)
                pbar.update(1)

    return ids

garsgrid(resolution, bbox=None, output_format='gpd')

Generate GARS grid for pure Python usage.

Parameters:

Name Type Description Default
resolution int

GARS resolution [1..4]

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', etc.). Defaults to None (list of GARS IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output depending on output_format

Source code in vgrid/generator/garsgrid.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def garsgrid(resolution, bbox=None, output_format="gpd"):
    """
    Generate GARS grid for pure Python usage.

    Args:
        resolution (int): GARS resolution [1..4]
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', etc.). Defaults to None (list of GARS IDs).

    Returns:
        dict, list, or str: Output depending on output_format
    """
    if bbox is None:
        resolution_minutes = GARS_RESOLUTION_MINUTES.get(resolution)
        total_cells = gars_num_cells(resolution)
        if total_cells > MAX_CELLS:
            raise ValueError(
                f"Resolution level {resolution} ({resolution_minutes} minutes) will generate {total_cells} cells which exceeds the limit of {MAX_CELLS}"
            )
        gdf = gars_grid(resolution)
    else:
        gdf = gars_grid(resolution, bbox)
    output_name = f"gars_grid_{resolution}"
    return convert_to_output_format(gdf, output_format, output_name)

VGRID Grid Generator Module

Generates VGRID DGGS grids for specified resolutions and bounding boxes with automatic cell generation and validation.

Key Functions: - vgrid_gen_ids(): Generate VGRID IDs only (returns list of strings) - vgrid_gen(): Main grid generation function with bounding box support (returns GeoDataFrame) - vgridgen(): User-facing function with multiple output formats - vgridgen_cli(): Command-line interface for grid generation

vgrid_gen(vgrid_instance, resolution=0, bbox=None)

Generate a VGRID grid for a given VGRID instance and bounding box.

Parameters:

Name Type Description Default
vgrid_instance VGRID

VGRID instance

required
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
resolution int

Resolution level to generate. If None, uses VGRID instance resolution.

0

Returns:

Name Type Description
GeoDataFrame GeoDataFrame

VGRID grid cells within the bounding box

Source code in vgrid/generator/vgridgen.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def vgrid_gen(
    vgrid_instance: VGRID, resolution: int = 0, bbox: list[float] = None
) -> gpd.GeoDataFrame:
    """
    Generate a VGRID grid for a given VGRID instance and bounding box.

    Args:
        vgrid_instance (VGRID): VGRID instance
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        resolution (int, optional): Resolution level to generate. If None, uses VGRID instance resolution.

    Returns:
        GeoDataFrame: VGRID grid cells within the bounding box
    """
    if bbox is None:
        # Default to whole world
        bbox = [-180, -90, 180, 90]

    # Use specified resolution or VGRID instance resolution
    resolution = validate_vgrid_resolution(resolution)

    # Calculate cell size for the target resolution
    # Use the same logic as tiles_for_bounding_box in VGRID.py
    base_cell_size = vgrid_instance.cell_size
    if resolution >= 0:
        # For positive resolutions, divide both longitude and latitude cell sizes by sqrt(aperture)^resolution
        # This ensures total cells increase by aperture factor, not aperture^2
        factor = (vgrid_instance.aperture**0.5) ** resolution
        target_cell_size_lon = base_cell_size / factor
        target_cell_size_lat = base_cell_size / factor
    else:
        # For negative resolutions, multiply both longitude and latitude cell sizes by sqrt(aperture)^|resolution|
        factor = (vgrid_instance.aperture**0.5) ** abs(resolution)
        target_cell_size_lon = base_cell_size * factor
        target_cell_size_lat = base_cell_size * factor
    target_total_columns = int(360 / target_cell_size_lon)

    # Generate tiles for the bounding box
    tiles = vgrid_instance.tiles_for_bounding_box(
        bbox[0], bbox[1], bbox[2], bbox[3], resolution
    )

    vgrid_records = []

    with tqdm(
        total=len(tiles),
        desc=f"Generating VGRID resolution {resolution}",
        unit=" cells",
    ) as pbar:
        for res, tile_index in tiles:
            # Get the bottom-left corner of the tile
            vgrid_id = vgrid_instance.to_vgrid_id(resolution, tile_index, 0)

            # Calculate lat/lon using target resolution's cell sizes
            lat = (tile_index // target_total_columns) * target_cell_size_lat - 90
            lon = (tile_index % target_total_columns) * target_cell_size_lon - 180

            # Create the tile polygon with proper bounds
            cell_polygon = Polygon(
                [
                    (lon, lat),
                    (min(180, lon + target_cell_size_lon), lat),
                    (
                        min(180, lon + target_cell_size_lon),
                        min(90, lat + target_cell_size_lat),
                    ),
                    (lon, min(90, lat + target_cell_size_lat)),
                    (lon, lat),
                ]
            )

            # Create record
            vgrid_record = graticule_dggs_to_geoseries(
                "vgrid", str(vgrid_id), resolution, cell_polygon
            )
            vgrid_records.append(vgrid_record)
            pbar.update(1)

    return gpd.GeoDataFrame(vgrid_records, geometry="geometry", crs="EPSG:4326")

vgrid_gen_ids(vgrid_instance, resolution=0, bbox=None)

Generate VGRID IDs for a given VGRID instance and bounding box.

Parameters:

Name Type Description Default
vgrid_instance VGRID

VGRID instance

required
resolution int

Resolution level to generate. If None, uses VGRID instance resolution.

0
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None

Returns:

Type Description
list[str]

list[str]: List of VGRID cell IDs within the bounding box

Source code in vgrid/generator/vgridgen.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def vgrid_gen_ids(
    vgrid_instance: VGRID, resolution: int = 0, bbox: list[float] = None
) -> list[str]:
    """
    Generate VGRID IDs for a given VGRID instance and bounding box.

    Args:
        vgrid_instance (VGRID): VGRID instance
        resolution (int, optional): Resolution level to generate. If None, uses VGRID instance resolution.
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

    Returns:
        list[str]: List of VGRID cell IDs within the bounding box
    """
    if bbox is None:
        # Default to whole world
        bbox = [-180, -90, 180, 90]

    # Use specified resolution or VGRID instance resolution
    resolution = validate_vgrid_resolution(resolution)

    # Calculate cell size for the target resolution
    # Use the same logic as tiles_for_bounding_box in VGRID.py
    # Generate tiles for the bounding box
    tiles = vgrid_instance.tiles_for_bounding_box(
        bbox[0], bbox[1], bbox[2], bbox[3], resolution
    )

    vgrid_ids = []

    with tqdm(
        total=len(tiles),
        desc=f"Generating VGRID IDs resolution {resolution}",
        unit=" cells",
    ) as pbar:
        for res, tile_index in tiles:
            # Get the VGRID ID for this tile
            vgrid_id = vgrid_instance.to_vgrid_id(resolution, tile_index, 0)
            vgrid_ids.append(str(vgrid_id))
            pbar.update(1)

    return vgrid_ids

vgridgen(vgrid_instance, resolution=0, bbox=None, output_format='gpd')

Generate VGRID grid for pure Python usage.

Note: Create a VGRID instance first using VGRID(cell_size, aperture).

Parameters:

Name Type Description Default
vgrid_instance VGRID

VGRID instance (create with VGRID(cell_size, aperture))

required
resolution int

Resolution level to generate. If None, uses VGRID instance resolution.

0
bbox list

Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).

None
output_format str

Output format ('geojson', 'csv', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of VGRID IDs).

'gpd'

Returns:

Type Description

dict, list, or str: Output in the requested format or file path.

Source code in vgrid/generator/vgridgen.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def vgridgen(
    vgrid_instance: VGRID,
    resolution: int = 0,
    bbox: list[float] = None,
    output_format: str = "gpd",
):
    """
    Generate VGRID grid for pure Python usage.

    Note: Create a VGRID instance first using VGRID(cell_size, aperture).

    Args:
        vgrid_instance (VGRID): VGRID instance (create with VGRID(cell_size, aperture))
        resolution (int, optional): Resolution level to generate. If None, uses VGRID instance resolution.
        bbox (list, optional): Bounding box [min_lon, min_lat, max_lon, max_lat]. Defaults to None (whole world).
        output_format (str, optional): Output format ('geojson', 'csv', 'gpd', 'shapefile', 'gpkg', 'parquet', or None for list of VGRID IDs).

    Returns:
        dict, list, or str: Output in the requested format or file path.
    """

    if bbox is None:
        # Calculate total cells for whole world
        total_columns = vgrid_instance.total_columns
        total_rows = vgrid_instance.total_rows
        total_cells = total_columns * total_rows

        if total_cells > MAX_CELLS:
            raise ValueError(
                f"VGRID instance will generate {total_cells} cells which exceeds the limit of {MAX_CELLS}"
            )

    if output_format is None:
        # Return list of VGrid IDs
        return vgrid_gen_ids(vgrid_instance, resolution, bbox)
    else:
        # Return GeoDataFrame in specified format
        gdf = vgrid_gen(vgrid_instance, resolution, bbox)
        output_name = f"vgrid_grid_{resolution}"
        return convert_to_output_format(gdf, output_format, output_name)

vgridgen_cli()

CLI interface for generating VGRID grid.

Source code in vgrid/generator/vgridgen.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
def vgridgen_cli():
    """CLI interface for generating VGRID grid."""
    parser = argparse.ArgumentParser(description="Generate VGRID DGGS.")
    parser.add_argument(
        "-cell_size",
        "--cell_size",
        type=float,
        required=True,
        help="Cell size in degrees (must divide 360 and 180 evenly)",
    )
    parser.add_argument(
        "-aperture",
        "--aperture",
        type=int,
        default=4,
        help="Ratio to the next resolution (4 for quadtree-like, 9 for nonagon-like, default: 4)",
    )
    parser.add_argument(
        "-resolution",
        "--resolution",
        type=int,
        help="Resolution level to generate (optional, uses calculated resolution if not specified)",
    )
    parser.add_argument(
        "-bbox",
        "--bbox",
        nargs=4,
        type=float,
        metavar=("min_lon", "min_lat", "max_lon", "max_lat"),
        help="Bounding box coordinates (default: whole world)",
    )
    parser.add_argument(
        "-output_format",
        "--output_format",
        choices=OUTPUT_FORMATS,
        default="geojson",
        help="Output format (default: geojson)",
    )

    args = parser.parse_args()

    try:
        # Create VGRID instance
        vgrid_instance = VGRID(args.cell_size, args.aperture)

        result = vgridgen(
            vgrid_instance, args.resolution, args.bbox, args.output_format
        )
        if result is not None:
            print(f"VGRID grid generated successfully: {result}")
    except Exception as e:
        print(f"Error generating VGRID grid: {e}")
        return 1

    return 0