Quadkey and S2 Grid Systems Demonstration.#

This example showcases the new Quadkey and S2 grid systems added to M3S, demonstrating their unique properties and use cases.

Quadkey vs S2 Grid Systems - Manhattan, Quadkey Grid Level 14 (15 cells), S2 Grid Level 16 (16 cells), Overlay Comparison
M3S: Quadkey and S2 Grid Systems
Advanced Spatial Indexing Demonstration
============================================================
QUADKEY GRID SYSTEM DEMONSTRATION
============================================================
Test point: New York City (40.7128, -74.006)

Quadkey properties at different levels:
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:32: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  grid = QuadkeyGrid(level=level)
  Level  8: Quadkey='03201011' | Size~1.0702deg x 1.4062deg | Length=8
  Level 12: Quadkey='032010110301' | Size~0.0667deg x 0.0879deg | Length=12
  Level 16: Quadkey='0320101103011111' | Size~0.0042deg x 0.0055deg | Length=16

Quadkey Hierarchical Properties:
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:48: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  grid = QuadkeyGrid(level=12)
Parent cell: 032010110301
Children: ['0320101103010', '0320101103011', '0320101103012', '0320101103013']
Children start with parent quadkey: True
Number of neighbors: 8

============================================================
S2 GRID SYSTEM DEMONSTRATION
============================================================
Test point: New York City (40.7128, -74.006)

S2 properties at different levels:
  Level  8: Token='89c25' | Area~0.11912978deg | Token Length=5
  Level 12: Token='89c25a3' | Area~0.00046488deg | Token Length=7
  Level 16: Token='89c25a221' | Area~0.00000182deg | Token Length=9

S2 Hierarchical Properties:
Cell token: 89c25a3
Number of children: 4
Sample child tokens: ['89c25a24', '89c25a2c']
Parent token: 89c25a4
Number of neighbors: 4

============================================================
QUADKEY vs S2 COMPARISON
============================================================
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:141: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  quadkey_grid = QuadkeyGrid(level=14)  # ~600m tiles
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:142: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  s2_grid = S2Grid(level=12)  # ~300m cells
Processing intersections...
Quadkey (level 14): 15 cells
S2 (level 16): 16 cells
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:210: UserWarning: Legend does not support handles for PatchCollection instances.
See: https://matplotlib.org/stable/tutorials/intermediate/legend_guide.html#implementing-a-custom-legend-handler
  ax3.legend()
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:210: UserWarning: No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
  ax3.legend()

============================================================
SPATIAL PROPERTIES ANALYSIS
============================================================
Analyzing grid cell identifiers for global points:
------------------------------------------------------------
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:236: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  quadkey_grid = QuadkeyGrid(level=10)
/home/runner/work/m3s/m3s/examples/quadkey_s2_example.py:237: DeprecationWarning: The 'level' parameter is deprecated. Use 'precision' instead.
  s2_grid = S2Grid(level=10)
New York     | Quadkey: 0320101103   | S2: 89c25b       | QK Area: 0.093870 | S2 Area: 0.007442
London       | Quadkey: 0313131311   | S2: 487605       | QK Area: 0.076926 | S2 Area: 0.007898
Tokyo        | Quadkey: 1330021123   | S2: 6018f3       | QK Area: 0.100491 | S2 Area: 0.005990
Sydney       | Quadkey: 3112301330   | S2: 6b12af       | QK Area: 0.102622 | S2 Area: 0.007633
Mexico City  | Quadkey: 0233100332   | S2: 85d1ff       | QK Area: 0.116523 | S2 Area: 0.008349

============================================================
USE CASE DEMONSTRATIONS
============================================================
1. Quadkey Use Cases:
   - Web mapping and tile serving (Bing Maps)
   - Hierarchical spatial indexing
   - Database optimization with string keys
   - Cache-friendly tile loading
   - Simple spatial queries

2. S2 Use Cases:
   - Large-scale geospatial analysis (Google Maps)
   - Global spatial indexing
   - Spherical geometry calculations
   - Location-based services
   - Scientific geospatial applications

3. Performance Characteristics:
   Quadkey:
     - Fast string-based operations
     - Efficient for rectangular regions
     - Simple hierarchy traversal
     - Good for web applications
   S2:
     - Optimal spatial locality
     - Excellent for spherical calculations
     - Complex but powerful covering algorithms
     - Best for large-scale scientific applications

============================================================
SUMMARY
============================================================
+ Quadkey Grid System: Microsoft's tile-based spatial indexing
+ S2 Grid System: Google's spherical geometry library
+ Both systems offer unique advantages for different applications
+ M3S provides unified interface for both systems

Installation Notes:
- Quadkey: No additional dependencies (pure Python implementation)
- S2: Optional s2sphere library for full functionality
  - Install with: pip install s2sphere
  - Falls back to simplified implementation if not available

import warnings

import geopandas as gpd
import matplotlib.pyplot as plt
from shapely.geometry import box

from m3s import QuadkeyGrid, S2Grid


def demonstrate_quadkey():
    """Demonstrate Quadkey grid system features."""
    print("=" * 60)
    print("QUADKEY GRID SYSTEM DEMONSTRATION")
    print("=" * 60)

    # Create Quadkey grids at different levels
    levels = [8, 12, 16]
    test_point = (40.7128, -74.0060)  # NYC

    print(f"Test point: New York City {test_point}")
    print("\nQuadkey properties at different levels:")

    for level in levels:
        grid = QuadkeyGrid(level=level)
        cell = grid.get_cell_from_point(test_point[0], test_point[1])

        # Calculate approximate cell size
        bounds = grid.get_quadkey_bounds(cell.identifier)
        lat_size = bounds[2] - bounds[0]  # max_lat - min_lat
        lon_size = bounds[3] - bounds[1]  # max_lon - min_lon

        print(
            f"  Level {level:2d}: Quadkey='{cell.identifier}' | "
            f"Size~{lat_size:.4f}deg x {lon_size:.4f}deg | "
            f"Length={len(cell.identifier)}"
        )

    # Demonstrate hierarchical properties
    print("\nQuadkey Hierarchical Properties:")
    grid = QuadkeyGrid(level=12)
    cell = grid.get_cell_from_point(test_point[0], test_point[1])

    print(f"Parent cell: {cell.identifier}")

    # Get children
    children = grid.get_children(cell)
    print(f"Children: {[child.identifier for child in children]}")

    # Show that children start with parent quadkey
    children_match = all(
        child.identifier.startswith(cell.identifier) for child in children
    )
    print(f"Children start with parent quadkey: {children_match}")

    # Get neighbors
    neighbors = grid.get_neighbors(cell)
    print(f"Number of neighbors: {len(neighbors)}")

    return grid, cell


def demonstrate_s2():
    """Demonstrate S2 grid system features."""
    print("\n" + "=" * 60)
    print("S2 GRID SYSTEM DEMONSTRATION")
    print("=" * 60)

    # Suppress warnings for cleaner output
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")

        # Create S2 grids at different levels
        levels = [8, 12, 16]
        test_point = (40.7128, -74.0060)  # NYC

        print(f"Test point: New York City {test_point}")
        print("\nS2 properties at different levels:")

        cells_by_level = {}
        for level in levels:
            grid = S2Grid(level=level)
            cell = grid.get_cell_from_point(test_point[0], test_point[1])
            cells_by_level[level] = cell

            # Calculate approximate cell area
            area = cell.polygon.area

            print(
                f"  Level {level:2d}: Token='{cell.identifier}' | "
                f"Area~{area:.8f}deg | "
                f"Token Length={len(cell.identifier)}"
            )

        # Demonstrate hierarchical properties
        print("\nS2 Hierarchical Properties:")
        grid = S2Grid(level=12)
        cell = grid.get_cell_from_point(test_point[0], test_point[1])

        print(f"Cell token: {cell.identifier}")

        # Get children
        children = grid.get_children(cell)
        print(f"Number of children: {len(children)}")
        if children:
            child_ids = [child.identifier for child in children[:2]]
            print(f"Sample child tokens: {child_ids}")

        # Get parent
        parent = grid.get_parent(cell)
        if parent:
            print(f"Parent token: {parent.identifier}")

        # Get neighbors
        neighbors = grid.get_neighbors(cell)
        print(f"Number of neighbors: {len(neighbors)}")

        return grid, cell


def compare_grid_systems():
    """Compare Quadkey and S2 grid systems side by side."""
    print("\n" + "=" * 60)
    print("QUADKEY vs S2 COMPARISON")
    print("=" * 60)

    # Test area around Manhattan
    test_area = box(-74.02, 40.70, -73.98, 40.78)
    test_gdf = gpd.GeoDataFrame(
        {"name": ["Manhattan"]}, geometry=[test_area], crs="EPSG:4326"
    )

    # Create grids with similar resolutions
    quadkey_grid = QuadkeyGrid(level=14)  # ~600m tiles
    s2_grid = S2Grid(level=12)  # ~300m cells

    print("Processing intersections...")

    # Get intersections
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        quadkey_result = quadkey_grid.intersects(test_gdf)
        s2_result = s2_grid.intersects(test_gdf)

    print(f"Quadkey (level 14): {len(quadkey_result)} cells")
    print(f"S2 (level 16): {len(s2_result)} cells")

    # Create visualization
    fig, axes = plt.subplots(1, 3, figsize=(18, 6))
    fig.suptitle("Quadkey vs S2 Grid Systems - Manhattan", fontsize=16)

    # Plot Quadkey
    ax1 = axes[0]
    ax1.set_title(f"Quadkey Grid\nLevel 14 ({len(quadkey_result)} cells)")
    if len(quadkey_result) > 0:
        quadkey_result.plot(
            ax=ax1, facecolor="lightblue", edgecolor="blue", linewidth=0.5, alpha=0.7
        )
    test_gdf.plot(ax=ax1, facecolor="none", edgecolor="red", linewidth=2)
    ax1.set_xlabel("Longitude")
    ax1.set_ylabel("Latitude")
    ax1.grid(True, alpha=0.3)

    # Plot S2
    ax2 = axes[1]
    ax2.set_title(f"S2 Grid\nLevel 16 ({len(s2_result)} cells)")
    if len(s2_result) > 0:
        s2_result.plot(
            ax=ax2, facecolor="lightgreen", edgecolor="green", linewidth=0.5, alpha=0.7
        )
    test_gdf.plot(ax=ax2, facecolor="none", edgecolor="red", linewidth=2)
    ax2.set_xlabel("Longitude")
    ax2.set_ylabel("Latitude")
    ax2.grid(True, alpha=0.3)

    # Plot overlay
    ax3 = axes[2]
    ax3.set_title("Overlay Comparison")
    if len(quadkey_result) > 0:
        quadkey_result.plot(
            ax=ax3,
            facecolor="lightblue",
            edgecolor="blue",
            linewidth=0.5,
            alpha=0.5,
            label="Quadkey",
        )
    if len(s2_result) > 0:
        s2_result.plot(
            ax=ax3,
            facecolor="lightgreen",
            edgecolor="green",
            linewidth=0.5,
            alpha=0.5,
            label="S2",
        )
    test_gdf.plot(
        ax=ax3, facecolor="none", edgecolor="red", linewidth=2, label="Test Area"
    )
    ax3.set_xlabel("Longitude")
    ax3.set_ylabel("Latitude")
    ax3.grid(True, alpha=0.3)
    ax3.legend()

    plt.tight_layout()
    plt.show()

    return quadkey_result, s2_result


def analyze_spatial_properties():
    """Analyze spatial properties of different grid systems."""
    print("\n" + "=" * 60)
    print("SPATIAL PROPERTIES ANALYSIS")
    print("=" * 60)

    # Test points around the world
    test_points = [
        (40.7128, -74.0060, "New York"),
        (51.5074, -0.1278, "London"),
        (35.6762, 139.6503, "Tokyo"),
        (-33.8688, 151.2093, "Sydney"),
        (19.4326, -99.1332, "Mexico City"),
    ]

    print("Analyzing grid cell identifiers for global points:")
    print("-" * 60)

    quadkey_grid = QuadkeyGrid(level=10)
    s2_grid = S2Grid(level=10)

    results = []
    for lat, lon, city in test_points:
        quadkey_cell = quadkey_grid.get_cell_from_point(lat, lon)

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            s2_cell = s2_grid.get_cell_from_point(lat, lon)

        results.append(
            {
                "city": city,
                "lat": lat,
                "lon": lon,
                "quadkey_id": quadkey_cell.identifier,
                "quadkey_area": quadkey_cell.polygon.area,
                "s2_id": s2_cell.identifier,
                "s2_area": s2_cell.polygon.area,
            }
        )

    for result in results:
        print(
            f"{result['city']:12} | "
            f"Quadkey: {result['quadkey_id']:12} | "
            f"S2: {result['s2_id']:12} | "
            f"QK Area: {result['quadkey_area']:.6f} | "
            f"S2 Area: {result['s2_area']:.6f}"
        )

    return results


def demonstrate_use_cases():
    """Demonstrate typical use cases for each grid system."""
    print("\n" + "=" * 60)
    print("USE CASE DEMONSTRATIONS")
    print("=" * 60)

    print("1. Quadkey Use Cases:")
    print("   - Web mapping and tile serving (Bing Maps)")
    print("   - Hierarchical spatial indexing")
    print("   - Database optimization with string keys")
    print("   - Cache-friendly tile loading")
    print("   - Simple spatial queries")

    print("\n2. S2 Use Cases:")
    print("   - Large-scale geospatial analysis (Google Maps)")
    print("   - Global spatial indexing")
    print("   - Spherical geometry calculations")
    print("   - Location-based services")
    print("   - Scientific geospatial applications")

    print("\n3. Performance Characteristics:")
    print("   Quadkey:")
    print("     - Fast string-based operations")
    print("     - Efficient for rectangular regions")
    print("     - Simple hierarchy traversal")
    print("     - Good for web applications")

    print("   S2:")
    print("     - Optimal spatial locality")
    print("     - Excellent for spherical calculations")
    print("     - Complex but powerful covering algorithms")
    print("     - Best for large-scale scientific applications")


def main():
    """Run main demonstration."""
    print("M3S: Quadkey and S2 Grid Systems")
    print("Advanced Spatial Indexing Demonstration")

    # Individual demonstrations
    quadkey_grid, quadkey_cell = demonstrate_quadkey()
    s2_grid, s2_cell = demonstrate_s2()

    # Comparative analysis
    quadkey_result, s2_result = compare_grid_systems()
    analyze_spatial_properties()

    # Use case explanation
    demonstrate_use_cases()

    print("\n" + "=" * 60)
    print("SUMMARY")
    print("=" * 60)
    print("+ Quadkey Grid System: Microsoft's tile-based spatial indexing")
    print("+ S2 Grid System: Google's spherical geometry library")
    print("+ Both systems offer unique advantages for different applications")
    print("+ M3S provides unified interface for both systems")

    print("\nInstallation Notes:")
    print("- Quadkey: No additional dependencies (pure Python implementation)")
    print("- S2: Optional s2sphere library for full functionality")
    print("  - Install with: pip install s2sphere")
    print("  - Falls back to simplified implementation if not available")


if __name__ == "__main__":
    # Suppress matplotlib font warnings
    warnings.filterwarnings("ignore", category=UserWarning, module="matplotlib")

    main()

Total running time of the script: (0 minutes 0.764 seconds)

Gallery generated by Sphinx-Gallery