Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
data_pathYes
methodNoqueen
id_fieldNo
thresholdNo
kNo
binaryNo
transform_typeNor

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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)}"}
  • 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]:
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions the tool performs building AND transforming in one step, which implies a combined operation, but doesn't describe what the transformation does, what the output looks like, whether it modifies input data, or any performance/limitation considerations. For a tool with 7 parameters and spatial operations, this leaves significant behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with a clear purpose statement followed by a parameter list. Every sentence serves a purpose: the first establishes the tool's function, and the parameter explanations are necessary given the 0% schema coverage. It could be slightly more front-loaded with context about spatial weights, but overall it's appropriately sized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (7 parameters, spatial operations), no annotations, but with an output schema present, the description is moderately complete. It thoroughly documents parameters but lacks context about what spatial weights are, what the transformation types mean, and behavioral aspects. The output schema existence means return values don't need explanation, but more operational context would be helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description compensates well by listing all 7 parameters with clear explanations of their purposes and constraints. It specifies required parameters for certain methods (threshold for 'distance_band', k for 'knn'), explains what 'binary' means, and lists valid values for 'method' and 'transform_type'. This adds substantial meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Build and transform spatial weights in one step.' This specifies the action (build and transform) and the resource (spatial weights), making it distinct from most siblings. However, it doesn't explicitly differentiate from the closely related 'build_transform_and_save_weights' sibling tool, which appears to perform a similar function with additional saving capability.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. While it mentions parameters like method options, it doesn't explain scenarios for choosing this tool over sibling tools like 'distance_band_weights' or 'knn_weights', or when the 'one step' approach is preferable to separate building and transforming steps.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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