build_transform_and_save_weights
Generate and save spatial weights for GIS analysis by reading a shapefile, applying methods like queen, rook, or knn, transforming weights if needed, and exporting them in GAL or GWT format.
Instructions
Pipeline: Read shapefile, build spatial weights, optionally transform, and save to file.
Parameters:
- data_path: Path to point shapefile or GeoPackage
- method: 'queen', 'rook', 'distance_band', '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' (optional)
- output_path: File path to save weights
- format: 'gal' or 'gwt'
- overwrite: Allow overwriting if file exists
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| binary | No | ||
| data_path | Yes | ||
| format | No | gal | |
| id_field | No | ||
| k | No | ||
| method | No | queen | |
| output_path | No | weights.gal | |
| overwrite | No | ||
| threshold | No | ||
| transform_type | No |
Implementation Reference
- src/gis_mcp/pysal_functions.py:535-633 (handler)The handler function for the 'build_transform_and_save_weights' MCP tool. It reads geospatial data, constructs spatial weights matrices using libpysal (Queen, Rook, DistanceBand, KNN), applies optional transformations, saves to GAL/GWT format, and returns success/error status with details.def build_transform_and_save_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: Optional[str] = None, output_path: str = "weights.gal", format: str = "gal", overwrite: bool = False ) -> Dict[str, Any]: """ Pipeline: Read shapefile, build spatial weights, optionally transform, and save to file. Parameters: - data_path: Path to point shapefile or GeoPackage - method: 'queen', 'rook', 'distance_band', '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' (optional) - output_path: File path to save weights - format: 'gal' or 'gwt' - overwrite: Allow overwriting if file exists """ try: # --- Step 1: Check input 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 --- 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 given --- if transform_type: 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: Save weights to file --- format = (format or "").lower() if format not in {"gal", "gwt"}: return {"status": "error", "message": f"Invalid format: {format}"} if not output_path.lower().endswith(f".{format}"): output_path += f".{format}" if os.path.exists(output_path) and not overwrite: return {"status": "error", "message": f"File already exists: {output_path}. Set overwrite=True to replace it."} w.to_file(output_path, format=format) # --- Step 5: Build result --- return { "status": "success", "message": f"{method} weights built and saved successfully", "result": { "path": output_path, "format": format, "n": int(w.n), "transform": getattr(w, "transform", None), "islands": list(w.islands) if hasattr(w, "islands") else [], }, } except Exception as e: logger.error(f"Error in build_transform_and_save_weights: {str(e)}") return {"status": "error", "message": f"Failed to build and save weights: {str(e)}"}
- src/gis_mcp/main.py:66-72 (registration)Import of pysal_functions.py in main.py, which triggers the registration of all @gis_mcp.tool() decorators including 'build_transform_and_save_weights' upon module load.from . import ( geopandas_functions, shapely_functions, rasterio_functions, pyproj_functions, pysal_functions, )
- src/gis_mcp/pysal_functions.py:8-8 (helper)Import of the FastMCP instance 'gis_mcp' used for tool and resource decorators in pysal_functions.py.from .mcp import gis_mcp