Skip to content
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change log

### 0.3.1 - 2025-02-12
- Restored custom field support across schemas (CustomNode/Edge/Zone) while aligning required fields with original behavior.
- Relaxed edges/zones global required lists to let custom features omit `highway` (kept in branch-specific schemas).
- Enhanced 0.2 compatibility guard: allow `ext:` on nodes, block custom content per dataset with specific messages (e.g., Custom Edge/Point/Polygon), and reject non-point `ext:`.
- Added schema parity tests and fixtures for custom feature branches and 0.2 vs 0.3 behavior, including guard reason checks.
- Improved diff tooling/reporting and parity fixtures to cover custom scenarios.

### 0.3.0
- Default to OSW 0.3 dataset-specific schemas (edges, lines, nodes, points, polygons, zones) with filename-driven selection; removed legacy monolithic/geometry schema files.
- Enforce the six canonical OSW 0.3 filenames inside datasets; reject non-standard names and detect duplicates/missing required files (with new unit tests).
Expand Down
41 changes: 33 additions & 8 deletions src/python_osw_validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,24 @@ def _schema_key_from_text(self, text: Optional[str]) -> Optional[str]:
return key
return None

def _contains_disallowed_features_for_02(self, geojson_data: Dict[str, Any]) -> bool:
"""Detect Tree coverage or Custom Point/Line/Polygon in legacy 0.2 datasets."""
def _contains_disallowed_features_for_02(self, geojson_data: Dict[str, Any]) -> set:
"""Detect Tree coverage or Custom content in legacy 0.2 datasets.

Returns a set of reason tags, e.g. {"tree", "custom_ext", "custom_token"}.
Empty set means no 0.2-only violations detected.
"""
reasons = set()
for feat in geojson_data.get("features", []):
props = feat.get("properties") or {}
geom = feat.get("geometry") or {}
geom_type = geom.get("type") if isinstance(geom, dict) else None
is_point = isinstance(geom_type, str) and geom_type.lower() == "point"

val = props.get("natural")
if isinstance(val, str) and val.strip().lower() in {"tree", "wood"}:
return True
reasons.add("tree")
if any(k in props for k in ("leaf_cycle", "leaf_type")):
return True
reasons.add("tree")
for k, v in props.items():
target = ""
if isinstance(v, str):
Expand All @@ -142,8 +151,8 @@ def _contains_disallowed_features_for_02(self, geojson_data: Dict[str, Any]) ->
if any(tok in target for tok in ["custom point", "custom_point", "custompoint",
"custom line", "custom_line", "customline",
"custom polygon", "custom_polygon", "custompolygon"]):
return True
return False
reasons.add("custom_token")
return reasons

# ----------------------------
# Schema selection
Expand Down Expand Up @@ -475,9 +484,25 @@ def validate_osw_errors(self, file_path: str, max_errors: int) -> bool:

schema_url = geojson_data.get('$schema')
if isinstance(schema_url, str) and '0.2/schema.json' in schema_url:
if self._contains_disallowed_features_for_02(geojson_data):
reasons = self._contains_disallowed_features_for_02(geojson_data)
if reasons:
dataset_key = self._schema_key_from_text(file_path) or "data"
custom_label_map = {
"edges": "Custom Edge",
"lines": "Custom Line",
"polygons": "Custom Polygon",
"zones": "Custom Polygon/Zone",
"points": "Custom Point",
"nodes": "Custom Node",
}
parts = []
if "tree" in reasons:
parts.append("Tree coverage")
if "custom_ext" in reasons or "custom_token" in reasons:
parts.append(custom_label_map.get(dataset_key, "Custom content"))
msg = f"0.2 schema does not support " + " and ".join(parts)
self.log_errors(
message="0.2 schema does not support Tree coverage, Custom Point, Custom Line, and Custom Polygon",
message=msg,
filename=os.path.basename(file_path),
feature_index=None,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,10 +308,49 @@
"required": [
"_id",
"_u_id",
"_v_id",
"highway"
"_v_id"
],
"anyOf": [
{
"title": "CustomEdgeFields",
"type": "object",
"additionalProperties": false,
"properties": {
"_id": {
"minLength": 1,
"type": "string"
},
"_u_id": {
"minLength": 1,
"type": "string"
},
"_v_id": {
"minLength": 1,
"type": "string"
},
"foot": {
"description": "A field that indicates whether an edge can be used by pedestrians.",
"enum": [
"designated",
"destination",
"no",
"permissive",
"private",
"use_sidepath",
"yes"
],
"type": "string"
}
},
"required": [
"_id",
"_u_id",
"_v_id"
],
"patternProperties": {
"^ext:.*$": {}
}
},
{
"title": "AlleyFields",
"type": "object",
Expand Down Expand Up @@ -1895,4 +1934,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,23 @@
"_id"
],
"anyOf": [
{
"title": "CustomNodeFields",
"type": "object",
"additionalProperties": false,
"properties": {
"_id": {
"minLength": 1,
"type": "string"
}
},
"required": [
"_id"
],
"patternProperties": {
"^ext:.*$": {}
}
},
{
"title": "BareNodeFields",
"type": "object",
Expand Down Expand Up @@ -417,4 +434,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,65 @@
},
"required": [
"_id",
"_w_id",
"highway"
"_w_id"
],
"anyOf": [
{
"title": "CustomZoneFields",
"type": "object",
"additionalProperties": false,
"properties": {
"_id": {
"minLength": 1,
"type": "string"
},
"_w_id": {
"items": {
"type": "string"
},
"type": "array"
},
"foot": {
"description": "A field that indicates whether an edge can be used by pedestrians.",
"enum": [
"designated",
"destination",
"no",
"permissive",
"private",
"use_sidepath",
"yes"
],
"type": "string"
},
"name": {
"description": "A field for a designated name for an entity. Example: an official name for a trail.",
"type": "string"
},
"surface": {
"description": "A field for the surface material of the path.",
"enum": [
"asphalt",
"concrete",
"dirt",
"grass",
"grass_paver",
"gravel",
"paved",
"paving_stones",
"unpaved"
],
"type": "string"
}
},
"required": [
"_id",
"_w_id"
],
"patternProperties": {
"^ext:.*$": {}
}
},
{
"title": "PedestrianZoneFields",
"type": "object",
Expand Down Expand Up @@ -302,4 +357,4 @@
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/python_osw_validation/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.3.0'
__version__ = '0.3.1'
17 changes: 17 additions & 0 deletions tests/schema/fixtures/edges/invalid_bad_geometry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [0, 0]},
"properties": {
"_id": "edge-4",
"_u_id": "node-f",
"_v_id": "node-g",
"footway": "sidewalk",
"highway": "footway"
}
}
]
}
27 changes: 27 additions & 0 deletions tests/schema/fixtures/edges/invalid_custom_edge_schema02.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.2/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "sw_3234",
"highway": "footway",
"footway": "traffic_island",
"_u_id": "1",
"_v_id": "2"
}
},
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "cus_123",
"_u_id": "3",
"_v_id": "5",
"ext:test_field": "custom_value"
}
}
]
}
15 changes: 15 additions & 0 deletions tests/schema/fixtures/edges/invalid_missing_required.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "edge-3",
"_u_id": "node-e",
"footway": "sidewalk"
}
}
]
}
17 changes: 17 additions & 0 deletions tests/schema/fixtures/edges/valid_custom_edge.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "custom-edge-1",
"_u_id": "node-1",
"_v_id": "node-2",
"highway": "service",
"foot": "yes"
}
}
]
}
16 changes: 16 additions & 0 deletions tests/schema/fixtures/edges/valid_custom_edge_no_highway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "custom-edge-2",
"_u_id": "node-3",
"_v_id": "node-4",
"foot": "yes"
}
}
]
}
17 changes: 17 additions & 0 deletions tests/schema/fixtures/edges/valid_minimal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[0, 0], [1, 1]]},
"properties": {
"_id": "edge-1",
"_u_id": "node-a",
"_v_id": "node-b",
"footway": "sidewalk",
"highway": "footway"
}
}
]
}
19 changes: 19 additions & 0 deletions tests/schema/fixtures/edges/valid_typical.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "LineString", "coordinates": [[-1, 0], [0, 1]]},
"properties": {
"_id": "edge-2",
"_u_id": "node-c",
"_v_id": "node-d",
"footway": "crossing",
"highway": "footway",
"surface": "asphalt",
"width": 2.5
}
}
]
}
11 changes: 11 additions & 0 deletions tests/schema/fixtures/lines/invalid_bad_geometry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://sidewalks.washington.edu/opensidewalks/0.3/schema.json",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [0, 0]},
"properties": {"_id": "line-3"}
}
]
}
Loading
Loading