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
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Bug report
description: Report a loading, alignment, geometry or interface problem.
body:
- type: textarea
id: environment
attributes:
label: Versions and component
description: RhinoSpatial version, Rhino version, operating system and affected component.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps and source
description: How can we reproduce this? Include a public source URL or a small non-private file, study area and SRS where possible. Do not share API keys or private project data.
validations:
required: true
- type: textarea
id: behavior
attributes:
label: Expected and actual behavior
validations:
required: true
- type: textarea
id: status
attributes:
label: Status output and screenshots
description: Paste the component's full Status message and relevant screenshots, with secrets removed.
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: User guide and examples
url: https://pascalnun.eu/tools/rhinospatial/
about: Installation and the shared Spatial Context workflow.
- name: Component and source reference
url: https://github.com/PascalNun/RhinoSpatial/blob/main/docs/COMPONENT_REFERENCE.md
about: Supported formats, inputs, coordinate behavior and diagnostic messages.
17 changes: 17 additions & 0 deletions .github/ISSUE_TEMPLATE/improvement.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Improvement or provider compatibility
description: Suggest a practical improvement to an existing RhinoSpatial workflow.
body:
- type: textarea
id: workflow
attributes:
label: Workflow and problem
description: What are you trying to do, and what is difficult or unsupported today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
description: Explain the useful outcome. Include a public provider URL or sample when relevant.
validations:
required: true
9 changes: 9 additions & 0 deletions .github/SUPPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Help and support

- Start with the [website and user guide](https://pascalnun.eu/tools/rhinospatial/).
- See the [component reference](../docs/COMPONENT_REFERENCE.md) for formats, inputs, coordinate behavior and diagnostics.
- Try the [Grasshopper examples](../examples/README.md).
- Download the recommended version from [GitHub Latest](https://github.com/PascalNun/RhinoSpatial/releases/latest), [Food4Rhino](https://www.food4rhino.com/en/app/rhinospatial) or Rhino 8's Package Manager.
- Report reproducible bugs or propose improvements through [GitHub Issues](https://github.com/PascalNun/RhinoSpatial/issues/new/choose).

Include the component's full Status output and a small public test source where possible. Remove API keys, passwords and private data before sharing a definition or screenshot. Provider-specific service changes and unusual file metadata may require a compatibility fix.
11 changes: 11 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## What changes

Describe the problem, trigger and resulting behavior.

## Validation

List automated checks and any relevant Rhino/Grasshopper checks actually performed. State remaining manual validation or provider limitations.

## Compatibility

Note effects on component identities, input/output layouts, saved definitions, shared alignment, dependencies and documentation.
11 changes: 10 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

jobs:
build-and-check:
runs-on: macos-14
runs-on: macos-15
timeout-minutes: 20

steps:
Expand All @@ -37,6 +37,15 @@ jobs:
- name: Check reference sources
run: dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- reference-source-check

- name: Check source readers
run: dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- source-reader-check

- name: Check terrain meshes
run: dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- terrain-mesh-check

- name: Check ATOM downloads and cache
run: dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- lod2-atom-check

- name: Check 3D Tiles selection
run: dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- tiles-selection-check

Expand Down
29 changes: 26 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
# Changelog

RhinoSpatial follows an alpha release line while its existing source categories are stabilized through real project use. Earlier releases remain documented on [GitHub Releases](https://github.com/PascalNun/RhinoSpatial/releases).
RhinoSpatial improves its established source categories through real project use. Earlier releases remain documented on [GitHub Releases](https://github.com/PascalNun/RhinoSpatial/releases).

## Unreleased
## 0.4.0 — 2026-11-01

Version 0.4.0 is the recommended public release and is published as GitHub Latest. Component names, GUIDs and normal input/output connections are retained. Provider-specific compatibility continues to evolve.

### Automatic LoD2 ATOM downloads

- Added ATOM download services to the existing `Load LoD2 Buildings` source input, preserving the component GUID and input/output layout.
- Follow dataset feeds and pagination, select intersecting tiles from supported spatial metadata or declared UTM kilometre grids, and reuse the existing CityGML ZIP import and shared placement path.
- Added persistent validated archive caching, download progress, cancellation/time limits and explicit diagnostics for missing spatial information, failed transfers and unsupported archives.
- Queue progress updates asynchronously to prevent a Rhino UI/Grasshopper worker deadlock during ATOM loading on macOS.
- Diagnose explicitly advertised CSV/raster downloads as unsuitable for the building loader before evaluating their tile grid, including Berlin's DGM1 terrain feed.
- Recognize standard AdV UTM32/UTM33 horizontal CRS names, including Berlin's compound height-reference identifier, while retaining source heights.
- Added deterministic ATOM regression checks and a live Berlin probe. Museum Island and a two-tile boundary area were exercised through download, cache reuse and filtered CityGML parsing; interactive loading in Rhino on macOS was confirmed after correcting the progress deadlock; detailed cross-layer alignment remains on the manual checklist.

### Terrain and vector correctness

- Corrected Esri ASCII Grid cell-center placement and reference extents, optional NoData headers, and wrapped cell values; malformed dimensions and incomplete or excess data now return clear errors.
- Reject irregular XYZ/CSV terrain spacing and non-finite positions instead of distorting their placement.
- Transform terrain from every supported source CRS into the shared context, preserve downsampled raster edges, and leave NoData areas open instead of creating artificial flat surfaces. Unsupported WCS coordinate systems and non-overlapping coverage fail explicitly.
- Prevent missing terrain elevations from being reused as invalid building-height samples.
- Accept GeoJSON null attributes and empty geometries without fabricating origin points; correctly read versioned EPSG URN and URL identifiers.
- Added deterministic source-reader and terrain-mesh regression checks. Rhino/Grasshopper visual validation is still required for the changed terrain output.

### Documentation and package presentation

- Reorganized the README around the architect and designer workflow, with a shorter introduction, clearer installation path, and progressive links into technical detail.
- Added a complete [Component and Source Reference](docs/COMPONENT_REFERENCE.md) so supported formats, inputs, placement behavior, fallbacks, and diagnostics remain available without overwhelming the first page.
- Refreshed the project scope, roadmap, workflow guide, examples, validation notes, and source catalogue to match the current `0.3.3-alpha` behavior and terminology.
- Refreshed the project scope, roadmap, workflow guide, examples, validation notes, and source catalogue to match the established toolkit scope and current terminology.
- Added a self-contained release-package README and corrected package instructions, links, author name, and user-facing metadata.
- Updated version and installation guidance for 0.4.0; the managed Yak package now targets Rhino 8 on macOS and Windows.
- Added contributor/support guidance, issue forms and a pull request template, and included all six regression suites in GitHub validation.

## 0.3.3-alpha — 2026-07-18

Expand Down
32 changes: 32 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Contributing to RhinoSpatial

RhinoSpatial brings multiple geospatial layers into one aligned Rhino and Grasshopper study area. Contributions should strengthen that workflow through reproducible compatibility fixes, correctness, clear diagnostics, documentation and practical examples.

## Report a problem

Open a [bug report](https://github.com/PascalNun/RhinoSpatial/issues/new/choose). Include the RhinoSpatial version, Rhino version, operating system, component, selected area/SRS, exact status message and steps to reproduce. A small public source URL or non-private sample is especially helpful. Never publish API keys, authentication links or private project data.

## Propose a change

Check the [project scope](docs/PROJECT_SCOPE.md) and [roadmap](docs/ROADMAP.md). For substantial changes, describe the real workflow and proposed behavior in an issue first. Keep pull requests focused and preserve component names, GUIDs, input/output layouts and existing Grasshopper definitions.

Reusable source readers, clients and coordinate logic belong in `RhinoSpatial.Core`; Grasshopper orchestration and Rhino geometry belong in `RhinoSpatial`. All aligned sources use the shared Spatial Context. Unsupported coordinates, missing coverage and source failures should produce honest diagnostics.

## Build and validate

Install a .NET SDK able to build the solution's .NET 7 and .NET 10 targets, then run:

```bash
dotnet build RhinoSpatial.sln -c Release
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- ogc-request-check
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- reference-source-check
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- tiles-selection-check
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- source-reader-check
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- terrain-mesh-check
dotnet run --project RhinoSpatial.Sandbox.csproj -c Release --no-build -- lod2-atom-check
git diff --check
```

Update the relevant documentation and [test-source catalogue](docs/TEST_SOURCES.md). Changes to Rhino geometry, preview, materials, UI or saved definitions also need the relevant [manual checks](examples/VALIDATION.md); console checks do not establish visual correctness. Explain the validation performed and its limits in the pull request.

RhinoSpatial uses the [MIT License](LICENSE). Keep third-party notices current when dependencies change.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,19 @@ The easiest installation path is Rhino 8's Package Manager:

1. Open Rhino 8.
2. Run `PackageManager`.
3. Enable **Include pre-releases** while RhinoSpatial remains in alpha.
4. Search for `RhinoSpatial`.
5. Install the package and restart Rhino or Grasshopper if requested.
3. Search for `RhinoSpatial`.
4. Install the package and restart Rhino or Grasshopper if requested.

You can also find RhinoSpatial on [Food4Rhino](https://www.food4rhino.com/en/app/rhinospatial?lang=en).

For manual installation, open [GitHub Releases](https://github.com/PascalNun/RhinoSpatial/releases), choose the current release, and download its `RhinoSpatial-…-alpha.zip` asset. See the [manual installation guide](package/food4rhino/INSTALL.md) for the Grasshopper Libraries locations on macOS and Windows.
For manual installation, open [GitHub Releases](https://github.com/PascalNun/RhinoSpatial/releases), choose the current release, and download its `RhinoSpatial-0.4.0.zip` asset. See the [manual installation guide](package/food4rhino/INSTALL.md) for the Grasshopper Libraries locations on macOS and Windows.

Release packages include a small number of third-party libraries for raster handling, topology operations, coordinate transforms, and 3D tile decoding. Their licenses are documented in [Third-Party Notices](THIRD-PARTY-NOTICES.md).

## New in 0.4.0

Connect a supported ATOM download service directly to `Load LoD2 Buildings`: RhinoSpatial selects the required CityGML tiles for your study area and reuses completed downloads. This release also adds AdV UTM CRS names and corrects terrain placement, NoData handling and local vector parsing. See the [changelog](CHANGELOG.md) for details.

## Your first RhinoSpatial workflow

1. Open Grasshopper and place `Spatial Context`.
Expand Down Expand Up @@ -77,7 +80,7 @@ For a visual walkthrough, open the [RhinoSpatial workflow guide](docs/SHOWCASE.m
| Maps, aerial imagery, orthophotos, or raster overlays | `Load WMS` | WMS imagery placed as an aligned image mesh and material |
| Local georeferenced imagery | `Load GeoTIFF` | A local `.tif` or `.tiff` placed in the same study space |
| Ground and elevation context | `Load Terrain` | WCS, GeoTIFF DEM, Esri ASCII Grid, XYZ/CSV grid, or a small-area fallback; aligned terrain meshes |
| Official 3D building and roof geometry | `Load LoD2 Buildings` | WFS, CityGML/GML/XML, CityJSON, folder, or ZIP archive; building Breps |
| Official 3D building and roof geometry | `Load LoD2 Buildings` | WFS, ATOM download feeds, CityGML/GML/XML, CityJSON, folder, or ZIP archive; building Breps |
| Fast urban and landscape context | `Load OSM` | OpenStreetMap buildings, roads, water, green areas, and rail |
| Photogrammetric visual reference | `3D Tiles Viewer (Google)` | Temporary preview meshes and materials using your own Google Maps API key |

Expand All @@ -89,9 +92,9 @@ See the [Component and Source Reference](docs/COMPONENT_REFERENCE.md) for suppor

## Current status

RhinoSpatial is in active alpha development. The current `v0.3.3-alpha` line contains the complete planned source set: Spatial Context, vector data, imagery, terrain, LoD2 buildings, GeoTIFF, OSM, and the optional Google 3D Tiles reference viewer.
**0.4.0 is the recommended public release**, available as GitHub Latest and through Rhino’s Package Manager without enabling pre-releases. RhinoSpatial contains the complete planned source set: Spatial Context, vector data, imagery, terrain, LoD2 buildings, GeoTIFF, OSM, and the optional Google 3D Tiles reference viewer.

The toolkit has been exercised against a growing international set of public services and local formats. Individual providers, coordinate systems, geometry types, service versions, API access rules, and file metadata can still expose edge cases. For project work, prefer official or project-specific sources and keep an older working definition when testing a new alpha release.
The toolkit has been exercised against a growing international set of public services and local formats. Individual providers, coordinate systems, geometry types, service versions, API access rules, and file metadata can still expose edge cases. For project work, prefer official or project-specific sources and keep an older working definition when upgrading.

The [changelog](CHANGELOG.md) records release changes. The [test source catalogue](docs/TEST_SOURCES.md) documents the current compatibility set and known provider limitations.

Expand Down
31 changes: 15 additions & 16 deletions RhinoSpatial.Core/GeoJsonReader.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

namespace RhinoSpatial.Core
Expand Down Expand Up @@ -34,22 +35,12 @@ public static class GeoJsonReader
var epsgIndex = name.LastIndexOf("EPSG", StringComparison.OrdinalIgnoreCase);
if (epsgIndex >= 0)
{
var digits = string.Empty;
for (var index = epsgIndex + 4; index < name.Length; index++)
var code = name[(epsgIndex + 4)..].Trim()
.Split(new[] { ':', '/', '#' }, StringSplitOptions.RemoveEmptyEntries)
.LastOrDefault();
if (!string.IsNullOrEmpty(code) && code.All(char.IsAsciiDigit))
{
if (char.IsDigit(name[index]))
{
digits += name[index];
}
else if (digits.Length > 0)
{
break;
}
}

if (!string.IsNullOrWhiteSpace(digits))
{
return $"EPSG:{digits}";
return $"EPSG:{code}";
}
}

Expand Down Expand Up @@ -100,6 +91,10 @@ private static WfsFeature ReadFeature(JsonElement featureElement, string sourceL
private static Dictionary<string, string?> ReadAttributes(JsonElement propertiesElement)
{
var attributes = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
if (propertiesElement.ValueKind == JsonValueKind.Null)
{
return attributes;
}

foreach (var property in propertiesElement.EnumerateObject())
{
Expand Down Expand Up @@ -128,6 +123,10 @@ private static WfsGeometry ReadGeometry(JsonElement geometryElement)
}

var geometryType = geometryElement.GetProperty("type").GetString() ?? "Unknown";
if (coordinatesElement.ValueKind == JsonValueKind.Array && coordinatesElement.GetArrayLength() == 0)
{
return new WfsGeometry { Type = geometryType };
}

return geometryType switch
{
Expand Down Expand Up @@ -248,7 +247,7 @@ private static Coordinate2D ReadPoint(JsonElement pointElement)
{
if (pointElement.GetArrayLength() < 2)
{
return new Coordinate2D(0, 0);
throw new InvalidOperationException("A GeoJSON point must contain at least two coordinates.");
}

return new Coordinate2D(pointElement[0].GetDouble(), pointElement[1].GetDouble());
Expand Down
Loading
Loading