Install
$ agentstack add skill-impertio-studio-cross-tech-aec-claude-skill-package-crosstech-errors-coordinate-mismatch ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
crosstech-errors-coordinate-mismatch
Quick Reference
| Symptom | Likely Cause | Jump To | |---------|-------------|---------| | Model rotated 90° / lying on side | Y-up vs Z-up axis swap | [Failure Mode 1](#failure-mode-1-y-up-vs-z-up-axis-swap) | | Model 1000x too large or too small | Unit mismatch (mm/m/ft) | [Failure Mode 2](#failure-mode-2-unit-mismatch-mmmft) | | Model at (0°N, 0°E) in Gulf of Guinea | Missing georeferencing | [Failure Mode 3](#failure-mode-3-missing-georeferencing) | | Model offset by hundreds of km | Wrong CRS / EPSG code | [Failure Mode 4](#failure-mode-4-wrong-crs--epsg-code) | | Building rotated on map, footprint correct | True North rotation error | [Failure Mode 5](#failure-mode-5-true-north-rotation-error) | | Coordinates jump at project boundary | UTM zone boundary | [Failure Mode 6](#failure-mode-6-utm-zone-boundary) | | Building floating ~43m above terrain | Vertical datum confusion | [Failure Mode 7](#failure-mode-7-vertical-datum-confusion) |
Axis Convention Table
| Tool | Up Axis | Handedness | Default Units | Coordinate Order | |------|---------|-----------|---------------|-----------------| | Blender | Z-up | Right-handed | Meters | (X, Y, Z) | | Three.js | Y-up | Right-handed | Unitless | (X, Y, Z) | | QGIS | Z-up (2.5D) | Right-handed | Map units (m/deg) | (X=East, Y=North) | | IFC | Z-up | Right-handed | Project units (mm/m) | (X, Y, Z) | | Revit | Z-up | Right-handed | Internal: feet | (X, Y, Z) | | FreeCAD | Z-up | Right-handed | Internal: mm | (X, Y, Z) | | Speckle | Z-up | Right-handed | Meters | (X, Y, Z) | | web-ifc | Z-up | Right-handed | IFC project units | (X, Y, Z) |
Key boundary: ALL BIM/GIS tools use Z-up. ONLY web 3D renderers (Three.js, Babylon.js) use Y-up.
Diagnostic Decision Tree
START: Model appears wrong in target application
│
├─ Is the model rotated 90°?
│ YES → Failure Mode 1 (Y/Z axis swap)
│
├─ Is the model at the correct location but wrong size?
│ ├─ ~1000x too large → IFC in mm, target expects m (Scale missing)
│ ├─ ~3.28x too large → IFC in feet, target expects m
│ ├─ ~0.3x too small → IFC in m, target expects feet
│ └─ YES → Failure Mode 2 (unit mismatch)
│
├─ Is the model near (0, 0) / Gulf of Guinea?
│ YES → Failure Mode 3 (missing georeferencing)
│
├─ Is the model offset by >1 km from expected position?
│ ├─ Offset ~100–500 km → Failure Mode 4 (wrong CRS)
│ ├─ Offset ~500,000 m in easting → Failure Mode 6 (UTM zone)
│ └─ Offset 1.0) {
console.error("Y/Z axis swap detected: model is lying flat");
}
Fix
// JavaScript: BIM Z-up → Three.js Y-up
function bimToThreeJs(x, y, z) {
return { x: x, y: z, z: -y };
}
The negation of y → -z preserves right-handedness. Without negation, the model appears mirrored.
ALWAYS apply this transform when loading IFC/BIM data into Three.js or Babylon.js.
NEVER apply this transform when loading into Blender, QGIS, or other Z-up tools.
Tool-Specific Notes
- web-ifc / ThatOpen Components: Handles Y/Z swap automatically when using
FragmentsManager. Manual loading viaIfcLoaderrequires explicit swap. - Three.js GLTFLoader: glTF is Y-up by spec — no swap needed for glTF files. Only swap for raw IFC coordinate data.
- Speckle: Converts axes automatically per target application on receive.
Failure Mode 2: Unit Mismatch (mm/m/ft)
Symptom
Model appears at the correct location but is absurdly large or small. A 10m building spans 10km on the map (mm→m) or appears as a 3m speck (feet→m without conversion).
Detection
import ifcopenshell
ifc = ifcopenshell.open("model.ifc")
# Step 1: Read project length units
units = ifc.by_type("IfcUnitAssignment")[0]
for unit in units.Units:
if hasattr(unit, "UnitType") and unit.UnitType == "LENGTHUNIT":
if hasattr(unit, "Prefix") and unit.Prefix == "MILLI":
project_unit = "mm"
expected_scale = 0.001
elif hasattr(unit, "Name") and unit.Name == "FOOT":
project_unit = "ft"
expected_scale = 0.3048
else:
project_unit = "m"
expected_scale = 1.0
# Step 2: Read IfcMapConversion Scale
map_conv = ifc.by_type("IfcMapConversion")
if map_conv:
actual_scale = map_conv[0].Scale or 1.0 # None defaults to 1.0
if abs(actual_scale - expected_scale) > 0.0001:
print(f"SCALE MISMATCH: project={project_unit}, "
f"MapConversion.Scale={actual_scale}, expected={expected_scale}")
Common Scale Factors
| From | To | Scale Factor | |------|----|-------------| | mm | m | 0.001 | | m | m | 1.0 | | ft | m | 0.3048 | | in | m | 0.0254 |
Fix
Set IfcMapConversion.Scale to the correct conversion factor from project units to CRS map units.
ALWAYS check IfcUnitAssignment before interpreting IfcMapConversion.Scale.
NEVER assume project units are meters — Revit uses feet internally, FreeCAD uses mm.
Failure Mode 3: Missing Georeferencing
Symptom
Model appears at latitude 0°, longitude 0° (Gulf of Guinea, West Africa) or at the CRS origin point. This is the single most common BIM↔GIS integration failure.
Detection
import ifcopenshell
ifc = ifcopenshell.open("model.ifc")
has_map_conv = len(ifc.by_type("IfcMapConversion")) > 0
has_crs = len(ifc.by_type("IfcProjectedCRS")) > 0
if not has_map_conv:
print("MISSING: IfcMapConversion — no coordinate transformation defined")
if not has_crs:
print("MISSING: IfcProjectedCRS — no target CRS defined")
Fix
Add georeferencing using IfcOpenShell:
import ifcopenshell
import ifcopenshell.api
ifc = ifcopenshell.open("model.ifc")
# Add georeferencing entities
ifcopenshell.api.run("georeference.add_georeferencing", ifc)
# Set CRS and map conversion parameters
ifcopenshell.api.run("georeference.edit_georeferencing", ifc,
projected_crs={"Name": "EPSG:28992"},
coordinate_operation={
"Eastings": 155000.0,
"Northings": 463000.0,
"OrthogonalHeight": 0.0,
})
ifc.write("model_georef.ifc")
ALWAYS verify Eastings/Northings fall within the valid range of the target CRS.
NEVER leave IfcProjectedCRS.Name empty — downstream tools rely on this field to resolve the CRS.
Failure Mode 4: Wrong CRS / EPSG Code
Symptom
Model appears offset by tens to hundreds of kilometers from the expected position.
Detection
Validate that coordinate values are plausible for the assigned CRS:
| CRS | Valid Easting Range | Valid Northing Range | |-----|-------------------|---------------------| | EPSG:28992 (RD New) | -7,000 to 300,000 | 289,000 to 629,000 | | EPSG:32631 (UTM 31N) | 166,000 to 834,000 | 0 to 9,400,000 | | EPSG:32632 (UTM 32N) | 166,000 to 834,000 | 0 to 9,400,000 |
import ifcopenshell
ifc = ifcopenshell.open("model.ifc")
map_conv = ifc.by_type("IfcMapConversion")
crs = ifc.by_type("IfcProjectedCRS")
if map_conv and crs:
e = map_conv[0].Eastings
n = map_conv[0].Northings
crs_name = crs[0].Name
# Example: validate RD New range
if "28992" in (crs_name or ""):
if not (-7000 0.001:
print(f"WARNING: direction vector not normalized (length={vec_length:.4f})")
Fix
Set XAxisAbscissa = cos(angle) and XAxisOrdinate = sin(angle) where angle is the anti-clockwise rotation from grid north (CRS Y-axis) to project north.
ALWAYS verify the direction vector is normalized (length = 1.0).
NEVER confuse the sign convention — positive angle = anti-clockwise rotation in IFC.
Failure Mode 6: UTM Zone Boundary
Symptom
Project straddles a UTM zone boundary. Coordinates jump by ~500,000m in easting, or measurements near the boundary show 0.04% distortion.
Detection
from pyproj import CRS
def utm_zone_from_longitude(lon):
"""Return UTM zone number for a given longitude."""
return int((lon + 180) / 6) + 1
# Example: Netherlands straddles UTM zones 31 and 32
# Zone 31N: 0°E to 6°E (central meridian 3°E)
# Zone 32N: 6°E to 12°E (central meridian 9°E)
western_nl_zone = utm_zone_from_longitude(4.5) # Zone 31
eastern_nl_zone = utm_zone_from_longitude(6.5) # Zone 32
Fix
NEVER mix coordinates from different UTM zones in one project.
Use a single CRS that covers the entire project area:
- Netherlands: EPSG:28992 (RD New) covers the entire country
- Europe-wide: EPSG:3035 (ETRS89 / LAEA) for pan-European projects
- If UTM is required, pick the zone containing the project centroid
Failure Mode 7: Vertical Datum Confusion
Symptom
Model floats ~42–44 meters above the terrain (Netherlands) or is buried below ground. Horizontal position is correct.
Cause
Mixing NAP (Normaal Amsterdams Peil) orthometric heights with WGS84 ellipsoidal heights. In the Netherlands, the geoid undulation (difference between ellipsoid and geoid) is approximately 42–44 meters.
Detection
import ifcopenshell
ifc = ifcopenshell.open("model.ifc")
crs = ifc.by_type("IfcProjectedCRS")
map_conv = ifc.by_type("IfcMapConversion")
if crs:
vert_datum = crs[0].VerticalDatum
if vert_datum is None:
print("WARNING: No vertical datum specified — height interpretation ambiguous")
if map_conv:
h = map_conv[0].OrthogonalHeight
if abs(h) > 40 and abs(h) < 50:
print(f"SUSPICIOUS: OrthogonalHeight={h}m — possible NAP↔ellipsoid confusion")
Fix
from pyproj import Transformer
# Transform NAP height to WGS84 ellipsoidal height (or vice versa)
# Requires PROJ grid files (nl_nsgi_nlgeo2018.tif)
t = Transformer.from_crs(
"EPSG:7415", # RD New + NAP (compound)
"EPSG:4979", # WGS84 3D (geographic + ellipsoidal height)
always_xy=True
)
lon, lat, h_ellipsoid = t.transform(easting, northing, h_nap)
geoid_undulation = h_ellipsoid - h_nap # ~42-44m in NL
ALWAYS specify IfcProjectedCRS.VerticalDatum (e.g., "NAP") to make height interpretation unambiguous.
NEVER assume heights are ellipsoidal — most BIM models use orthometric (above sea level) heights.
Critical Rules
- ALWAYS check axis conventions before crossing a tool boundary — Z-up to Y-up swaps are the most common error.
- ALWAYS verify
IfcMapConversion.Scalematches the ratio of project units to CRS units. - ALWAYS use
always_xy=Truewhen creating pyproj Transformers. - ALWAYS validate coordinate ranges against the assigned CRS before declaring georeferencing correct.
- NEVER assume georeferencing exists — explicitly check for
IfcMapConversionandIfcProjectedCRS. - NEVER mix coordinates from different UTM zones in a single project.
- NEVER assume heights are ellipsoidal — BIM models use orthometric heights by default.
- NEVER skip the direction vector normalization check for True North rotation.
- ALWAYS check for compound errors — multiple failure modes can occur simultaneously.
- ALWAYS verify fixes with a known reference point before declaring the issue resolved.
Reference Links
- [references/methods.md](references/methods.md) — Diagnostic API signatures for coordinate issues per tool
- [references/examples.md](references/examples.md) — Real coordinate error scenarios with step-by-step fixes
- [references/anti-patterns.md](references/anti-patterns.md) — Coordinate handling mistakes to avoid
Official Sources
- https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcrepresentationresource/lexical/ifcmapconversion.htm
- https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcrepresentationresource/lexical/ifcprojectedcrs.htm
- https://pyproj4.github.io/pyproj/stable/api/transformer.html
- https://ifcopenshell.github.io/docs/python/html/ifcopenshell-python/geometry_processing.html
- https://epsg.io/
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Cross-Tech-AEC-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.