Skip to main content
Glama

getis_ord_g_local

Identify local spatial clusters and hotspots in geographic data by analyzing spatial autocorrelation patterns within shapefiles.

Instructions

Local Getis-Ord G.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
shapefile_pathYes
dependent_varNoLAND_USE
target_crsNoEPSG:4326
distance_thresholdNo

Implementation Reference

  • Main execution function for getis_ord_g_local tool: loads data, creates spatial weights, computes local Getis-Ord G* using esda.G_Local, handles islands with KNN fallback or filtering.
    @gis_mcp.tool()
    def getis_ord_g_local(shapefile_path: str, dependent_var: str = "LAND_USE", target_crs: str = "EPSG:4326",
                          distance_threshold: float = 100000) -> Dict[str, Any]:
        """Local Getis-Ord G."""
        gdf, y, w, (threshold, unit), err = pysal_load_data(shapefile_path, dependent_var, target_crs, distance_threshold)
        if err:
            return {"status": "error", "message": err}
    
        # Handle islands - if all points are islands, fall back to KNN weights for connectivity
        import libpysal
        if w.islands:
            if len(w.islands) == len(gdf):
                # All points are islands - fall back to KNN weights
                try:
                    # Use k=4 for a 5x5 grid to ensure connectivity
                    w = libpysal.weights.KNN.from_dataframe(gdf, k=4)
                    w.transform = 'r'
                except Exception as e:
                    return {"status": "error", "message": f"All units are islands and KNN fallback failed: {str(e)}"}
            else:
                # Some islands - filter them out
                keep_idx = [i for i in range(len(gdf)) if i not in set(w.islands)]
                if len(keep_idx) == 0:
                    return {"status": "error", "message": "All units are islands (no neighbors). Try increasing distance_threshold."}
                # Filter data
                gdf_filtered = gdf.iloc[keep_idx].reset_index(drop=True)
                y_filtered = y[keep_idx]
                # Rebuild weights without islands using the same threshold
                w_filtered = libpysal.weights.DistanceBand.from_dataframe(
                    gdf_filtered, 
                    threshold=threshold,  # Use the effective threshold already calculated in pysal_load_data
                    binary=False
                )
                w_filtered.transform = 'r'
                gdf, y, w = gdf_filtered, y_filtered, w_filtered
    
        import esda
        stat = esda.G_Local(y, w)
        preview = gdf[['geometry', dependent_var]].head(5).copy()
        preview['geometry'] = preview['geometry'].apply(lambda g: g.wkt)
    
        return {
            "status": "success",
            "message": f"Local Getis-Ord G completed successfully (threshold: {threshold} {unit})",
            "result": {
                "G_local": stat.Gs.tolist() if hasattr(stat.Gs, 'tolist') else list(stat.Gs),
                "p_values": stat.p_sim.tolist() if hasattr(stat.p_sim, 'tolist') else list(stat.p_sim),
                "z_scores": stat.z_sim.tolist() if hasattr(stat.z_sim, 'tolist') else list(stat.z_sim),
                "data_preview": preview.to_dict(orient="records")
            }
        }
  • Shared helper function for loading shapefile, reprojecting, extracting dependent variable, and creating row-standardized distance band spatial weights; used by getis_ord_g_local and other PySAL tools.
    def pysal_load_data(shapefile_path: str, dependent_var: str, target_crs: str, distance_threshold: float):
        """Common loader and weight creation for esda statistics."""
        if not os.path.exists(shapefile_path):
            return None, None, None, None, f"Shapefile not found: {shapefile_path}"
    
        gdf = gpd.read_file(shapefile_path)
        if dependent_var not in gdf.columns:
            return None, None, None, None, f"Dependent variable '{dependent_var}' not found in shapefile columns"
    
        gdf = gdf.to_crs(target_crs)
    
        effective_threshold = distance_threshold
        unit = "meters"
        if target_crs.upper() == "EPSG:4326":
            effective_threshold = distance_threshold / 111000
            unit = "degrees"
    
        y = gdf[dependent_var].values.astype(np.float64)
        import libpysal
        w = libpysal.weights.DistanceBand.from_dataframe(gdf, threshold=effective_threshold, binary=False)
        w.transform = 'r'
    
        for island in w.islands:
            w.weights[island] = [0] * len(w.weights[island])
            w.cardinalities[island] = 0
    
        return gdf, y, w, (effective_threshold, unit), None
  • Import statement that loads pysal_functions.py, triggering registration of getis_ord_g_local via @gis_mcp.tool() decorator.
    from .pysal_functions import * 
  • Resource endpoint listing available ESDA/PySAL operations including getis_ord_g_local.
    @gis_mcp.resource("gis://operations/esda")
    def get_spatial_operations() -> Dict[str, List[str]]:
        """List available spatial analysis operations. This is for esda library. They are using pysal library."""
        return {
            "operations": [
                "getis_ord_g",
                "morans_i",
                "gearys_c",
                "gamma_statistic",
                "moran_local",
                "getis_ord_g_local",
                "join_counts",
                "join_counts_local",
                "adbscan"
            ]
        }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mahdin75/gis-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server