Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion src/e84_geoai_common/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@
from e84_geoai_common.tracing import timed_function


def validate_and_fix_geometry(geom: BaseGeometry) -> BaseGeometry:
"""Validate and fix invalid geometries.

Common issues in GeoSpatial data include self-intersections, duplicate vertices,
and topological errors. This function attempts to fix these automatically.

Args:
geom: The geometry to validate and fix.

Returns:
A valid geometry. If the input is already valid, returns it unchanged.
If invalid, attempts to fix using buffer(0) which resolves many common issues.

Example:
>>> from shapely import Polygon
>>> invalid = Polygon([(0, 0), (1, 1), (1, 0), (0, 1), (0, 0)]) # self-intersecting
>>> fixed = validate_and_fix_geometry(invalid)
>>> fixed.is_valid
True
"""
if geom.is_valid:
return geom
# buffer(0) is a common technique to fix invalid geometries
return geom.buffer(0)


def geometry_from_wkt(wkt: str) -> BaseGeometry:
"""Create shapely geometry from Well-Known Text (WKT) string."""
return shapely.from_wkt(wkt) # type: ignore[reportUnknownVariableType]
Expand Down Expand Up @@ -222,7 +248,7 @@ def remove_extraneous_geoms(geom: BaseGeometry, *, max_points: int) -> BaseGeome
return combine_geometry(geoms_to_combine)


# FUTURE the performance of this could be spead up for very large geometries with many small
# FUTURE the performance of this could be sped up for very large geometries with many small
# polygons by comparing the number of sub geometries to the number of total points. If a ratio
# exceeds a certain percentage then it may make sense to remove geometries initially that
# are less than a certain percent of the total area. Then simplify after that. That could help
Expand Down Expand Up @@ -347,3 +373,48 @@ def add_buffer(g: BaseGeometry, distance_km: float) -> BaseGeometry:
# This will fall apart at the poles but works for our current use cases.

return g.buffer((lon_distance + lat_distance) / 2.0)


def approximate_area_km2(g: BaseGeometry) -> float:
"""Calculate approximate area in square kilometers for a geometry in WGS84 coordinates.

Uses a simple approximation based on the average latitude of the geometry.
More accurate than using square degrees directly, but less accurate than
proper geodesic calculations. Suitable for most GeoSpatial AI applications
where approximate area is sufficient.

Args:
g: The input geometry in WGS84 (EPSG:4326) coordinates.

Returns:
Approximate area in square kilometers.

Note:
- Assumes input geometry is in WGS84 (longitude/latitude) coordinates.
- Accuracy decreases near the poles and for very large geometries.
- For high-precision area calculations, consider reprojecting to an
appropriate projected coordinate system.

Example:
>>> from shapely.geometry import box
>>> # A rough 1-degree by 1-degree box near the equator
>>> square = box(0, 0, 1, 1)
>>> area = approximate_area_km2(square)
>>> print(f"Area: {area:.2f} km²")
Area: 12364.46 km²
"""
# Get area in square degrees
area_deg2 = g.area

# Convert to approximate km² using average latitude
avg_lat = g.centroid.y
avg_lat_rad = degrees_to_radians(avg_lat)

# One degree of latitude is approximately 111.32 km everywhere
km_per_deg_lat = 111.32

# One degree of longitude varies with latitude: 111.32 * cos(lat)
km_per_deg_lon = 111.32 * math.cos(avg_lat_rad)

# Convert square degrees to square kilometers
return area_deg2 * km_per_deg_lat * km_per_deg_lon
24 changes: 24 additions & 0 deletions src/e84_geoai_common/llm/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
ClaudeInvokeLLMRequest,
)
from e84_geoai_common.llm.models.converse import (
COHERE_COMMAND_LIGHT_TEXT,
COHERE_COMMAND_R,
COHERE_COMMAND_R_PLUS,
COHERE_COMMAND_TEXT,
CONVERSE_BEDROCK_MODEL_IDS,
LLAMA_3_1_8_B_INSTRUCT,
LLAMA_3_1_70_B_INSTRUCT,
Expand All @@ -23,6 +27,14 @@
LLAMA_3_2_11_B_VISION_INSTRUCT,
LLAMA_3_2_90_B_VISION_INSTRUCT,
LLAMA_3_3_70_B_INSTRUCT,
MISTRAL_7B_INSTRUCT,
MISTRAL_8X7B_INSTRUCT,
MISTRAL_LARGE,
MISTRAL_LARGE_2407,
MISTRAL_SMALL,
TITAN_TEXT_EXPRESS,
TITAN_TEXT_LITE,
TITAN_TEXT_PREMIER,
BedrockConverseLLM,
ConverseInvokeLLMRequest,
)
Expand All @@ -48,6 +60,10 @@
"CLAUDE_4_SONNET",
"CLAUDE_BEDROCK_MODEL_IDS",
"CLAUDE_INSTANT",
"COHERE_COMMAND_LIGHT_TEXT",
"COHERE_COMMAND_R",
"COHERE_COMMAND_R_PLUS",
"COHERE_COMMAND_TEXT",
"CONVERSE_BEDROCK_MODEL_IDS",
"LLAMA_3_1_8_B_INSTRUCT",
"LLAMA_3_1_70_B_INSTRUCT",
Expand All @@ -56,12 +72,20 @@
"LLAMA_3_2_11_B_VISION_INSTRUCT",
"LLAMA_3_2_90_B_VISION_INSTRUCT",
"LLAMA_3_3_70_B_INSTRUCT",
"MISTRAL_7B_INSTRUCT",
"MISTRAL_8X7B_INSTRUCT",
"MISTRAL_LARGE",
"MISTRAL_LARGE_2407",
"MISTRAL_SMALL",
"NOVA_BEDROCK_MODEL_IDS",
"NOVA_CANVAS",
"NOVA_LITE",
"NOVA_MICRO",
"NOVA_PRO",
"NOVA_REEL",
"TITAN_TEXT_EXPRESS",
"TITAN_TEXT_LITE",
"TITAN_TEXT_PREMIER",
"BedrockClaudeLLM",
"BedrockConverseLLM",
"BedrockNovaLLM",
Expand Down
24 changes: 24 additions & 0 deletions src/e84_geoai_common/llm/models/converse/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""Wrappers for Bedrock Converse API."""

from e84_geoai_common.llm.models.converse.converse import (
COHERE_COMMAND_LIGHT_TEXT,
COHERE_COMMAND_R,
COHERE_COMMAND_R_PLUS,
COHERE_COMMAND_TEXT,
CONVERSE_BEDROCK_MODEL_IDS,
LLAMA_3_1_8_B_INSTRUCT,
LLAMA_3_1_70_B_INSTRUCT,
Expand All @@ -9,6 +13,14 @@
LLAMA_3_2_11_B_VISION_INSTRUCT,
LLAMA_3_2_90_B_VISION_INSTRUCT,
LLAMA_3_3_70_B_INSTRUCT,
MISTRAL_7B_INSTRUCT,
MISTRAL_8X7B_INSTRUCT,
MISTRAL_LARGE,
MISTRAL_LARGE_2407,
MISTRAL_SMALL,
TITAN_TEXT_EXPRESS,
TITAN_TEXT_LITE,
TITAN_TEXT_PREMIER,
BedrockConverseLLM,
ConverseInferenceConfig,
ConverseInvokeLLMRequest,
Expand Down Expand Up @@ -61,6 +73,10 @@
)

__all__ = [
"COHERE_COMMAND_LIGHT_TEXT",
"COHERE_COMMAND_R",
"COHERE_COMMAND_R_PLUS",
"COHERE_COMMAND_TEXT",
"CONVERSE_BEDROCK_MODEL_IDS",
"LLAMA_3_1_8_B_INSTRUCT",
"LLAMA_3_1_70_B_INSTRUCT",
Expand All @@ -69,6 +85,14 @@
"LLAMA_3_2_11_B_VISION_INSTRUCT",
"LLAMA_3_2_90_B_VISION_INSTRUCT",
"LLAMA_3_3_70_B_INSTRUCT",
"MISTRAL_7B_INSTRUCT",
"MISTRAL_8X7B_INSTRUCT",
"MISTRAL_LARGE",
"MISTRAL_LARGE_2407",
"MISTRAL_SMALL",
"TITAN_TEXT_EXPRESS",
"TITAN_TEXT_LITE",
"TITAN_TEXT_PREMIER",
"BedrockConverseLLM",
"ConverseAdditionalModelRequestFields",
"ConverseAnyToolChoice",
Expand Down
30 changes: 30 additions & 0 deletions src/e84_geoai_common/llm/models/converse/converse.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@
LLAMA_3_2_90_B_VISION_INSTRUCT = "us.meta.llama3-2-90b-instruct-v1:0"
LLAMA_3_3_70_B_INSTRUCT = "us.meta.llama3-3-70b-instruct-v1:0"

# Amazon Titan Models
TITAN_TEXT_EXPRESS = "amazon.titan-text-express-v1"
TITAN_TEXT_LITE = "amazon.titan-text-lite-v1"
TITAN_TEXT_PREMIER = "amazon.titan-text-premier-v1:0"

# Mistral AI Models
MISTRAL_7B_INSTRUCT = "mistral.mistral-7b-instruct-v0:2"
MISTRAL_8X7B_INSTRUCT = "mistral.mixtral-8x7b-instruct-v0:1"
MISTRAL_LARGE = "mistral.mistral-large-2402-v1:0"
MISTRAL_LARGE_2407 = "mistral.mistral-large-2407-v1:0"
MISTRAL_SMALL = "mistral.mistral-small-2402-v1:0"

# Cohere Models
COHERE_COMMAND_TEXT = "cohere.command-text-v14"
COHERE_COMMAND_LIGHT_TEXT = "cohere.command-light-text-v14"
COHERE_COMMAND_R = "cohere.command-r-v1:0"
COHERE_COMMAND_R_PLUS = "cohere.command-r-plus-v1:0"

# DEPRECATED: Use the constants above instead.
CONVERSE_BEDROCK_MODEL_IDS = {
"Claude 3 Haiku": CLAUDE_3_HAIKU,
Expand All @@ -104,6 +122,18 @@
"Llama 3.2 3B Instruct": LLAMA_3_2_3_B_INSTRUCT,
"Llama 3.2 90B Vision Instruct": LLAMA_3_2_90_B_VISION_INSTRUCT,
"Llama 3.3 70B Instruct": LLAMA_3_3_70_B_INSTRUCT,
"Titan Text Express": TITAN_TEXT_EXPRESS,
"Titan Text Lite": TITAN_TEXT_LITE,
"Titan Text Premier": TITAN_TEXT_PREMIER,
"Mistral 7B Instruct": MISTRAL_7B_INSTRUCT,
"Mistral 8x7B Instruct": MISTRAL_8X7B_INSTRUCT,
"Mistral Large": MISTRAL_LARGE,
"Mistral Large 2407": MISTRAL_LARGE_2407,
"Mistral Small": MISTRAL_SMALL,
"Cohere Command Text": COHERE_COMMAND_TEXT,
"Cohere Command Light Text": COHERE_COMMAND_LIGHT_TEXT,
"Cohere Command R": COHERE_COMMAND_R,
"Cohere Command R+": COHERE_COMMAND_R_PLUS,
}


Expand Down
Loading