diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9428002 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,273 @@ +# EncDotNet - editorconfig +# +# Machine-readable encoding of the C# coding style guide +# (docs/coding-style.md). CI enforces whitespace and using-directive rules; +# broader style and naming rules are surfaced by IDE analyzers and followed by +# hand. + +root = true + +########################################## +# All files +########################################## +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 4 + +[*.{json,yml,yaml,csproj,props,targets,slnx,xml,axaml,xaml,resx}] +indent_size = 2 + +[*.{cmd,bat}] +end_of_line = crlf + +[*.md] +# Trailing double-space is a hard line break in Markdown. +trim_trailing_whitespace = false + +########################################## +# C# files +########################################## +[*.cs] +indent_size = 4 +insert_final_newline = true + +#### Language / usings #### + +# File-scoped namespaces only (guide section 2). +csharp_style_namespace_declarations = file_scoped:warning + +# using directives outside the namespace, System.* first (guide section 2). +csharp_using_directive_placement = outside_namespace:warning +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false +# Unused usings (build already treats warnings as errors). +dotnet_diagnostic.IDE0005.severity = warning + +#### Formatting: newlines (Allman braces, guide section 3) #### + +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +#### Formatting: indentation #### + +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +#### Formatting: spacing #### + +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +#### Formatting: wrapping #### + +csharp_preserve_single_line_blocks = true +# Keep terse single-line statements (for example, `if (x) return false;`) intact +# rather than force-splitting them onto multiple lines (guide section 3). +csharp_preserve_single_line_statements = true + +#### 'this.' qualification - not used (guide section 4) #### + +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning + +#### Language keywords over framework type names #### + +dotnet_style_predefined_type_for_locals_parameters_members = true:warning +dotnet_style_predefined_type_for_member_access = true:warning + +#### var preferences (guide section 5) #### +# `var` is the default; explicit types remain acceptable where clearer, so +# these are suggestions rather than warnings. + +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +#### Expression-bodied members (guide section 3) #### + +csharp_style_expression_bodied_methods = when_on_single_line:suggestion +csharp_style_expression_bodied_constructors = when_on_single_line:suggestion +csharp_style_expression_bodied_operators = when_on_single_line:suggestion +csharp_style_expression_bodied_properties = when_on_single_line:suggestion +csharp_style_expression_bodied_indexers = when_on_single_line:suggestion +csharp_style_expression_bodied_accessors = when_on_single_line:suggestion +csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion +csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion + +#### Modern language idioms (guide section 5) #### + +csharp_style_namespace_declarations = file_scoped:warning +csharp_prefer_braces = when_multiline:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +dotnet_style_collection_initializer = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +#### Modifiers #### + +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +dotnet_style_readonly_field = true:suggestion + +#### Parentheses clarity #### + +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent + +########################################## +# Naming conventions (guide section 4) +########################################## + +# --- Symbol groups --- +dotnet_naming_symbols.types.applicable_kinds = class, struct, enum, delegate +dotnet_naming_symbols.types.applicable_accessibilities = * + +dotnet_naming_symbols.interfaces.applicable_kinds = interface +dotnet_naming_symbols.interfaces.applicable_accessibilities = * + +dotnet_naming_symbols.type_parameters.applicable_kinds = type_parameter +dotnet_naming_symbols.type_parameters.applicable_accessibilities = * + +dotnet_naming_symbols.members.applicable_kinds = property, method, event, namespace +dotnet_naming_symbols.members.applicable_accessibilities = * + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.applicable_accessibilities = * +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = * +dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly + +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, protected, protected_internal + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, internal, private_protected + +dotnet_naming_symbols.parameters_locals.applicable_kinds = parameter, local +dotnet_naming_symbols.parameters_locals.applicable_accessibilities = * + +# --- Naming styles --- +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.camel_case.capitalization = camel_case + +dotnet_naming_style.interface_prefix.capitalization = pascal_case +dotnet_naming_style.interface_prefix.required_prefix = I + +dotnet_naming_style.type_parameter_prefix.capitalization = pascal_case +dotnet_naming_style.type_parameter_prefix.required_prefix = T + +dotnet_naming_style.underscore_camel_case.capitalization = camel_case +dotnet_naming_style.underscore_camel_case.required_prefix = _ + +# --- Rules (ordered specific -> general) --- +dotnet_naming_rule.interfaces_prefixed_i.symbols = interfaces +dotnet_naming_rule.interfaces_prefixed_i.style = interface_prefix +dotnet_naming_rule.interfaces_prefixed_i.severity = warning + +dotnet_naming_rule.type_parameters_prefixed_t.symbols = type_parameters +dotnet_naming_rule.type_parameters_prefixed_t.style = type_parameter_prefix +dotnet_naming_rule.type_parameters_prefixed_t.severity = warning + +dotnet_naming_rule.types_pascal.symbols = types +dotnet_naming_rule.types_pascal.style = pascal_case +dotnet_naming_rule.types_pascal.severity = warning + +dotnet_naming_rule.members_pascal.symbols = members +dotnet_naming_rule.members_pascal.style = pascal_case +dotnet_naming_rule.members_pascal.severity = warning + +dotnet_naming_rule.constants_pascal.symbols = constants +dotnet_naming_rule.constants_pascal.style = pascal_case +dotnet_naming_rule.constants_pascal.severity = warning + +dotnet_naming_rule.static_readonly_pascal.symbols = static_readonly_fields +dotnet_naming_rule.static_readonly_pascal.style = pascal_case +dotnet_naming_rule.static_readonly_pascal.severity = warning + +dotnet_naming_rule.public_fields_pascal.symbols = public_fields +dotnet_naming_rule.public_fields_pascal.style = pascal_case +dotnet_naming_rule.public_fields_pascal.severity = warning + +dotnet_naming_rule.private_fields_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_underscore.style = underscore_camel_case +dotnet_naming_rule.private_fields_underscore.severity = warning + +dotnet_naming_rule.parameters_locals_camel.symbols = parameters_locals +dotnet_naming_rule.parameters_locals_camel.style = camel_case +dotnet_naming_rule.parameters_locals_camel.severity = suggestion + +########################################## +# Test projects - relax naming so xUnit +# `Method_State_Expected` style is allowed. +########################################## +[tests/**.cs] +dotnet_naming_rule.members_pascal.severity = none +dotnet_diagnostic.IDE0005.severity = warning diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 568cb57..5475d34 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -105,6 +105,12 @@ S-57 uses **chain-node topology**: ## Coding Conventions +- The normative C# rules are in [`docs/coding-style.md`](../docs/coding-style.md) + and encoded in [`.editorconfig`](../.editorconfig). Follow both when changing + code. +- Before finishing, run `dotnet format whitespace EncDotNet.slnx` and + `dotnet format style EncDotNet.slnx --diagnostics IDE0005`; CI verifies both + with `--verify-no-changes`. - Target framework: **.NET 10** with nullable reference types and implicit usings enabled. - Use `ReadOnlySpan` for parsing hot paths where possible. - Use `ImmutableArray` and `ImmutableDictionary` for parsed record data — records are immutable after construction. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4da271a..a72248b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup .NET uses: actions/setup-dotnet@v4 @@ -49,6 +49,32 @@ jobs: name: nupkgs path: nupkgs/*.nupkg + format: + name: Format check + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v5 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + - name: Restore + run: dotnet restore + + - name: Verify formatting (whitespace) + # Fix locally with: dotnet format whitespace EncDotNet.slnx + run: dotnet format whitespace EncDotNet.slnx --no-restore --verify-no-changes + + - name: Verify usings (ordering + unused) + # Fix locally with: + # dotnet format style EncDotNet.slnx --diagnostics IDE0005 + run: dotnet format style EncDotNet.slnx --diagnostics IDE0005 --severity warn --no-restore --verify-no-changes + publish: needs: build strategy: @@ -74,7 +100,7 @@ jobs: MACOS_SIGNING_ENABLED: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12 != '' && github.event_name != 'pull_request' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Setup .NET uses: actions/setup-dotnet@v4 diff --git a/Directory.Build.props b/Directory.Build.props index 918776b..26982e6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -6,6 +6,11 @@ enable true true + + true + true + all diff --git a/Directory.Packages.props b/Directory.Packages.props index b492568..6c55d20 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,23 +3,23 @@ true - - - - + + + + - + - + - - - + + + diff --git a/docs/coding-style.md b/docs/coding-style.md new file mode 100644 index 0000000..033eec1 --- /dev/null +++ b/docs/coding-style.md @@ -0,0 +1,108 @@ +# C# coding style guide + +This is the **normative** coding style guide for the C#/.NET code in +EncDotNet. It makes the repository's style explicit rather than requiring +contributors to infer it from surrounding code. + +> **Enforcement.** Mechanically checkable rules are encoded in +> [`.editorconfig`](../.editorconfig). CI gates whitespace with `dotnet format +> whitespace --verify-no-changes` and using directives with `dotnet format +> style --diagnostics IDE0005 --verify-no-changes`. Broader style and naming +> rules remain hand-followed because `dotnet format` cannot safely Fix-All +> naming. When this guide and `.editorconfig` disagree, `.editorconfig` wins. + +## 1. Guiding principles + +1. Prefer consistency over personal preference. +2. Treat compiler and analyzer warnings as failures; fix causes rather than + suppressing warnings. +3. Model nullability honestly instead of defeating the analyzer. +4. Use `dotnet format` rather than hand-formatting against the rules. + +## 2. Files, namespaces, and usings + +- Use one top-level type per file and match the file name to the type. Small, + tightly coupled helpers and partial types are exceptions. +- Use file-scoped namespaces. +- Keep `using` directives outside the namespace, order `System.*` first, and + remove unused directives. +- Use UTF-8, LF line endings, a trailing newline, and no trailing whitespace. + +## 3. Formatting + +- Indent C# with four spaces and never tabs. +- Use Allman braces. +- Brace multi-line blocks. Terse, same-line guards may remain unbraced. +- Keep one statement and one declaration per line. +- Keep lines reasonably short, generally 100-120 columns. +- Use expression-bodied members for readable one-liners. + +## 4. Naming + +| Element | Convention | Example | +|---|---|---| +| Namespace, type, method, property, event, enum member | `PascalCase` | `S57Document`, `TryParse` | +| Interface | `PascalCase` prefixed with `I` | `IRecordReader` | +| Type parameter | `PascalCase` prefixed with `T` | `TRecord`, `TKey` | +| Local variable, parameter | `camelCase` | `recordLength`, `field` | +| Private/internal instance field | `_camelCase` | `_recordReader` | +| Constant, `static readonly` | `PascalCase` | `FieldTerminator` | +| Async method | `PascalCase` suffixed `Async` | `ReadCatalogAsync` | + +Do not prefix member access with `this.`. Prefer descriptive names, while +retaining well-known S-57, S-52, and ISO/IEC 8211 domain abbreviations. + +## 5. Language features and idioms + +- Prefer `var` for locals unless an explicit type materially improves clarity. +- Prefer target-typed `new`, collection expressions, and range/index operators. +- Prefer pattern matching and switch expressions where they read clearly. +- Prefer string interpolation and use an explicit `StringComparison` where + culture affects semantics. +- Use `is null` and `is not null` for reference equality checks. + +## 6. Nullability and argument validation + +- Do not use the null-forgiving operator merely to silence the analyzer. +- Validate public entry-point arguments with the standard + `ArgumentException` helpers. +- Use `Parse`/`TryParse` pairs for parsing and throw the most specific standard + exception with a useful message. + +## 7. Documentation comments + +- Public and protected APIs require XML documentation, including parameters, + return values, and exceptions where applicable. +- Use `` and `...` for references and inline code. +- Comments explain non-obvious rationale or standards-derived invariants; they + do not narrate self-explanatory implementation. + +## 8. Standards-derived code + +For constants, enums, attributes, field tags, subfields, and encodings derived +from IHO S-57, IHO S-52, or ISO/IEC 8211, cite the relevant standard and section +in XML documentation when practical. + +## 9. Async + +- Suffix async methods with `Async` and return `Task`, `Task`, or + `ValueTask`. +- Accept and honor `CancellationToken` for asynchronous I/O APIs. +- Do not expose `async void` except for event handlers. +- Avoid `.Result`, `.Wait()`, and `.GetAwaiter().GetResult()`. + +## 10. Dependencies and build + +- Manage NuGet versions centrally in `Directory.Packages.props`. +- Do not use `#pragma warning disable` or `` without a specific, + documented justification. +- Keep code cross-platform and gate platform-specific APIs appropriately. + +Before pushing, apply and verify the CI-enforced subsets: + +```bash +dotnet format whitespace EncDotNet.slnx +dotnet format style EncDotNet.slnx --diagnostics IDE0005 +dotnet format whitespace EncDotNet.slnx --verify-no-changes +dotnet format style EncDotNet.slnx --diagnostics IDE0005 --verify-no-changes +``` diff --git a/docs/index.md b/docs/index.md index 022a68d..0c34415 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,3 +7,4 @@ Conceptual guides and reference material for EncDotNet. - [Getting Started](getting-started.md) — Install the libraries and learn the reader APIs - [Architecture](architecture.md) — How ENC standards map to the library layers - [S-57 Glossary](glossary.md) — Key abbreviations and terminology from the IHO S-57 and ISO 8211 standards +- [C# Coding Style](coding-style.md) — Normative style and formatting rules for contributors diff --git a/docs/toc.yml b/docs/toc.yml index 6571706..30c1a3a 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -7,3 +7,5 @@ href: architecture.md - name: S-57 Glossary href: glossary.md + - name: C# Coding Style + href: coding-style.md diff --git a/src/EncDotNet.ChartViewer/App.axaml.cs b/src/EncDotNet.ChartViewer/App.axaml.cs index 1aac9b3..62b4e98 100644 --- a/src/EncDotNet.ChartViewer/App.axaml.cs +++ b/src/EncDotNet.ChartViewer/App.axaml.cs @@ -1,4 +1,3 @@ -using System; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -90,4 +89,4 @@ private static void ConfigureServices(IServiceCollection services) services.AddTransient(); services.AddTransient(); } -} \ No newline at end of file +} diff --git a/src/EncDotNet.ChartViewer/Baking/ChartBaker.cs b/src/EncDotNet.ChartViewer/Baking/ChartBaker.cs index f48bafa..cfb3eb6 100644 --- a/src/EncDotNet.ChartViewer/Baking/ChartBaker.cs +++ b/src/EncDotNet.ChartViewer/Baking/ChartBaker.cs @@ -1,7 +1,4 @@ using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; using EncDotNet.S57; using MessagePack; using MessagePack.Resolvers; diff --git a/src/EncDotNet.ChartViewer/ChartViewerDiagnostics.cs b/src/EncDotNet.ChartViewer/ChartViewerDiagnostics.cs index ca9c10e..fc3c588 100644 --- a/src/EncDotNet.ChartViewer/ChartViewerDiagnostics.cs +++ b/src/EncDotNet.ChartViewer/ChartViewerDiagnostics.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.Metrics; using System.Reflection; diff --git a/src/EncDotNet.ChartViewer/Charts/AppDataPaths.cs b/src/EncDotNet.ChartViewer/Charts/AppDataPaths.cs index 5e6905c..11afb26 100644 --- a/src/EncDotNet.ChartViewer/Charts/AppDataPaths.cs +++ b/src/EncDotNet.ChartViewer/Charts/AppDataPaths.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.Text.Json; using System.Text.Json.Serialization.Metadata; using EncDotNet.ChartViewer.Models; diff --git a/src/EncDotNet.ChartViewer/Charts/CachedChartSource.cs b/src/EncDotNet.ChartViewer/Charts/CachedChartSource.cs index 6c291ad..d1b7778 100644 --- a/src/EncDotNet.ChartViewer/Charts/CachedChartSource.cs +++ b/src/EncDotNet.ChartViewer/Charts/CachedChartSource.cs @@ -1,6 +1,4 @@ using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; using EncDotNet.ChartViewer.Models; using EncDotNet.S57.Charts; diff --git a/src/EncDotNet.ChartViewer/Charts/FileSystemChartCatalogSource.cs b/src/EncDotNet.ChartViewer/Charts/FileSystemChartCatalogSource.cs index ffdc70e..be253a2 100644 --- a/src/EncDotNet.ChartViewer/Charts/FileSystemChartCatalogSource.cs +++ b/src/EncDotNet.ChartViewer/Charts/FileSystemChartCatalogSource.cs @@ -1,11 +1,7 @@ -using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization.Metadata; -using System.Threading; -using System.Threading.Tasks; using EncDotNet.ChartViewer.Baking; using EncDotNet.ChartViewer.Models; using EncDotNet.S57.Charts; diff --git a/src/EncDotNet.ChartViewer/Charts/IChartCatalogSource.cs b/src/EncDotNet.ChartViewer/Charts/IChartCatalogSource.cs index 399608a..22dc5d7 100644 --- a/src/EncDotNet.ChartViewer/Charts/IChartCatalogSource.cs +++ b/src/EncDotNet.ChartViewer/Charts/IChartCatalogSource.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Threading; using EncDotNet.ChartViewer.Models; namespace EncDotNet.ChartViewer.Charts; diff --git a/src/EncDotNet.ChartViewer/Charts/IChartPackageManager.cs b/src/EncDotNet.ChartViewer/Charts/IChartPackageManager.cs index 6bbd69f..355ed54 100644 --- a/src/EncDotNet.ChartViewer/Charts/IChartPackageManager.cs +++ b/src/EncDotNet.ChartViewer/Charts/IChartPackageManager.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - namespace EncDotNet.ChartViewer.Charts; public sealed record ChartPackage diff --git a/src/EncDotNet.ChartViewer/Charts/IChartSource.cs b/src/EncDotNet.ChartViewer/Charts/IChartSource.cs index 286f3fa..ab17edd 100644 --- a/src/EncDotNet.ChartViewer/Charts/IChartSource.cs +++ b/src/EncDotNet.ChartViewer/Charts/IChartSource.cs @@ -1,5 +1,3 @@ -using System.Threading; -using System.Threading.Tasks; using EncDotNet.ChartViewer.Models; using EncDotNet.S57.Charts; diff --git a/src/EncDotNet.ChartViewer/Charts/NoaaChartPackageManager.cs b/src/EncDotNet.ChartViewer/Charts/NoaaChartPackageManager.cs index 14fb2c8..918b5b7 100644 --- a/src/EncDotNet.ChartViewer/Charts/NoaaChartPackageManager.cs +++ b/src/EncDotNet.ChartViewer/Charts/NoaaChartPackageManager.cs @@ -1,17 +1,10 @@ -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; -using System.Linq; -using System.Net.Http; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization.Metadata; -using System.Threading; -using System.Threading.Tasks; using EncDotNet.ChartViewer.Models; -using EncDotNet.S57.ExchangeSets; using EncDotNet.Noaa; +using EncDotNet.S57.ExchangeSets; namespace EncDotNet.ChartViewer.Charts; diff --git a/src/EncDotNet.ChartViewer/DiagnosticOptions.cs b/src/EncDotNet.ChartViewer/DiagnosticOptions.cs index 192e165..ad70f0b 100644 --- a/src/EncDotNet.ChartViewer/DiagnosticOptions.cs +++ b/src/EncDotNet.ChartViewer/DiagnosticOptions.cs @@ -1,4 +1,3 @@ -using System; using System.Globalization; namespace EncDotNet.ChartViewer; diff --git a/src/EncDotNet.ChartViewer/Models/LayerUpdate.cs b/src/EncDotNet.ChartViewer/Models/LayerUpdate.cs index 09bba66..752dde6 100644 --- a/src/EncDotNet.ChartViewer/Models/LayerUpdate.cs +++ b/src/EncDotNet.ChartViewer/Models/LayerUpdate.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Mapsui.Layers; namespace EncDotNet.ChartViewer.Models; diff --git a/src/EncDotNet.ChartViewer/Models/S57FeatureCategory.cs b/src/EncDotNet.ChartViewer/Models/S57FeatureCategory.cs index 4ab9a16..bff2ff0 100644 --- a/src/EncDotNet.ChartViewer/Models/S57FeatureCategory.cs +++ b/src/EncDotNet.ChartViewer/Models/S57FeatureCategory.cs @@ -21,7 +21,7 @@ private S57FeatureCategory(string name, bool defaultEnabled, params S57ObjectCod { Name = name; DefaultEnabled = defaultEnabled; - ObjectCodes = [..objectCodes]; + ObjectCodes = [.. objectCodes]; } // S-57 object class codes diff --git a/src/EncDotNet.ChartViewer/Program.cs b/src/EncDotNet.ChartViewer/Program.cs index 3d93a39..6930a7b 100644 --- a/src/EncDotNet.ChartViewer/Program.cs +++ b/src/EncDotNet.ChartViewer/Program.cs @@ -1,10 +1,9 @@ -using Avalonia; +using Avalonia; using ReactiveUI.Avalonia; -using System; namespace EncDotNet.ChartViewer; -sealed class Program +internal sealed class Program { [STAThread] public static void Main(string[] args) @@ -19,4 +18,4 @@ public static AppBuilder BuildAvaloniaApp() .WithInterFont() .LogToTrace() .UseReactiveUI(); -} \ No newline at end of file +} diff --git a/src/EncDotNet.ChartViewer/ProjectedEdgeCache.cs b/src/EncDotNet.ChartViewer/ProjectedEdgeCache.cs index 8f92a8f..df9fb8a 100644 --- a/src/EncDotNet.ChartViewer/ProjectedEdgeCache.cs +++ b/src/EncDotNet.ChartViewer/ProjectedEdgeCache.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.CompilerServices; using EncDotNet.S57; using EncDotNet.S57.Charts; diff --git a/src/EncDotNet.ChartViewer/S57AreaGeometryBuilder.cs b/src/EncDotNet.ChartViewer/S57AreaGeometryBuilder.cs index 38b1902..b0c796c 100644 --- a/src/EncDotNet.ChartViewer/S57AreaGeometryBuilder.cs +++ b/src/EncDotNet.ChartViewer/S57AreaGeometryBuilder.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using EncDotNet.S57; using EncDotNet.S57.Charts; using NetTopologySuite.Geometries; diff --git a/src/EncDotNet.ChartViewer/S57CoverageHelper.cs b/src/EncDotNet.ChartViewer/S57CoverageHelper.cs index d5f8ae2..851916a 100644 --- a/src/EncDotNet.ChartViewer/S57CoverageHelper.cs +++ b/src/EncDotNet.ChartViewer/S57CoverageHelper.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using EncDotNet.S57.Charts; using NetTopologySuite.Geometries; using NetTopologySuite.Geometries.Prepared; diff --git a/src/EncDotNet.ChartViewer/S57LayerFactory.cs b/src/EncDotNet.ChartViewer/S57LayerFactory.cs index 101582c..428e173 100644 --- a/src/EncDotNet.ChartViewer/S57LayerFactory.cs +++ b/src/EncDotNet.ChartViewer/S57LayerFactory.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.Metrics; -using EncDotNet.S57; -using EncDotNet.S57.Charts; using EncDotNet.ChartViewer.Models; using EncDotNet.ChartViewer.ViewModels; +using EncDotNet.S57; +using EncDotNet.S57.Charts; using Mapsui; using Mapsui.Layers; @@ -195,14 +192,14 @@ internal static void RecalculateMinVisible(IReadOnlyCollection l // simplified geometry could cause visual artifacts. Three levels balances coverage // completeness against overscale artifact risk; fewer levels can leave gaps where // no chart covers the viewport. - const int overlapLevels = 3; + const int OverlapLevels = 3; var scales = new List>(scaleMaxVisible); var scaleMinVisible = new Dictionary(); for (int i = 0; i < scales.Count; i++) { - // The finest N scales (i < overlapLevels) keep MinVisible = 0. + // The finest N scales (i < OverlapLevels) keep MinVisible = 0. // Coarser scales reference the chart N levels finer. - double minVis = i >= overlapLevels ? scales[i - overlapLevels].Value : 0; + double minVis = i >= OverlapLevels ? scales[i - OverlapLevels].Value : 0; scaleMinVisible[scales[i].Key] = minVis; } diff --git a/src/EncDotNet.ChartViewer/S57LayerTemplate.cs b/src/EncDotNet.ChartViewer/S57LayerTemplate.cs index 3291cd2..4a8ee42 100644 --- a/src/EncDotNet.ChartViewer/S57LayerTemplate.cs +++ b/src/EncDotNet.ChartViewer/S57LayerTemplate.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; using System.Globalization; -using EncDotNet.S57.Charts; using EncDotNet.ChartViewer.Models; +using EncDotNet.S57.Charts; using Mapsui; using Mapsui.Nts; using Mapsui.Projections; diff --git a/src/EncDotNet.ChartViewer/S57LayerTemplates.cs b/src/EncDotNet.ChartViewer/S57LayerTemplates.cs index 7f4b5f4..e078d72 100644 --- a/src/EncDotNet.ChartViewer/S57LayerTemplates.cs +++ b/src/EncDotNet.ChartViewer/S57LayerTemplates.cs @@ -1,9 +1,8 @@ using System.Collections.Frozen; -using System.Collections.Generic; using System.Globalization; +using EncDotNet.ChartViewer.Models; using EncDotNet.S57; using EncDotNet.S57.Charts; -using EncDotNet.ChartViewer.Models; using Mapsui; using Mapsui.Nts; using Mapsui.Projections; @@ -423,9 +422,9 @@ private static IEnumerable CreateDepcntFeatures(S57Chart chart, S57Lin // Default depth zone boundaries (metres). S-52 allows the mariner to // adjust these; the values below are the S-52 recommended defaults. private const double DepthDryingLine = 0.0; // boundary between DEPIT and DEPVS - private const double DepthShallow = 2.0; // boundary between DEPVS and DEPMS - private const double DepthSafety = 5.0; // boundary between DEPMS and DEPMD (default safety contour) - private const double DepthDeep = 30.0; // boundary between DEPMD and DEPDW + private const double DepthShallow = 2.0; // boundary between DEPVS and DEPMS + private const double DepthSafety = 5.0; // boundary between DEPMS and DEPMD (default safety contour) + private const double DepthDeep = 30.0; // boundary between DEPMD and DEPDW /// /// Returns the colour and line width for a depth contour feature. @@ -560,11 +559,11 @@ private static string FormatFathoms(double totalFeet) int fathoms = wholeFeet / 6; int remainingFeet = wholeFeet % 6; - const string subscriptDigits = "₀₁₂₃₄₅"; + const string SubscriptDigits = "₀₁₂₃₄₅"; return remainingFeet == 0 ? fathoms.ToString() - : $"{fathoms}{subscriptDigits[remainingFeet]}"; + : $"{fathoms}{SubscriptDigits[remainingFeet]}"; } // --- Buoy icon selection --- diff --git a/src/EncDotNet.ChartViewer/S57LineGeometryBuilder.cs b/src/EncDotNet.ChartViewer/S57LineGeometryBuilder.cs index 6806f35..22fbdb4 100644 --- a/src/EncDotNet.ChartViewer/S57LineGeometryBuilder.cs +++ b/src/EncDotNet.ChartViewer/S57LineGeometryBuilder.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Linq; using EncDotNet.S57; using EncDotNet.S57.Charts; using NetTopologySuite.Geometries; diff --git a/src/EncDotNet.ChartViewer/ViewLocator.cs b/src/EncDotNet.ChartViewer/ViewLocator.cs index a1a5a97..fe0f4db 100644 --- a/src/EncDotNet.ChartViewer/ViewLocator.cs +++ b/src/EncDotNet.ChartViewer/ViewLocator.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Controls; using Avalonia.Controls.Templates; @@ -34,4 +33,4 @@ public bool Match(object? data) { return data is ViewModelBase; } -} \ No newline at end of file +} diff --git a/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureItemViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureItemViewModel.cs index c9b0e81..7b4bb5e 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureItemViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureItemViewModel.cs @@ -1,4 +1,3 @@ -using System; using EncDotNet.S57; using ReactiveUI; diff --git a/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureViewModel.cs index b177c73..ffdb10e 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/ChartFeatureViewModel.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Immutable; -using System.Linq; using EncDotNet.ChartViewer.Models; using EncDotNet.S57; using ReactiveUI; diff --git a/src/EncDotNet.ChartViewer/ViewModels/ChartViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/ChartViewModel.cs index d02a1ec..13298a7 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/ChartViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/ChartViewModel.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.Metrics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Input; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; diff --git a/src/EncDotNet.ChartViewer/ViewModels/MainWindowViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/MainWindowViewModel.cs index 2e67be1..a968606 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/MainWindowViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/MainWindowViewModel.cs @@ -1,14 +1,9 @@ -using System; using System.Collections.ObjectModel; -using System.Reactive; -using System.Threading; -using System.Threading.Tasks; using System.Windows.Input; using EncDotNet.ChartViewer.Charts; using EncDotNet.ChartViewer.Models; using EncDotNet.S57.Charts; using ReactiveUI; -using System.Linq; namespace EncDotNet.ChartViewer.ViewModels; @@ -222,4 +217,4 @@ private void ApplyChartFilter() } } } -} \ No newline at end of file +} diff --git a/src/EncDotNet.ChartViewer/ViewModels/ManageChartsViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/ManageChartsViewModel.cs index 650abc1..8249707 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/ManageChartsViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/ManageChartsViewModel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using System.Windows.Input; using EncDotNet.ChartViewer.Charts; using ReactiveUI; diff --git a/src/EncDotNet.ChartViewer/ViewModels/SetupWizardViewModel.cs b/src/EncDotNet.ChartViewer/ViewModels/SetupWizardViewModel.cs index e4f7c4f..1ce7aaa 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/SetupWizardViewModel.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/SetupWizardViewModel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using System.Windows.Input; using EncDotNet.ChartViewer.Charts; using ReactiveUI; diff --git a/src/EncDotNet.ChartViewer/ViewModels/ViewModelBase.cs b/src/EncDotNet.ChartViewer/ViewModels/ViewModelBase.cs index bc493b8..8d0756a 100644 --- a/src/EncDotNet.ChartViewer/ViewModels/ViewModelBase.cs +++ b/src/EncDotNet.ChartViewer/ViewModels/ViewModelBase.cs @@ -1,7 +1,7 @@ -using ReactiveUI; +using ReactiveUI; namespace EncDotNet.ChartViewer.ViewModels; public abstract class ViewModelBase : ReactiveObject { -} \ No newline at end of file +} diff --git a/src/EncDotNet.ChartViewer/Views/FeatureInfoWindow.axaml.cs b/src/EncDotNet.ChartViewer/Views/FeatureInfoWindow.axaml.cs index 3d9421d..5de9748 100644 --- a/src/EncDotNet.ChartViewer/Views/FeatureInfoWindow.axaml.cs +++ b/src/EncDotNet.ChartViewer/Views/FeatureInfoWindow.axaml.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using System.Text; using Avalonia.Controls; -using Avalonia.Input; using Avalonia.Interactivity; using EncDotNet.S57; diff --git a/src/EncDotNet.ChartViewer/Views/MainWindow.axaml.cs b/src/EncDotNet.ChartViewer/Views/MainWindow.axaml.cs index 15f315c..67cf364 100644 --- a/src/EncDotNet.ChartViewer/Views/MainWindow.axaml.cs +++ b/src/EncDotNet.ChartViewer/Views/MainWindow.axaml.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; -using System.IO; -using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; @@ -17,18 +12,17 @@ using EncDotNet.ChartViewer.ViewModels; using EncDotNet.S57; using Mapsui; -using Microsoft.Extensions.DependencyInjection; using Mapsui.Extensions; using Mapsui.Layers; -using System.Collections.Immutable; using Mapsui.Manipulations; using Mapsui.Nts; using Mapsui.Projections; using Mapsui.Styles; using Mapsui.Styles.Thematics; using Mapsui.Tiling; -using NetTopologySuite.Geometries; using Mapsui.Widgets.ScaleBar; +using Microsoft.Extensions.DependencyInjection; +using NetTopologySuite.Geometries; namespace EncDotNet.ChartViewer.Views; diff --git a/src/EncDotNet.ChartViewer/Views/ManageChartsWindow.axaml.cs b/src/EncDotNet.ChartViewer/Views/ManageChartsWindow.axaml.cs index 42d259c..b81731a 100644 --- a/src/EncDotNet.ChartViewer/Views/ManageChartsWindow.axaml.cs +++ b/src/EncDotNet.ChartViewer/Views/ManageChartsWindow.axaml.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Controls; using EncDotNet.ChartViewer.ViewModels; diff --git a/src/EncDotNet.ChartViewer/Views/SetupWizardWindow.axaml.cs b/src/EncDotNet.ChartViewer/Views/SetupWizardWindow.axaml.cs index 2bec5bb..0f6c4cf 100644 --- a/src/EncDotNet.ChartViewer/Views/SetupWizardWindow.axaml.cs +++ b/src/EncDotNet.ChartViewer/Views/SetupWizardWindow.axaml.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Controls; using EncDotNet.ChartViewer.ViewModels; diff --git a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecord.cs b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecord.cs index 87c4f7b..8f4666b 100644 --- a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecord.cs +++ b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecord.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.Iso8211; /// diff --git a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs index 78966f6..0481042 100644 --- a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs +++ b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Collections.Immutable; -using System.Text; namespace EncDotNet.Iso8211; @@ -40,7 +39,7 @@ public static class Iso8211DataDescriptiveRecordReader /// Since every DDR field shares the same internal layout for a given field control length, /// we avoid repeated allocations by caching the definition. /// - private static readonly ConcurrentDictionary s_ddrFieldDefinitionCache = new(); + private static readonly ConcurrentDictionary DdrFieldDefinitionCache = new(); /// /// Parses an that represents a DDR and returns a @@ -108,7 +107,7 @@ public static Iso8211DataDescriptiveRecord Read(Iso8211Record record) /// An describing the DDR's own field structure. public static Iso8211FieldDefinition GetDdrFieldDefinition(int fieldControlLength) { - return s_ddrFieldDefinitionCache.GetOrAdd(fieldControlLength, static fcl => + return DdrFieldDefinitionCache.GetOrAdd(fieldControlLength, static fcl => { var subfieldDefinitions = CreateDdrSubfieldDefinitions(fcl); diff --git a/src/EncDotNet.Iso8211/Iso8211Document.cs b/src/EncDotNet.Iso8211/Iso8211Document.cs index 070416c..337070b 100644 --- a/src/EncDotNet.Iso8211/Iso8211Document.cs +++ b/src/EncDotNet.Iso8211/Iso8211Document.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.Iso8211; /// diff --git a/src/EncDotNet.Iso8211/Iso8211DocumentReader.cs b/src/EncDotNet.Iso8211/Iso8211DocumentReader.cs index 6a4606a..e7b4e16 100644 --- a/src/EncDotNet.Iso8211/Iso8211DocumentReader.cs +++ b/src/EncDotNet.Iso8211/Iso8211DocumentReader.cs @@ -1,5 +1,3 @@ -using System.Text; - namespace EncDotNet.Iso8211; /// diff --git a/src/EncDotNet.Iso8211/Iso8211FieldDefinition.cs b/src/EncDotNet.Iso8211/Iso8211FieldDefinition.cs index 2e1d2b1..8d04968 100644 --- a/src/EncDotNet.Iso8211/Iso8211FieldDefinition.cs +++ b/src/EncDotNet.Iso8211/Iso8211FieldDefinition.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.Iso8211; /// diff --git a/src/EncDotNet.Iso8211/Iso8211FieldReader.cs b/src/EncDotNet.Iso8211/Iso8211FieldReader.cs index 496bf7a..ac1a3bd 100644 --- a/src/EncDotNet.Iso8211/Iso8211FieldReader.cs +++ b/src/EncDotNet.Iso8211/Iso8211FieldReader.cs @@ -370,7 +370,7 @@ public IEnumerable GetSubfieldGroups() { break; } - + // For fixed-width subfields in repeating groups, check if we've hit the end: // If the remaining data is just the field terminator, we're done. if (format.IsFixedWidth && _fieldDefinition.HasRepeatingGroup) @@ -688,7 +688,7 @@ private static object ConvertUnsignedBinary(ReadOnlySpan data, int widt { // Use actual data length if shorter than expected width int actualWidth = Math.Min(width, data.Length); - + ulong value = actualWidth switch { 1 => data[0], @@ -753,7 +753,7 @@ private static object ConvertSignedBinary(ReadOnlySpan data, int width) { // Use actual data length if shorter than expected width int actualWidth = Math.Min(width, data.Length); - + long value = actualWidth switch { 1 => (sbyte)data[0], @@ -896,7 +896,7 @@ private static ulong ReadUnsignedLittleEndian(ReadOnlySpan data, int width private static long ReadSignedLittleEndian(ReadOnlySpan data, int width) { var unsigned = ReadUnsignedLittleEndian(data, width); - + // Sign extend if the high bit is set var signBit = 1UL << (width * 8 - 1); if ((unsigned & signBit) != 0) diff --git a/src/EncDotNet.Iso8211/Iso8211Reader.cs b/src/EncDotNet.Iso8211/Iso8211Reader.cs index 7a69d2c..a2406d8 100644 --- a/src/EncDotNet.Iso8211/Iso8211Reader.cs +++ b/src/EncDotNet.Iso8211/Iso8211Reader.cs @@ -358,7 +358,7 @@ public bool TrySkipRecord() // Move past the current record var recordEnd = _fieldAreaOffset + (_leader.RecordLength - _leader.BaseAddressOfFieldArea); - + // Check if we have enough data if (recordEnd > _buffer.Length) { @@ -370,7 +370,7 @@ public bool TrySkipRecord() // Need more data return false; } - + var bytesToSkip = recordEnd - _consumed; if (bytesToSkip > 0) { @@ -498,7 +498,7 @@ private bool TryReadRecordStart() // Parse leader var recordLength = ParseNumeric(leaderSpan, 0, 5); - + // Check if we have the entire record if (_buffer.Length - _consumed < recordLength) { diff --git a/src/EncDotNet.Iso8211/Iso8211Record.cs b/src/EncDotNet.Iso8211/Iso8211Record.cs index 6649467..b88bf4a 100644 --- a/src/EncDotNet.Iso8211/Iso8211Record.cs +++ b/src/EncDotNet.Iso8211/Iso8211Record.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.Iso8211; /// diff --git a/src/EncDotNet.Iso8211/Iso8211SubfieldEncoder.cs b/src/EncDotNet.Iso8211/Iso8211SubfieldEncoder.cs index 60a6ea5..b166c5f 100644 --- a/src/EncDotNet.Iso8211/Iso8211SubfieldEncoder.cs +++ b/src/EncDotNet.Iso8211/Iso8211SubfieldEncoder.cs @@ -151,17 +151,17 @@ private static byte[] EncodeFloatingBinary(object? value, int width) switch (width) { case 4: - { - var bytes = new byte[4]; - BinaryPrimitives.WriteSingleLittleEndian(bytes, (float)d); - return bytes; - } + { + var bytes = new byte[4]; + BinaryPrimitives.WriteSingleLittleEndian(bytes, (float)d); + return bytes; + } case 8: - { - var bytes = new byte[8]; - BinaryPrimitives.WriteDoubleLittleEndian(bytes, d); - return bytes; - } + { + var bytes = new byte[8]; + BinaryPrimitives.WriteDoubleLittleEndian(bytes, d); + return bytes; + } default: throw new InvalidOperationException($"Unsupported floating-point binary width: {width} byte(s)."); } @@ -178,10 +178,10 @@ private static byte[] EncodeBitString(object? value, int width) } return FitToWidth(raw, width, 0); case string hex: - { - var raw = Convert.FromHexString(hex); - return width <= 0 ? raw : FitToWidth(raw, width, 0); - } + { + var raw = Convert.FromHexString(hex); + return width <= 0 ? raw : FitToWidth(raw, width, 0); + } case null: return width <= 0 ? Array.Empty() : new byte[width]; default: diff --git a/src/EncDotNet.Noaa/EncProductCatalogClient.cs b/src/EncDotNet.Noaa/EncProductCatalogClient.cs index 5c0b033..16d677a 100644 --- a/src/EncDotNet.Noaa/EncProductCatalogClient.cs +++ b/src/EncDotNet.Noaa/EncProductCatalogClient.cs @@ -45,8 +45,8 @@ public async Task GetCatalogAsync(string url, CancellationTok /// The deserialized . public async Task GetNoaaCatalogAsync(CancellationToken cancellationToken = default) { - const string noaaUrl = "https://www.charts.noaa.gov/ENCs/ENCProdCat.xml"; - return await GetCatalogAsync(noaaUrl, cancellationToken); + const string NoaaUrl = "https://www.charts.noaa.gov/ENCs/ENCProdCat.xml"; + return await GetCatalogAsync(NoaaUrl, cancellationToken); } /// diff --git a/src/EncDotNet.S57/Charts/S57AreaFeature.cs b/src/EncDotNet.S57/Charts/S57AreaFeature.cs index 74ae463..10e2969 100644 --- a/src/EncDotNet.S57/Charts/S57AreaFeature.cs +++ b/src/EncDotNet.S57/Charts/S57AreaFeature.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Linq; namespace EncDotNet.S57.Charts; diff --git a/src/EncDotNet.S57/Charts/S57Edge.cs b/src/EncDotNet.S57/Charts/S57Edge.cs index 7c0638e..04252c5 100644 --- a/src/EncDotNet.S57/Charts/S57Edge.cs +++ b/src/EncDotNet.S57/Charts/S57Edge.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/Charts/S57EdgeReference.cs b/src/EncDotNet.S57/Charts/S57EdgeReference.cs index 24e2ddd..f6e50b6 100644 --- a/src/EncDotNet.S57/Charts/S57EdgeReference.cs +++ b/src/EncDotNet.S57/Charts/S57EdgeReference.cs @@ -37,6 +37,6 @@ public S57EdgeReference( } /// - public override string ToString() => + public override string ToString() => $"Edge {EdgeName}, Orientation={Orientation}, Usage={Usage}"; } diff --git a/src/EncDotNet.S57/Charts/S57IsolatedNode.cs b/src/EncDotNet.S57/Charts/S57IsolatedNode.cs index d6f13c6..73a64bc 100644 --- a/src/EncDotNet.S57/Charts/S57IsolatedNode.cs +++ b/src/EncDotNet.S57/Charts/S57IsolatedNode.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/Charts/S57LineFeature.cs b/src/EncDotNet.S57/Charts/S57LineFeature.cs index 632f679..d545c39 100644 --- a/src/EncDotNet.S57/Charts/S57LineFeature.cs +++ b/src/EncDotNet.S57/Charts/S57LineFeature.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/Charts/S57PointFeature.cs b/src/EncDotNet.S57/Charts/S57PointFeature.cs index 7f24143..d5cd42a 100644 --- a/src/EncDotNet.S57/Charts/S57PointFeature.cs +++ b/src/EncDotNet.S57/Charts/S57PointFeature.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/Charts/S57SpatialRecord.cs b/src/EncDotNet.S57/Charts/S57SpatialRecord.cs index ec1eb37..f4a96a7 100644 --- a/src/EncDotNet.S57/Charts/S57SpatialRecord.cs +++ b/src/EncDotNet.S57/Charts/S57SpatialRecord.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/Charts/S57TypedFeature.cs b/src/EncDotNet.S57/Charts/S57TypedFeature.cs index 3459231..14c6040 100644 --- a/src/EncDotNet.S57/Charts/S57TypedFeature.cs +++ b/src/EncDotNet.S57/Charts/S57TypedFeature.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.Charts; /// diff --git a/src/EncDotNet.S57/ExchangeSets/S57Catalog.cs b/src/EncDotNet.S57/ExchangeSets/S57Catalog.cs index 706c7d1..3dcc780 100644 --- a/src/EncDotNet.S57/ExchangeSets/S57Catalog.cs +++ b/src/EncDotNet.S57/ExchangeSets/S57Catalog.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57.ExchangeSets; /// diff --git a/src/EncDotNet.S57/S57Document.cs b/src/EncDotNet.S57/S57Document.cs index 53a3cbf..69bb4f3 100644 --- a/src/EncDotNet.S57/S57Document.cs +++ b/src/EncDotNet.S57/S57Document.cs @@ -186,8 +186,8 @@ private static S57FeatureRecord ApplyFeatureModify(S57FeatureRecord baseRecord, UpdateInstruction = updateRecord.UpdateInstruction, Attributes = MergeAttributes(baseRecord.Attributes, updateRecord.Attributes), NationalAttributes = MergeAttributes(baseRecord.NationalAttributes, updateRecord.NationalAttributes), - SpatialPointers = ApplyArrayUpdate([..baseRecord.SpatialPointers], [..updateRecord.SpatialPointers], updateRecord.SpatialPointerControl), - FeaturePointers = ApplyArrayUpdate([..baseRecord.FeaturePointers], [..updateRecord.FeaturePointers], updateRecord.FeaturePointerControl), + SpatialPointers = ApplyArrayUpdate([.. baseRecord.SpatialPointers], [.. updateRecord.SpatialPointers], updateRecord.SpatialPointerControl), + FeaturePointers = ApplyArrayUpdate([.. baseRecord.FeaturePointers], [.. updateRecord.FeaturePointers], updateRecord.FeaturePointerControl), }; } @@ -199,9 +199,9 @@ private static S57VectorRecord ApplyVectorModify(S57VectorRecord baseRecord, S57 RecordVersion = updateRecord.RecordVersion, UpdateInstruction = updateRecord.UpdateInstruction, Attributes = MergeAttributes(baseRecord.Attributes, updateRecord.Attributes), - VectorPointers = ApplyArrayUpdate([..baseRecord.VectorPointers], [..updateRecord.VectorPointers], updateRecord.VectorPointerControl), - Coordinates2D = ApplyArrayUpdate([..baseRecord.Coordinates2D], [..updateRecord.Coordinates2D], updateRecord.CoordinateControl), - Soundings = ApplyArrayUpdate([..baseRecord.Soundings], [..updateRecord.Soundings], updateRecord.CoordinateControl), + VectorPointers = ApplyArrayUpdate([.. baseRecord.VectorPointers], [.. updateRecord.VectorPointers], updateRecord.VectorPointerControl), + Coordinates2D = ApplyArrayUpdate([.. baseRecord.Coordinates2D], [.. updateRecord.Coordinates2D], updateRecord.CoordinateControl), + Soundings = ApplyArrayUpdate([.. baseRecord.Soundings], [.. updateRecord.Soundings], updateRecord.CoordinateControl), }; } diff --git a/src/EncDotNet.S57/S57DocumentReader.cs b/src/EncDotNet.S57/S57DocumentReader.cs index a1f6aef..7c2bb6b 100644 --- a/src/EncDotNet.S57/S57DocumentReader.cs +++ b/src/EncDotNet.S57/S57DocumentReader.cs @@ -456,7 +456,7 @@ private static ImmutableArray ParseAttributes(Iso8211Record r { var attributes = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(fieldTag); - + foreach (var field in record.GetFieldsByTag(fieldTag)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -466,7 +466,7 @@ private static ImmutableArray ParseAttributes(Iso8211Record r // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data, lexicalLevel); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -495,7 +495,7 @@ private static ImmutableArray ParseSpatialPointers(Iso8211Rec { var pointers = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(S57FieldTags.FSPT); - + foreach (var field in record.GetFieldsByTag(S57FieldTags.FSPT)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -505,7 +505,7 @@ private static ImmutableArray ParseSpatialPointers(Iso8211Rec // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -514,7 +514,7 @@ private static ImmutableArray ParseSpatialPointers(Iso8211Rec // Read it as raw bytes and decompose var nameBytes = group.GetSubfieldBytes(S57SubfieldNames.NAME); var name = DecomposeNameField(nameBytes); - + var ornt = group.GetSubfield(S57SubfieldNames.ORNT); var usag = group.GetSubfield(S57SubfieldNames.USAG); var mask = group.GetSubfield(S57SubfieldNames.MASK); @@ -545,7 +545,7 @@ private static ImmutableArray ParseFeaturePointers(Iso8211Rec { var pointers = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(S57FieldTags.FFPT); - + foreach (var field in record.GetFieldsByTag(S57FieldTags.FFPT)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -555,7 +555,7 @@ private static ImmutableArray ParseFeaturePointers(Iso8211Rec // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -563,7 +563,7 @@ private static ImmutableArray ParseFeaturePointers(Iso8211Rec // LNAM is a composite subfield (8 bytes: AGEN(2) + FIDN(4) + FIDS(2)) var lnamBytes = group.GetSubfieldBytes(S57SubfieldNames.LNAM); var lnam = DecomposeLongNameField(lnamBytes); - + var rind = group.GetSubfield(S57SubfieldNames.RIND); var comt = group.GetSubfield(S57SubfieldNames.COMT); @@ -592,7 +592,7 @@ private static ImmutableArray ParseVectorPointers(Iso8211Recor { var pointers = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(S57FieldTags.VRPT); - + foreach (var field in record.GetFieldsByTag(S57FieldTags.VRPT)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -602,7 +602,7 @@ private static ImmutableArray ParseVectorPointers(Iso8211Recor // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -610,7 +610,7 @@ private static ImmutableArray ParseVectorPointers(Iso8211Recor // NAME is a composite subfield (5 bytes: RCNM(1) + RCID(4)) var nameBytes = group.GetSubfieldBytes(S57SubfieldNames.NAME); var name = DecomposeNameField(nameBytes); - + var ornt = group.GetSubfield(S57SubfieldNames.ORNT); var usag = group.GetSubfield(S57SubfieldNames.USAG); var topi = group.GetSubfield(S57SubfieldNames.TOPI); @@ -643,7 +643,7 @@ private static ImmutableArray ParseCoordinates2D(Iso8211Record { var coordinates = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(S57FieldTags.SG2D); - + foreach (var field in record.GetFieldsByTag(S57FieldTags.SG2D)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -653,7 +653,7 @@ private static ImmutableArray ParseCoordinates2D(Iso8211Record // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -685,7 +685,7 @@ private static ImmutableArray ParseSoundings(Iso8211Record record, { var soundings = ImmutableArray.CreateBuilder(); var fieldDef = ddr?.GetFieldDefinition(S57FieldTags.SG3D); - + foreach (var field in record.GetFieldsByTag(S57FieldTags.SG3D)) { if (fieldDef == null || !fieldDef.HasRepeatingGroup) @@ -695,7 +695,7 @@ private static ImmutableArray ParseSoundings(Iso8211Record record, // Use DDR-based field reader with repeating groups var reader = new Iso8211FieldReader(fieldDef, field.Data); - + foreach (var group in reader.GetSubfieldGroups()) { try @@ -731,7 +731,7 @@ private static S57RecordName DecomposeNameField(ReadOnlySpan data) { return default; } - + var rcnm = data[0]; var rcid = BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(1, 4)); return S57RecordName.FromRcnmRcid(rcnm, (int)rcid); @@ -746,7 +746,7 @@ private static S57RecordName DecomposeLongNameField(ReadOnlySpan data) { return default; } - + var agen = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(0, 2)); var fidn = BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(2, 4)); var fids = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)); diff --git a/src/EncDotNet.S57/S57FeatureRecord.cs b/src/EncDotNet.S57/S57FeatureRecord.cs index d1a16ad..99d7628 100644 --- a/src/EncDotNet.S57/S57FeatureRecord.cs +++ b/src/EncDotNet.S57/S57FeatureRecord.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57; /// diff --git a/src/EncDotNet.S57/S57VectorRecord.cs b/src/EncDotNet.S57/S57VectorRecord.cs index 05c7d7b..29fb0ad 100644 --- a/src/EncDotNet.S57/S57VectorRecord.cs +++ b/src/EncDotNet.S57/S57VectorRecord.cs @@ -1,5 +1,3 @@ -using System.Collections.Immutable; - namespace EncDotNet.S57; /// diff --git a/tests/EncDotNet.Benchmarks/ParsingLayerBenchmarks.cs b/tests/EncDotNet.Benchmarks/ParsingLayerBenchmarks.cs index 4da3db0..1560d0c 100644 --- a/tests/EncDotNet.Benchmarks/ParsingLayerBenchmarks.cs +++ b/tests/EncDotNet.Benchmarks/ParsingLayerBenchmarks.cs @@ -1,6 +1,6 @@ using BenchmarkDotNet.Attributes; -using EncDotNet.S57; using EncDotNet.Iso8211; +using EncDotNet.S57; namespace EncDotNet.Benchmarks; diff --git a/tests/EncDotNet.Benchmarks/Program.cs b/tests/EncDotNet.Benchmarks/Program.cs index 6017b3f..1a73212 100644 --- a/tests/EncDotNet.Benchmarks/Program.cs +++ b/tests/EncDotNet.Benchmarks/Program.cs @@ -1,4 +1,3 @@ -using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; namespace EncDotNet.Benchmarks; diff --git a/tests/EndDotNet.UnitTests/Iso8211DocumentReaderTests.cs b/tests/EndDotNet.UnitTests/Iso8211DocumentReaderTests.cs index 1214e32..77862e0 100644 --- a/tests/EndDotNet.UnitTests/Iso8211DocumentReaderTests.cs +++ b/tests/EndDotNet.UnitTests/Iso8211DocumentReaderTests.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Text; using EncDotNet.Iso8211; @@ -82,7 +81,7 @@ private static byte[] CreateMinimalRecord(bool isDataDescriptiveRecord = true) private static byte[] CreateMultiFieldRecord(bool isDataDescriptiveRecord = false) { var leaderIdentifier = isDataDescriptiveRecord ? 'L' : 'D'; - + // Fields: 0001 -> "HELLO", 0002 -> "WORLD" var field1Tag = "0001"; var field1Data = "HELLO"u8.ToArray(); diff --git a/tests/EndDotNet.UnitTests/Iso8211FieldReaderTests.cs b/tests/EndDotNet.UnitTests/Iso8211FieldReaderTests.cs index e4e7eb6..300fe43 100644 --- a/tests/EndDotNet.UnitTests/Iso8211FieldReaderTests.cs +++ b/tests/EndDotNet.UnitTests/Iso8211FieldReaderTests.cs @@ -351,7 +351,7 @@ public void GetSubfield_VariableLengthString_StopsAtUnitTerminator() var fieldDef = CreateFieldDefinition("TEST", ("DSNM", Iso8211SubfieldFormatType.CharacterData, 0, false), ("EDTN", Iso8211SubfieldFormatType.CharacterData, 0, false)); - + // Use \u001F instead of \x1F to avoid hex digit consumption (\x1FE would be wrong) var data = Encoding.ASCII.GetBytes("FILE1\u001FEDITION1\u001E"); var reader = new Iso8211FieldReader(fieldDef, data); diff --git a/tests/EndDotNet.UnitTests/S57AreaGeometryBuilderTests.cs b/tests/EndDotNet.UnitTests/S57AreaGeometryBuilderTests.cs index 27c2330..b5be2d4 100644 --- a/tests/EndDotNet.UnitTests/S57AreaGeometryBuilderTests.cs +++ b/tests/EndDotNet.UnitTests/S57AreaGeometryBuilderTests.cs @@ -73,12 +73,12 @@ private static S57VectorPointer FaceEdgePointer( int edgeId, S57Orientation orientation, S57UsageIndicator usage) => new() - { - Name = EdgeName(edgeId), - Orientation = orientation, - Usage = usage, - Mask = S57MaskingIndicator.Show - }; + { + Name = EdgeName(edgeId), + Orientation = orientation, + Usage = usage, + Mask = S57MaskingIndicator.Show + }; private static S57SpatialPointer ExteriorEdgeRef(int edgeId, S57Orientation orientation = S57Orientation.Forward) => new() { diff --git a/tests/EndDotNet.UnitTests/S57ChartDirectoryTests.cs b/tests/EndDotNet.UnitTests/S57ChartDirectoryTests.cs index 120bd08..acb1be0 100644 --- a/tests/EndDotNet.UnitTests/S57ChartDirectoryTests.cs +++ b/tests/EndDotNet.UnitTests/S57ChartDirectoryTests.cs @@ -1,7 +1,6 @@ using System.Collections.Immutable; using System.Text; using EncDotNet.S57; -using EncDotNet.S57.Charts; using EncDotNet.S57.ExchangeSets; namespace EndDotNet.UnitTests; diff --git a/tests/EndDotNet.UnitTests/S57ChartTests.cs b/tests/EndDotNet.UnitTests/S57ChartTests.cs index bca4e9d..414e0cb 100644 --- a/tests/EndDotNet.UnitTests/S57ChartTests.cs +++ b/tests/EndDotNet.UnitTests/S57ChartTests.cs @@ -43,8 +43,8 @@ private static S57VectorRecord CreateIsolatedNodeRecord( RecordName = S57RecordName.FromRcnmRcid(S57RecordNameCodes.IsolatedNode, rcid), RecordVersion = 1, UpdateInstruction = S57UpdateInstruction.Insert, - Coordinates2D = position.HasValue - ? ImmutableArray.Create(position.Value) + Coordinates2D = position.HasValue + ? ImmutableArray.Create(position.Value) : ImmutableArray.Empty, Soundings = soundings?.ToImmutableArray() ?? ImmutableArray.Empty, Attributes = attributes?.ToImmutableArray() ?? ImmutableArray.Empty, @@ -481,7 +481,7 @@ public void ConnectedNode_MultiplNodes_IndexedByRecordName() // Assert Assert.Equal(3, chart.ConnectedNodes.Count); - + var retrieved1 = chart.GetConnectedNode(S57RecordName.FromRcnmRcid(S57RecordNameCodes.ConnectedNode, 1)); Assert.NotNull(retrieved1); Assert.Equal(100, retrieved1.Position.X); @@ -600,10 +600,10 @@ public void Face_WithExteriorBoundary_HasCorrectEdgeReferences() var face = chart.Faces.Values.First(); Assert.True(face.HasExteriorBoundary); Assert.Equal(3, face.ExteriorBoundary.Count); - + Assert.Equal(1, face.ExteriorBoundary[0].EdgeName.RecordId); Assert.Equal(S57Orientation.Forward, face.ExteriorBoundary[0].Orientation); - + Assert.Equal(2, face.ExteriorBoundary[1].EdgeName.RecordId); Assert.Equal(S57Orientation.Reverse, face.ExteriorBoundary[1].Orientation); } @@ -735,12 +735,12 @@ public void PointFeature_WithAttributes_PreservesAttributesAndHasHelperMethods() var pointFeature = chart.PointFeatures[0]; Assert.True(pointFeature.HasAttributes); Assert.Equal(3, pointFeature.Attributes.Count); - + // Single value lookup Assert.Equal("RED", pointFeature.GetAttributeValue(116)); Assert.Equal("1", pointFeature.GetAttributeValue(117)); Assert.Null(pointFeature.GetAttributeValue(999)); - + // Multiple value lookup var values = pointFeature.GetAttributeValues(116).ToList(); Assert.Equal(2, values.Count); diff --git a/tests/EndDotNet.UnitTests/S57DocumentReaderTests.cs b/tests/EndDotNet.UnitTests/S57DocumentReaderTests.cs index 723a0c1..78061a5 100644 --- a/tests/EndDotNet.UnitTests/S57DocumentReaderTests.cs +++ b/tests/EndDotNet.UnitTests/S57DocumentReaderTests.cs @@ -20,27 +20,27 @@ private static byte[] CreateS57Document(params byte[][] dataRecords) { // Create DDR (Data Descriptive Record) with proper S-57 field definitions var ddr = CreateS57Ddr(); - + // Calculate total size var totalSize = ddr.Length; foreach (var record in dataRecords) { totalSize += record.Length; } - + // Combine all records var result = new byte[totalSize]; var offset = 0; - + Array.Copy(ddr, 0, result, offset, ddr.Length); offset += ddr.Length; - + foreach (var record in dataRecords) { Array.Copy(record, 0, result, offset, record.Length); offset += record.Length; } - + return result; } @@ -206,10 +206,10 @@ private static byte[] CreateDdrFieldData(string fieldName, string subfieldDescri ms.WriteByte((byte)('0' + dataType)); // Field name and subfield descriptors - var descriptors = string.IsNullOrEmpty(fieldName) - ? subfieldDescriptors + var descriptors = string.IsNullOrEmpty(fieldName) + ? subfieldDescriptors : (string.IsNullOrEmpty(subfieldDescriptors) ? fieldName : $"{fieldName}!{subfieldDescriptors}"); - + if (!string.IsNullOrEmpty(descriptors)) { ms.Write(Encoding.ASCII.GetBytes(descriptors)); @@ -231,48 +231,48 @@ private static byte[] CreateDdrRecord((string tag, byte[] data)[] fields) // Calculate directory entries var directoryEntries = new List(); var currentPosition = 0; - + foreach (var (tag, data) in fields) { var entry = Encoding.ASCII.GetBytes($"{tag}{data.Length:D3}{currentPosition:D3}"); directoryEntries.Add(entry); currentPosition += data.Length; } - + var directorySize = directoryEntries.Sum(e => e.Length); var baseAddress = 24 + directorySize + 1; var totalFieldSize = fields.Sum(f => f.data.Length); var recordLength = baseAddress + totalFieldSize; - + // DDR leader: 'L' for leader identifier, field control length = 2 var leader = Encoding.ASCII.GetBytes( $"{recordLength:D5}3LE1 02{baseAddress:D5} 3304" ); - + var record = new byte[recordLength]; var offset = 0; - + // Copy leader Array.Copy(leader, 0, record, offset, leader.Length); offset += leader.Length; - + // Copy directory entries foreach (var entry in directoryEntries) { Array.Copy(entry, 0, record, offset, entry.Length); offset += entry.Length; } - + // Directory terminator record[offset++] = FieldTerminator; - + // Copy field data foreach (var (_, data) in fields) { Array.Copy(data, 0, record, offset, data.Length); offset += data.Length; } - + return record; } @@ -294,7 +294,7 @@ private static byte[] CreateDsidRecord( // Build DSID field data using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); - + writer.Write(rcnm); // RCNM writer.Write(rcid); // RCID writer.Write((byte)1); // EXPP @@ -312,9 +312,9 @@ private static byte[] CreateDsidRecord( writer.Write(agen); // AGEN WriteString(writer, ""); // COMT writer.Write((byte)0x1E); // Field terminator - + var dsidData = ms.ToArray(); - + return CreateDataRecord("DSID", dsidData); } @@ -337,7 +337,7 @@ private static byte[] CreateDspmRecord( { using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); - + writer.Write(rcnm); // RCNM writer.Write(rcid); // RCID writer.Write(hdat); // HDAT @@ -352,9 +352,9 @@ private static byte[] CreateDspmRecord( writer.Write(somf); // SOMF WriteString(writer, ""); // COMT writer.Write((byte)0x1E); // Field terminator - + var dspmData = ms.ToArray(); - + return CreateDataRecord("DSPM", dspmData); } @@ -377,7 +377,7 @@ private static byte[] CreateFeatureRecord( { using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); - + // FRID field writer.Write(rcnm); // RCNM writer.Write(rcid); // RCID @@ -387,42 +387,42 @@ private static byte[] CreateFeatureRecord( writer.Write(rver); // RVER writer.Write(ruin); // RUIN writer.Write((byte)0x1E); // Field terminator - + var fridData = ms.ToArray(); - + // Build fields dictionary var fields = new List<(string tag, byte[] data)> { ("FRID", fridData) }; - + // Add ATTF field if attributes provided if (attributes != null && attributes.Length > 0) { using var attfMs = new MemoryStream(); using var attfWriter = new BinaryWriter(attfMs); - + foreach (var attr in attributes) { attfWriter.Write((ushort)attr.AttributeCode); WriteString(attfWriter, attr.Value); } attfWriter.Write((byte)0x1E); - + fields.Add(("ATTF", attfMs.ToArray())); } - + // Add FSPC field if spatial pointer control provided if (spatialPointerControl.HasValue) { using var fspcMs = new MemoryStream(); using var fspcWriter = new BinaryWriter(fspcMs); - + fspcWriter.Write((byte)spatialPointerControl.Value.UpdateInstruction); fspcWriter.Write((ushort)spatialPointerControl.Value.Index); fspcWriter.Write((ushort)spatialPointerControl.Value.Count); fspcWriter.Write((byte)0x1E); - + fields.Add(("FSPC", fspcMs.ToArray())); } @@ -431,7 +431,7 @@ private static byte[] CreateFeatureRecord( { using var fsptMs = new MemoryStream(); using var fsptWriter = new BinaryWriter(fsptMs); - + foreach (var ptr in spatialPointers) { fsptWriter.Write((byte)ptr.Name.RecordNameCode); @@ -441,7 +441,7 @@ private static byte[] CreateFeatureRecord( fsptWriter.Write((byte)ptr.Mask); } fsptWriter.Write((byte)0x1E); - + fields.Add(("FSPT", fsptMs.ToArray())); } @@ -450,12 +450,12 @@ private static byte[] CreateFeatureRecord( { using var ffpcMs = new MemoryStream(); using var ffpcWriter = new BinaryWriter(ffpcMs); - + ffpcWriter.Write((byte)featurePointerControl.Value.UpdateInstruction); ffpcWriter.Write((ushort)featurePointerControl.Value.Index); ffpcWriter.Write((ushort)featurePointerControl.Value.Count); ffpcWriter.Write((byte)0x1E); - + fields.Add(("FFPC", ffpcMs.ToArray())); } @@ -464,7 +464,7 @@ private static byte[] CreateFeatureRecord( { using var ffptMs = new MemoryStream(); using var ffptWriter = new BinaryWriter(ffptMs); - + foreach (var ptr in featurePointers) { // LNAM: AGEN(2) + FIDN(4) + FIDS(2) @@ -475,10 +475,10 @@ private static byte[] CreateFeatureRecord( WriteString(ffptWriter, ptr.Comment); } ffptWriter.Write((byte)0x1E); - + fields.Add(("FFPT", ffptMs.ToArray())); } - + return CreateDataRecordMultiField(fields.ToArray()); } @@ -498,33 +498,33 @@ private static byte[] CreateVectorRecord( { using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); - + // VRID field writer.Write(rcnm); // RCNM writer.Write(rcid); // RCID writer.Write(rver); // RVER writer.Write(ruin); // RUIN writer.Write((byte)0x1E); // Field terminator - + var vridData = ms.ToArray(); - + // Build fields dictionary var fields = new List<(string tag, byte[] data)> { ("VRID", vridData) }; - + // Add VRPC field if vector pointer control provided if (vectorPointerControl.HasValue) { using var vrpcMs = new MemoryStream(); using var vrpcWriter = new BinaryWriter(vrpcMs); - + vrpcWriter.Write((byte)vectorPointerControl.Value.UpdateInstruction); vrpcWriter.Write((ushort)vectorPointerControl.Value.Index); vrpcWriter.Write((ushort)vectorPointerControl.Value.Count); vrpcWriter.Write((byte)0x1E); - + fields.Add(("VRPC", vrpcMs.ToArray())); } @@ -533,7 +533,7 @@ private static byte[] CreateVectorRecord( { using var vrptMs = new MemoryStream(); using var vrptWriter = new BinaryWriter(vrptMs); - + foreach (var ptr in vectorPointers) { vrptWriter.Write((byte)ptr.Name.RecordNameCode); @@ -544,7 +544,7 @@ private static byte[] CreateVectorRecord( vrptWriter.Write((byte)ptr.Mask); } vrptWriter.Write((byte)0x1E); - + fields.Add(("VRPT", vrptMs.ToArray())); } @@ -553,12 +553,12 @@ private static byte[] CreateVectorRecord( { using var sgccMs = new MemoryStream(); using var sgccWriter = new BinaryWriter(sgccMs); - + sgccWriter.Write((byte)coordinateControl.Value.UpdateInstruction); sgccWriter.Write((ushort)coordinateControl.Value.Index); sgccWriter.Write((ushort)coordinateControl.Value.Count); sgccWriter.Write((byte)0x1E); - + fields.Add(("SGCC", sgccMs.ToArray())); } @@ -567,23 +567,23 @@ private static byte[] CreateVectorRecord( { using var sg2dMs = new MemoryStream(); using var sg2dWriter = new BinaryWriter(sg2dMs); - + foreach (var coord in coordinates) { sg2dWriter.Write(coord.Y); sg2dWriter.Write(coord.X); } sg2dWriter.Write((byte)0x1E); - + fields.Add(("SG2D", sg2dMs.ToArray())); } - + // Add SG3D field if soundings provided if (soundings != null && soundings.Length > 0) { using var sg3dMs = new MemoryStream(); using var sg3dWriter = new BinaryWriter(sg3dMs); - + foreach (var snd in soundings) { sg3dWriter.Write(snd.Y); @@ -591,10 +591,10 @@ private static byte[] CreateVectorRecord( sg3dWriter.Write(snd.Depth); } sg3dWriter.Write((byte)0x1E); - + fields.Add(("SG3D", sg3dMs.ToArray())); } - + return CreateDataRecordMultiField(fields.ToArray()); } @@ -612,51 +612,51 @@ private static byte[] CreateDataRecord(string tag, byte[] fieldData) private static byte[] CreateDataRecordMultiField(params (string tag, byte[] data)[] fields) { var fieldTerminator = (byte)0x1E; - + // Calculate directory entries var directoryEntries = new List(); var currentPosition = 0; - + foreach (var (tag, data) in fields) { var entry = Encoding.ASCII.GetBytes($"{tag}{data.Length:D3}{currentPosition:D3}"); directoryEntries.Add(entry); currentPosition += data.Length; } - + var directorySize = directoryEntries.Sum(e => e.Length); var baseAddress = 24 + directorySize + 1; // +1 for directory terminator var totalFieldSize = fields.Sum(f => f.data.Length); var recordLength = baseAddress + totalFieldSize; - + var leader = Encoding.ASCII.GetBytes( $"{recordLength:D5}3DE1 00{baseAddress:D5} 3304" ); - + var record = new byte[recordLength]; var offset = 0; - + // Copy leader Array.Copy(leader, 0, record, offset, leader.Length); offset += leader.Length; - + // Copy directory entries foreach (var entry in directoryEntries) { Array.Copy(entry, 0, record, offset, entry.Length); offset += entry.Length; } - + // Directory terminator record[offset++] = fieldTerminator; - + // Copy field data foreach (var (_, data) in fields) { Array.Copy(data, 0, record, offset, data.Length); offset += data.Length; } - + return record; } @@ -678,10 +678,10 @@ public void Read_EmptyDocument_ReturnsDocumentWithNoRecords() { // Arrange var data = CreateS57Document(); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.NotNull(document); Assert.Null(document.DataSetIdentification); @@ -707,10 +707,10 @@ public void Read_DocumentWithDsid_ParsesDataSetIdentification() agen: 540 ); var data = CreateS57Document(dsidRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal(10, document.DataSetIdentification.RecordName.RecordNameCode); @@ -744,10 +744,10 @@ public void Read_DocumentWithDspm_ParsesDataSetParameters() somf: 10 ); var data = CreateS57Document(dspmRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.NotNull(document.DataSetParameters); Assert.Equal(20, document.DataSetParameters.RecordName.RecordNameCode); @@ -778,10 +778,10 @@ public void Read_DocumentWithFeatureRecord_ParsesFeatureRecord() ruin: (byte)S57UpdateInstruction.Insert ); var data = CreateS57Document(featureRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.FeatureRecords); var feature = document.FeatureRecords[0]; @@ -815,10 +815,10 @@ public void Read_DocumentWithFeatureRecordAndAttributes_ParsesAttributes() attributes: attributes ); var data = CreateS57Document(featureRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.FeatureRecords); var feature = document.FeatureRecords[0]; @@ -842,10 +842,10 @@ public void Read_DocumentWithVectorRecord_ParsesVectorRecord() ruin: (byte)S57UpdateInstruction.Insert ); var data = CreateS57Document(vectorRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.VectorRecords); var vector = document.VectorRecords[0]; @@ -872,10 +872,10 @@ public void Read_DocumentWithVectorRecordAnd2DCoordinates_ParsesCoordinates() coordinates: coordinates ); var data = CreateS57Document(vectorRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.VectorRecords); var vector = document.VectorRecords[0]; @@ -903,10 +903,10 @@ public void Read_DocumentWithVectorRecordAndSoundings_ParsesSoundings() soundings: soundings ); var data = CreateS57Document(vectorRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.VectorRecords); var vector = document.VectorRecords[0]; @@ -929,16 +929,16 @@ public void Read_DocumentWithMultipleRecords_ParsesAllRecords() var featureRecord2 = CreateFeatureRecord(rcid: 2, objl: 159); var vectorRecord1 = CreateVectorRecord(rcnm: S57RecordNameCodes.IsolatedNode, rcid: 1); var vectorRecord2 = CreateVectorRecord(rcnm: S57RecordNameCodes.Edge, rcid: 2); - + var data = CreateS57Document( - dsidRecord, dspmRecord, + dsidRecord, dspmRecord, featureRecord1, featureRecord2, vectorRecord1, vectorRecord2 ); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("TESTDATA", document.DataSetIdentification.DataSetName); @@ -960,11 +960,11 @@ public void GetFeatureRecord_ByRecordName_ReturnsMatchingRecord() var featureRecord2 = CreateFeatureRecord(rcnm: 100, rcid: 2, objl: 159); var data = CreateS57Document(featureRecord1, featureRecord2); var document = S57DocumentReader.Read(data); - + // Act var targetName = S57RecordName.FromRcnmRcid(100, 2); var result = document.GetFeatureRecord(targetName); - + // Assert Assert.NotNull(result); Assert.Equal(2, result.RecordName.RecordId); @@ -978,11 +978,11 @@ public void GetFeatureRecord_NonExistentName_ReturnsNull() var featureRecord = CreateFeatureRecord(rcnm: 100, rcid: 1, objl: 75); var data = CreateS57Document(featureRecord); var document = S57DocumentReader.Read(data); - + // Act var targetName = S57RecordName.FromRcnmRcid(100, 999); var result = document.GetFeatureRecord(targetName); - + // Assert Assert.Null(result); } @@ -995,11 +995,11 @@ public void GetVectorRecord_ByRecordName_ReturnsMatchingRecord() var vectorRecord2 = CreateVectorRecord(rcnm: S57RecordNameCodes.Edge, rcid: 2); var data = CreateS57Document(vectorRecord1, vectorRecord2); var document = S57DocumentReader.Read(data); - + // Act var targetName = S57RecordName.FromRcnmRcid(S57RecordNameCodes.Edge, 2); var result = document.GetVectorRecord(targetName); - + // Assert Assert.NotNull(result); Assert.Equal(S57RecordNameCodes.Edge, result.RecordName.RecordNameCode); @@ -1015,10 +1015,10 @@ public void GetFeaturesByObjectCode_ReturnsMatchingRecords() var featureRecord3 = CreateFeatureRecord(rcnm: 100, rcid: 3, objl: 75); var data = CreateS57Document(featureRecord1, featureRecord2, featureRecord3); var document = S57DocumentReader.Read(data); - + // Act var results = document.GetFeaturesByObjectCode(S57ObjectCode.LIGHTS).ToArray(); - + // Assert Assert.Equal(2, results.Length); Assert.All(results, r => Assert.Equal(S57ObjectCode.LIGHTS, r.ObjectCode)); @@ -1031,10 +1031,10 @@ public void CoordinateMultiplicationFactor_WithDspm_ReturnsValue() var dspmRecord = CreateDspmRecord(comf: 10000000); var data = CreateS57Document(dspmRecord); var document = S57DocumentReader.Read(data); - + // Act var result = document.CoordinateMultiplicationFactor; - + // Assert Assert.Equal(10000000, result); } @@ -1045,10 +1045,10 @@ public void CoordinateMultiplicationFactor_WithoutDspm_ReturnsDefault() // Arrange var data = CreateS57Document(); var document = S57DocumentReader.Read(data); - + // Act var result = document.CoordinateMultiplicationFactor; - + // Assert Assert.Equal(10000000, result); // Default value } @@ -1060,10 +1060,10 @@ public void SoundingMultiplicationFactor_WithDspm_ReturnsValue() var dspmRecord = CreateDspmRecord(somf: 100); var data = CreateS57Document(dspmRecord); var document = S57DocumentReader.Read(data); - + // Act var result = document.SoundingMultiplicationFactor; - + // Assert Assert.Equal(100, result); } @@ -1074,10 +1074,10 @@ public void SoundingMultiplicationFactor_WithoutDspm_ReturnsDefault() // Arrange var data = CreateS57Document(); var document = S57DocumentReader.Read(data); - + // Act var result = document.SoundingMultiplicationFactor; - + // Assert Assert.Equal(10, result); // Default value } @@ -1091,7 +1091,7 @@ public void S57RecordName_FromRcnmRcid_CreatesCorrectly() { // Act var name = S57RecordName.FromRcnmRcid(100, 42); - + // Assert Assert.Equal(100, name.RecordNameCode); Assert.Equal(42, name.RecordId); @@ -1105,7 +1105,7 @@ public void S57RecordName_FromLongName_CreatesCorrectly() { // Act var name = S57RecordName.FromLongName(540, 12345, 1); - + // Assert Assert.Equal(540, name.AgencyCode); Assert.Equal(12345, name.FeatureId); @@ -1118,7 +1118,7 @@ public void S57RecordName_Equality_SameValues_ReturnsTrue() // Arrange var name1 = S57RecordName.FromRcnmRcid(100, 42); var name2 = S57RecordName.FromRcnmRcid(100, 42); - + // Assert Assert.Equal(name1, name2); Assert.True(name1 == name2); @@ -1131,7 +1131,7 @@ public void S57RecordName_Equality_DifferentValues_ReturnsFalse() // Arrange var name1 = S57RecordName.FromRcnmRcid(100, 42); var name2 = S57RecordName.FromRcnmRcid(100, 43); - + // Assert Assert.NotEqual(name1, name2); Assert.False(name1 == name2); @@ -1144,7 +1144,7 @@ public void S57RecordName_GetHashCode_SameValues_ReturnsSameHash() // Arrange var name1 = S57RecordName.FromRcnmRcid(100, 42); var name2 = S57RecordName.FromRcnmRcid(100, 42); - + // Assert Assert.Equal(name1.GetHashCode(), name2.GetHashCode()); } @@ -1154,10 +1154,10 @@ public void S57RecordName_ToString_ReturnsExpectedFormat() { // Arrange var name = S57RecordName.FromRcnmRcid(100, 42); - + // Act var result = name.ToString(); - + // Assert Assert.Equal("RCNM=100, RCID=42", result); } @@ -1171,7 +1171,7 @@ public void S57AttributeValue_Constructor_SetsProperties() { // Act var attr = new S57AttributeValue(116, "RED"); - + // Assert Assert.Equal(116, attr.AttributeCode); Assert.Equal("RED", attr.Value); @@ -1182,10 +1182,10 @@ public void S57AttributeValue_ToString_ReturnsExpectedFormat() { // Arrange var attr = new S57AttributeValue(116, "RED"); - + // Act var result = attr.ToString(); - + // Assert Assert.Equal("ATTL=116, ATVL=RED", result); } @@ -1200,10 +1200,10 @@ public void S57Coordinate2D_ToDecimalDegrees_ConvertsCorrectly() // Arrange var coord = new S57Coordinate2D { X = -1225000000, Y = 475000000 }; var multiplicationFactor = 10000000; - + // Act var (longitude, latitude) = coord.ToDecimalDegrees(multiplicationFactor); - + // Assert Assert.Equal(-122.5, longitude, 6); Assert.Equal(47.5, latitude, 6); @@ -1220,10 +1220,10 @@ public void S57Sounding_ToDecimalValues_ConvertsCorrectly() var sounding = new S57Sounding { X = -1225000000, Y = 475000000, Depth = 150 }; var coordFactor = 10000000; var soundingFactor = 10; - + // Act var (longitude, latitude, depth) = sounding.ToDecimalValues(coordFactor, soundingFactor); - + // Assert Assert.Equal(-122.5, longitude, 6); Assert.Equal(47.5, latitude, 6); @@ -1281,10 +1281,10 @@ public void Read_ByteArray_ReturnsDocument() // Arrange var dsidRecord = CreateDsidRecord(dsnm: "BYTEARRAY"); var data = CreateS57Document(dsidRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("BYTEARRAY", document.DataSetIdentification.DataSetName); @@ -1296,10 +1296,10 @@ public void Read_ReadOnlySpan_ReturnsDocument() // Arrange var dsidRecord = CreateDsidRecord(dsnm: "SPANTEST"); var data = CreateS57Document(dsidRecord); - + // Act var document = S57DocumentReader.Read(data.AsSpan()); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("SPANTEST", document.DataSetIdentification.DataSetName); @@ -1311,14 +1311,14 @@ public void Read_Stream_ReturnsDocument() // Arrange var dsidRecord = CreateDsidRecord(dsnm: "STREAMTEST"); var data = CreateS57Document(dsidRecord); - + // Act S57Document document; using (var stream = new MemoryStream(data)) { document = S57DocumentReader.Read(stream); } - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("STREAMTEST", document.DataSetIdentification.DataSetName); @@ -1330,14 +1330,14 @@ public async Task ReadAsync_Stream_ReturnsDocument() // Arrange var dsidRecord = CreateDsidRecord(dsnm: "ASYNCSTREAM"); var data = CreateS57Document(dsidRecord); - + // Act S57Document document; using (var stream = new MemoryStream(data)) { document = await S57DocumentReader.ReadAsync(stream); } - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("ASYNCSTREAM", document.DataSetIdentification.DataSetName); @@ -1350,14 +1350,14 @@ public void ReadFromFile_ValidFile_ReturnsDocument() var dsidRecord = CreateDsidRecord(dsnm: "FILETEST"); var data = CreateS57Document(dsidRecord); var tempFile = Path.GetTempFileName(); - + try { File.WriteAllBytes(tempFile, data); - + // Act var document = S57DocumentReader.ReadFromFile(tempFile); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("FILETEST", document.DataSetIdentification.DataSetName); @@ -1375,14 +1375,14 @@ public async Task ReadFromFileAsync_ValidFile_ReturnsDocument() var dsidRecord = CreateDsidRecord(dsnm: "ASYNCFILE"); var data = CreateS57Document(dsidRecord); var tempFile = Path.GetTempFileName(); - + try { await File.WriteAllBytesAsync(tempFile, data); - + // Act var document = await S57DocumentReader.ReadFromFileAsync(tempFile); - + // Assert Assert.NotNull(document.DataSetIdentification); Assert.Equal("ASYNCFILE", document.DataSetIdentification.DataSetName); @@ -1759,10 +1759,10 @@ public void Read_FeatureRecordWithEmptyAttributes_ReturnsEmptyAttributeArray() attributes: null ); var data = CreateS57Document(featureRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.FeatureRecords); Assert.Empty(document.FeatureRecords[0].Attributes); @@ -1778,10 +1778,10 @@ public void Read_VectorRecordWithNoCoordinates_ReturnsEmptyCoordinateArray() coordinates: null ); var data = CreateS57Document(vectorRecord); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Single(document.VectorRecords); Assert.Empty(document.VectorRecords[0].Coordinates2D); @@ -1796,10 +1796,10 @@ public void Read_MultipleFeatureRecords_PreservesOrder() var featureRecord2 = CreateFeatureRecord(rcnm: 100, rcid: 2, objl: 20); var featureRecord3 = CreateFeatureRecord(rcnm: 100, rcid: 3, objl: 30); var data = CreateS57Document(featureRecord1, featureRecord2, featureRecord3); - + // Act - Call S57Reader.Read FIRST var document = S57DocumentReader.Read(data); - + // Assert Assert.Equal(3, document.FeatureRecords.Count); Assert.Equal(1, document.FeatureRecords[0].RecordName.RecordId); @@ -1816,10 +1816,10 @@ public void Read_DifferentVectorTypes_ParsesCorrectly() var edge = CreateVectorRecord(rcnm: S57RecordNameCodes.Edge, rcid: 3); var face = CreateVectorRecord(rcnm: S57RecordNameCodes.Face, rcid: 4); var data = CreateS57Document(isolatedNode, connectedNode, edge, face); - + // Act var document = S57DocumentReader.Read(data); - + // Assert Assert.Equal(4, document.VectorRecords.Count); Assert.Equal(S57RecordNameCodes.IsolatedNode, document.VectorRecords[0].RecordName.RecordNameCode);