🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
7.2 KiB
Info Objects - Metric Field Analysis
Overview
The metric field in info objects (map-info-object) is NOT a simple integer or boolean. It's a calculated string containing measurements based on the geometry type.
Data from Verofy API (Map 15685)
Type 1: Marker (Point)
{
"mapinfoobjecttypeId": 1,
"metric": "Lat: 40.760311 Lng: -124.172782"
}
Format: "Lat: {latitude} Lng: {longitude}"
Type 2: Polyline (Lines/Cables)
{
"mapinfoobjecttypeId": 2,
"metric": "Mileage: 0.1519; Footage: 802"
}
Format: "Mileage: {miles}; Footage: {feet}"
Calculation:
- Calculate the total length of the LineString
- Convert to miles and feet
- Format as shown
Type 3: Polygon (Boundaries/Parcels)
{
"mapinfoobjecttypeId": 3,
"metric": "Square Miles: 0.0065"
}
Format: "Square Miles: {sq_miles}"
Calculation:
- Calculate the area of the Polygon
- Convert to square miles
- Format as shown
Required Implementation
For Cables (Type 2 - Polyline)
Current code sends polygons/parcels to info objects. For cables specifically:
from shapely.geometry import LineString
import geopandas as gpd
def calculate_line_metric(geometry):
"""Calculate metric string for a line geometry"""
if geometry.geom_type != 'LineString':
return "Invalid geometry type"
# Calculate length in degrees (WGS84)
# Convert to approximate miles and feet
# Note: This is approximate - for accurate results, reproject to appropriate CRS
# Rough conversion: 1 degree ≈ 69 miles at equator
# For more accuracy, use geopy or pyproj
length_degrees = geometry.length
length_miles = length_degrees * 69 # Approximate
length_feet = length_miles * 5280
return f"Mileage: {length_miles:.4f}; Footage: {length_feet:.0f}"
# In _upload_cables():
metric = calculate_line_metric(row.geometry)
info_data = {
"mapProjectId": int(map_id),
"name": str(row.get('Name', f'Cable-{idx}')),
"mapinfoobjecttypeId": 2,
"data": data,
"color": "#ffffff",
"alpha": "1.00",
"metric": metric # ✅ Calculated value
}
For Cabinet Boundaries & Parcels (Type 3 - Polygon)
from shapely.geometry import Polygon
def calculate_polygon_metric(geometry):
"""Calculate metric string for a polygon geometry"""
if geometry.geom_type != 'Polygon':
return "Invalid geometry type"
# Calculate area in square degrees (WGS84)
# Convert to square miles
# Note: This is approximate - for accurate results, reproject to appropriate CRS
area_sq_degrees = geometry.area
# Rough conversion: 1 degree² ≈ 4,761 square miles at equator
# This varies by latitude, so this is very approximate
area_sq_miles = area_sq_degrees * 4761
return f"Square Miles: {area_sq_miles:.4f}"
# In _upload_cabinet_boundaries() and _upload_parcels():
metric = calculate_polygon_metric(row.geometry)
info_data = {
"mapProjectId": int(map_id),
"name": str(row.get('Name', f'Boundary-{idx}')),
"mapinfoobjecttypeId": 3,
"data": data,
"color": "#ffffff",
"alpha": "0.40",
"metric": metric # ✅ Calculated value
}
Better Implementation Using GeoPandas
GeoPandas can handle CRS transformations for more accurate calculations:
def calculate_geometry_metric(geometry, geom_type):
"""
Calculate metric string for any geometry type
Args:
geometry: Shapely geometry object
geom_type: 1 (Point), 2 (LineString), 3 (Polygon)
"""
if geom_type == 1: # Point/Marker
return f"Lat: {geometry.y:.6f} Lng: {geometry.x:.6f}"
elif geom_type == 2: # LineString/Cable
# For accurate length, project to UTM or other metric CRS
# Rough approximation using great circle distance
from shapely.ops import transform
import pyproj
from functools import partial
# Define projection from WGS84 to a metric system (meters)
project = partial(
pyproj.transform,
pyproj.Proj('EPSG:4326'), # WGS84
pyproj.Proj('EPSG:3857') # Web Mercator (meters)
)
# Transform and calculate length
line_projected = transform(project, geometry)
length_meters = line_projected.length
length_miles = length_meters * 0.000621371
length_feet = length_meters * 3.28084
return f"Mileage: {length_miles:.4f}; Footage: {length_feet:.0f}"
elif geom_type == 3: # Polygon
# Similar projection for area
from shapely.ops import transform
import pyproj
from functools import partial
project = partial(
pyproj.transform,
pyproj.Proj('EPSG:4326'), # WGS84
pyproj.Proj('EPSG:3857') # Web Mercator (meters)
)
polygon_projected = transform(project, geometry)
area_sq_meters = polygon_projected.area
area_sq_miles = area_sq_meters * 0.000000386102
return f"Square Miles: {area_sq_miles:.4f}"
return "Unknown type"
Simplified Approach (Good Enough for Most Cases)
Since all geometries are in WGS84 (EPSG:4326), and the study area is around Eureka, CA (latitude ~40°):
def calculate_metric_simple(geometry, geom_type):
"""
Simplified metric calculation
Good enough approximation for small areas at mid-latitudes
"""
if geom_type == 1: # Point
return f"Lat: {geometry.y:.6f} Lng: {geometry.x:.6f}"
elif geom_type == 2: # LineString
# At 40° latitude: 1 degree longitude ≈ 53 miles
# 1 degree latitude ≈ 69 miles
coords = list(geometry.coords)
total_miles = 0
for i in range(len(coords) - 1):
lon1, lat1 = coords[i]
lon2, lat2 = coords[i + 1]
# Approximate distance
dlat = (lat2 - lat1) * 69
dlon = (lon2 - lon1) * 53 # At 40° latitude
segment_miles = (dlat**2 + dlon**2)**0.5
total_miles += segment_miles
total_feet = total_miles * 5280
return f"Mileage: {total_miles:.4f}; Footage: {total_feet:.0f}"
elif geom_type == 3: # Polygon
# Approximate area calculation
# At 40° latitude: 1 deg² ≈ 3,657 square miles
area_sq_degrees = geometry.area
area_sq_miles = area_sq_degrees * 3657
return f"Square Miles: {area_sq_miles:.4f}"
return ""
Recommendation
Use GeoPandas with proper CRS transformation for accurate results:
- Read geometries (already in WGS84 / EPSG:4326)
- Convert to a local projected CRS (like UTM Zone 10N - EPSG:32610 for California)
- Calculate length/area in meters
- Convert to miles/feet/square miles
- Format as string
This ensures accurate measurements regardless of latitude.
Impact on Current Code
Current assumption: "metric": 0 ❌
Reality: "metric": "Mileage: 0.1519; Footage: 802" ✅
Changes needed:
- Add metric calculation functions
- Update
_upload_cables()to calculate line metrics - Update
_upload_cabinet_boundaries()to calculate polygon metrics - Update
_upload_parcels()to calculate polygon metrics
This is more complex than originally thought but necessary for API compatibility.