build_and_transform_weights
Create and standardize spatial weight matrices for GIS analysis using methods like queen contiguity, distance bands, or k-nearest neighbors.
Instructions
Build and transform spatial weights in one step.
Parameters:
data_path: Path to point shapefile or GeoPackage
method: 'queen', 'rook', 'distance_band', or 'knn'
id_field: Optional field name for IDs
threshold: Distance threshold (required if method='distance_band')
k: Number of neighbors (required if method='knn')
binary: True for binary weights, False for inverse distance (DistanceBand only)
transform_type: 'r', 'v', 'b', 'o', or 'd'
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| data_path | Yes | ||
| method | No | queen | |
| id_field | No | ||
| threshold | No | ||
| k | No | ||
| binary | No | ||
| transform_type | No | r |
Implementation Reference
- src/gis_mcp/pysal_functions.py:1038-1139 (handler)The primary handler function implementing the 'build_and_transform_weights' tool logic. Builds spatial weights matrix using PySAL/libpysal for various methods (Queen, Rook, DistanceBand, KNN) from a shapefile or geopackage, applies row-standardization or other transformations, and returns summary statistics, neighbor previews, and island information.def build_and_transform_weights( data_path: str, method: str = "queen", id_field: Optional[str] = None, threshold: Optional[float] = None, k: Optional[int] = None, binary: bool = True, transform_type: str = "r" ) -> Dict[str, Any]: """ Build and transform spatial weights in one step. Parameters: - data_path: Path to point shapefile or GeoPackage - method: 'queen', 'rook', 'distance_band', or 'knn' - id_field: Optional field name for IDs - threshold: Distance threshold (required if method='distance_band') - k: Number of neighbors (required if method='knn') - binary: True for binary weights, False for inverse distance (DistanceBand only) - transform_type: 'r', 'v', 'b', 'o', or 'd' """ try: # --- Step 1: Check file --- if not os.path.exists(data_path): return {"status": "error", "message": f"Data file not found: {data_path}"} gdf = gpd.read_file(data_path) if gdf.empty: return {"status": "error", "message": "Input file contains no features"} coords = [(geom.x, geom.y) for geom in gdf.geometry] # --- Step 2: Build weights --- import libpysal method = (method or "").lower() if method == "queen": w = libpysal.weights.Queen.from_dataframe(gdf, idVariable=id_field) elif method == "rook": w = libpysal.weights.Rook.from_dataframe(gdf, idVariable=id_field) elif method == "distance_band": if threshold is None: return {"status": "error", "message": "Threshold is required for distance_band method"} if id_field and id_field in gdf.columns: ids = gdf[id_field].tolist() w = libpysal.weights.DistanceBand(coords, threshold=threshold, binary=binary, ids=ids) else: w = libpysal.weights.DistanceBand(coords, threshold=threshold, binary=binary) elif method == "knn": if k is None: return {"status": "error", "message": "k is required for knn method"} if id_field and id_field in gdf.columns: ids = gdf[id_field].tolist() w = libpysal.weights.KNN(coords, k=k, ids=ids) else: w = libpysal.weights.KNN(coords, k=k) else: return {"status": "error", "message": f"Unsupported method: {method}"} # --- Step 3: Apply transformation --- if not isinstance(w, libpysal.weights.W): return {"status": "error", "message": "Failed to build a valid W object"} transform_type = (transform_type or "").lower() if transform_type not in {"r", "v", "b", "o", "d"}: return {"status": "error", "message": f"Invalid transform type: {transform_type}"} w.transform = transform_type # --- Step 4: Build result --- ids = w.id_order neighbor_counts = [w.cardinalities[i] for i in ids] islands = list(w.islands) if hasattr(w, "islands") else [] preview_ids = ids[:5] neighbors_preview = {i: w.neighbors.get(i, []) for i in preview_ids} weights_preview = {i: w.weights.get(i, []) for i in preview_ids} result = { "n": int(w.n), "id_count": len(ids), "method": method, "threshold": threshold if method == "distance_band" else None, "k": k if method == "knn" else None, "binary": binary if method == "distance_band" else None, "transform": transform_type, "neighbors_stats": { "min": int(min(neighbor_counts)) if neighbor_counts else 0, "max": int(max(neighbor_counts)) if neighbor_counts else 0, "mean": float(np.mean(neighbor_counts)) if neighbor_counts else 0.0, }, "islands": islands, "neighbors_preview": neighbors_preview, "weights_preview": weights_preview, } return { "status": "success", "message": f"{method} spatial weights built and transformed successfully", "result": result, "weights_info": result, # Also include as weights_info for test compatibility } except Exception as e: logger.error(f"Error in build_and_transform_weights: {str(e)}") return {"status": "error", "message": f"Failed to build and transform weights: {str(e)}"}
- src/gis_mcp/main.py:66-72 (registration)Import of the pysal_functions module in the main server entrypoint, which executes the @gis_mcp.tool() decorators defined in pysal_functions.py, thereby registering the 'build_and_transform_weights' tool with the FastMCP server instance.from . import ( geopandas_functions, shapely_functions, rasterio_functions, pyproj_functions, pysal_functions, )
- Type annotations in the function signature define the input schema (parameters with types and defaults) and output type (Dict[str, Any]) for the MCP tool.data_path: str, method: str = "queen", id_field: Optional[str] = None, threshold: Optional[float] = None, k: Optional[int] = None, binary: bool = True, transform_type: str = "r" ) -> Dict[str, Any]: