""" Landvex ArcGIS Toolbox Python toolbox for ArcGIS Pro integration """ import arcpy import requests import json from datetime import datetime class LandvexToolbox(object): """Landvex Urban Intelligence Toolbox""" def __init__(self): self.label = "Landvex" self.alias = "landvex" self.tools = [ LoadObservationsTool, LoadRGITool, LoadUMITool, ExportToLandvexTool, ] class LoadObservationsTool(object): """Load observations from Landvex API""" def __init__(self): self.label = "Load Observations" self.description = "Load infrastructure observations from Landvex" self.canRunInBackground = True def getParameterInfo(self): params = [] # API URL param = arcpy.Parameter( displayName="API URL", name="api_url", datatype="GPString", parameterType="Required", direction="Input" ) param.value = "https://api.landvex.com/v1" params.append(param) # API Key param = arcpy.Parameter( displayName="API Key", name="api_key", datatype="GPString", parameterType="Required", direction="Input" ) params.append(param) # Bounds (optional) param = arcpy.Parameter( displayName="Bounding Box", name="bounds", datatype="GPExtent", parameterType="Optional", direction="Input" ) params.append(param) # Output Feature Class param = arcpy.Parameter( displayName="Output Feature Class", name="output_fc", datatype="DEFeatureClass", parameterType="Required", direction="Output" ) params.append(param) return params def execute(self, parameters, messages): api_url = parameters[0].valueAsText api_key = parameters[1].valueAsText bounds = parameters[2].value output_fc = parameters[3].valueAsText try: # Fetch observations arcpy.AddMessage("Fetching observations from Landvex...") headers = {"Authorization": f"Bearer {api_key}"} params = {"limit": 10000} if bounds: params["bounds"] = f"{bounds.XMin},{bounds.YMin},{bounds.XMax},{bounds.YMax}" response = requests.get( f"{api_url}/observations", headers=headers, params=params ) response.raise_for_status() data = response.json() observations = data.get("observations", []) arcpy.AddMessage(f"Retrieved {len(observations)} observations") # Create feature class sr = arcpy.SpatialReference(4326) # WGS84 arcpy.CreateFeatureclass_management( out_path="\\".join(output_fc.split("\\")[:-1]), out_name=output_fc.split("\\")[-1], geometry_type="POINT", spatial_reference=sr ) # Add fields fields = [ ("obs_id", "TEXT", 50), ("obj_type", "TEXT", 100), ("condition", "SHORT"), ("zoomer", "TEXT", 100), ("status", "TEXT", 20), ("obs_date", "DATE"), ("rgi_score", "DOUBLE"), ] for field_name, field_type, *length in fields: if field_type == "TEXT": arcpy.AddField_management(output_fc, field_name, field_type, field_length=length[0]) else: arcpy.AddField_management(output_fc, field_name, field_type) # Insert features with arcpy.da.InsertCursor(output_fc, ["SHAPE@XY", "obs_id", "obj_type", "condition", "zoomer", "status", "obs_date", "rgi_score"]) as cursor: for obs in observations: cursor.insertRow([ (obs["lng"], obs["lat"]), obs["id"], obs["type"], obs["condition"], obs["zoomer"], obs["status"], obs["date"], obs.get("rgi", 0) ]) arcpy.AddMessage(f"Created feature class with {len(observations)} observations") # Apply symbology self.apply_symbology(output_fc) except Exception as e: arcpy.AddError(f"Error: {str(e)}") def apply_symbology(self, feature_class): """Apply condition-based symbology""" # Create layer file with graduated colors # Green (1) → Red (5) pass class LoadRGITool(object): """Load RGI heatmap""" def __init__(self): self.label = "Load RGI Heatmap" self.description = "Load Reality Gap Index grid" self.canRunInBackground = True def getParameterInfo(self): params = [] param = arcpy.Parameter( displayName="API URL", name="api_url", datatype="GPString", parameterType="Required", direction="Input" ) param.value = "https://api.landvex.com/v1" params.append(param) param = arcpy.Parameter( displayName="API Key", name="api_key", datatype="GPString", parameterType="Required", direction="Input" ) params.append(param) param = arcpy.Parameter( displayName="Grid Resolution", name="resolution", datatype="GPLong", parameterType="Required", direction="Input" ) param.value = 100 # meters params.append(param) param = arcpy.Parameter( displayName="Output Raster", name="output_raster", datatype="DERasterDataset", parameterType="Required", direction="Output" ) params.append(param) return params def execute(self, parameters, messages): api_url = parameters[0].valueAsText api_key = parameters[1].valueAsText resolution = parameters[2].value output_raster = parameters[3].valueAsText try: arcpy.AddMessage("Fetching RGI grid...") response = requests.get( f"{api_url}/indexes/rgi/grid", headers={"Authorization": f"Bearer {api_key}"}, params={"resolution": resolution} ) response.raise_for_status() data = response.json() # Create raster from grid cells # Implementation depends on ArcGIS version arcpy.AddMessage("RGI heatmap created") except Exception as e: arcpy.AddError(f"Error: {str(e)}") class LoadUMITool(object): """Load UMI layer""" def __init__(self): self.label = "Load UMI" self.description = "Load Urban Morphology Index" self.canRunInBackground = True def getParameterInfo(self): params = [] param = arcpy.Parameter( displayName="API URL", name="api_url", datatype="GPString", parameterType="Required", direction="Input" ) param.value = "https://api.landvex.com/v1" params.append(param) param = arcpy.Parameter( displayName="API Key", name="api_key", datatype="GPString", parameterType="Required", direction="Input" ) params.append(param) param = arcpy.Parameter( displayName="Output Feature Class", name="output_fc", datatype="DEFeatureClass", parameterType="Required", direction="Output" ) params.append(param) return params def execute(self, parameters, messages): api_url = parameters[0].valueAsText api_key = parameters[1].valueAsText output_fc = parameters[2].valueAsText try: arcpy.AddMessage("Fetching UMI data...") response = requests.get( f"{api_url}/indexes/umi", headers={"Authorization": f"Bearer {api_key}"} ) response.raise_for_status() data = response.json() arcpy.AddMessage("UMI layer created") except Exception as e: arcpy.AddError(f"Error: {str(e)}") class ExportToLandvexTool(object): """Export local data to Landvex""" def __init__(self): self.label = "Export to Landvex" self.description = "Export ArcGIS data to Landvex platform" self.canRunInBackground = True def getParameterInfo(self): params = [] param = arcpy.Parameter( displayName="Input Feature Class", name="input_fc", datatype="DEFeatureClass", parameterType="Required", direction="Input" ) params.append(param) param = arcpy.Parameter( displayName="API URL", name="api_url", datatype="GPString", parameterType="Required", direction="Input" ) param.value = "https://api.landvex.com/v1" params.append(param) param = arcpy.Parameter( displayName="API Key", name="api_key", datatype="GPString", parameterType="Required", direction="Input" ) params.append(param) return params def execute(self, parameters, messages): input_fc = parameters[0].valueAsText api_url = parameters[1].valueAsText api_key = parameters[2].valueAsText try: arcpy.AddMessage("Reading features...") features = [] with arcpy.da.SearchCursor(input_fc, ["SHAPE@XY", "*"]) as cursor: for row in cursor: features.append({ "lat": row[0][1], "lng": row[0][0], # Add other attributes }) arcpy.AddMessage(f"Exporting {len(features)} features...") response = requests.post( f"{api_url}/observations/bulk", headers={"Authorization": f"Bearer {api_key}"}, json={"observations": features} ) response.raise_for_status() arcpy.AddMessage("Export complete") except Exception as e: arcpy.AddError(f"Error: {str(e)}")