# GisConverter.Lib `GisConverter.Lib` is the core library that implements GIS conversion logic, detection heuristics and supporting infrastructure used by the ConsoleApp and test projects. This README summarizes the library responsibilities, public surface, usage patterns, testing guidance, extension steps and operational notes for maintainers. > Targets: .NET Standard 2.1 (consumable by .NET 9 hosts) Contents - Overview - Responsibilities - Public surface & key types - Quickstart (host usage) - Logging and diagnostics - Licensing (Aspose.GIS) - Testing guidance - Extending the library (adding formats) - Packaging & compatibility - Troubleshooting - Contribution guidance - Maintainer checklist - Nullable reference types and language guidance - Quickstart examples ## Overview The library provides a small, focused API to convert between GIS formats (CSV, Shapefile, GML, GeoJSON and others) while centralizing file-system safety, archive handling and detection logic. Converters implement `IConverter` and are resolved by the registry-style `ConverterFactory`. ## Responsibilities - Implement format-specific converters (Aspose.GIS-backed or lightweight implementations). - Provide utilities for: - Input validation and directory preparation (`ConverterUtils`). - Archive inspection and safe extraction (zip-slip protection). - JSON format detection heuristics (`JsonFormatDetector`) for GeoJSON, EsriJSON, TopoJSON and GeoJSON Sequence (NDJSON). - Mapping of canonical option strings to drivers and file extension helpers. - Expose a lightweight logging abstraction (`IAppLogger` / `Log`) so tests and hosts capture diagnostics. - Centralize license application (`AsposeLicenseManager`) so converters are license-aware without embedding licensing logic. ## Public surface & key types - `IConverter` — converter contract. Main method: - `ConversionResult Convert(string gisInputFilePath, string gisSourceFormatOption, string gisTargetFormatOption, string outputFolderPath, string tempFolderPath)` - `IConverterFactory` / `ConverterFactory` — resolve converters by friendly display keys. Use `TryCreate` for non-throwing flows and `Create` when you want an exception with suggestion on unknown keys. - `ConverterUtils` — path validation, archive listing, safe extraction, output path construction and temp cleanup helpers. - `JsonFormatDetector` — heuristics and bounded-header sniffers for JSON formats. - `ConversionResult` / `ConversionStatus` — uniform result carrier for converters. - `FileExtension` / `FileExtensionHelpers` — canonical extension mappings used to build output file names. - `AsposeLicenseManager` — centralized license application helper. ## Quickstart (host usage) Typical flow for a host (ConsoleApp or service): 1. Configure logging early (see `GisConverter.Lib.Logging.Log`). 2. Optionally apply Aspose license: `AsposeLicenseManager.ApplyLicense()` and fail gracefully if required. 3. Construct or obtain a `ConverterFactory` instance. 4. Validate inputs using `ConverterUtils.ValidateInputs` and prepare output/temp folders with `ConverterUtils.PreparePaths`. 5. Resolve a converter using `TryCreate(sourceOrTargetOption, out var converter)` and call `converter.Convert(...)`. 6. Inspect `ConversionResult` and map to exit codes or UI messages. ### Minimal host wiring (example) // Configure logging (optional) Log.SetFile("logs/conversion.log", "Info"); Log.Enable(); // Apply Aspose license (optional for licensed converters) if (!AsposeLicenseManager.ApplyLicense()) { Console.Error.WriteLine("Aspose license not applied; licensed converters may fail."); } var factory = new ConverterFactory(); if (!factory.TryCreate("GeoJson", out var converter)) { Console.Error.WriteLine("Unknown target format"); return 1; } var validation = ConverterUtils.ValidateInputs(inputPath, outputFolder, tempFolder); if (validation != null) { Console.Error.WriteLine(validation.Message); return 1; } var prep = ConverterUtils.PreparePaths(outputFolder, tempFolder); if (prep != null) { Console.Error.WriteLine(prep.Message); return 1; } var result = converter.Convert(inputPath, sourceOption, targetOption, outputFolder, tempFolder); Console.WriteLine(result.Message); return result.Status == ConversionStatus.Success ? 0 : 1; ## Logging and diagnostics - Use the `Log` façade (`Log.Debug`, `Log.Info`, `Log.Warn`, `Log.Error`) throughout the library. - In production configure file logging via `Log.SetFile(path, level)` and call `Log.Enable()`. - In unit tests replace `Log.Current` with a `TestLogger` to capture messages for assertions using `LogAssert`. Key tokens to search for when diagnosing failures: - `starting conversion (option='...')` - `Conversion succeeded:` / `Conversion failed:` - Archive extraction / zip-slip warnings - `lookup miss` and factory registration summaries ## Licensing (Aspose.GIS) - `AsposeLicenseManager.ApplyLicense()` attempts to apply an Aspose license (embedded resource first, optional file fallback) and returns `true` on success. - The method logs diagnostics and returns `false` on failure (it does not throw); callers should surface friendly messages to users. - Do not commit license files to source control. Provision via CI secrets or secure provisioning for licensed integration runs. ## Testing guidance - Unit tests - Inject a small `registrations` dictionary and a `TestLogger` into `ConverterFactory` to avoid creating heavy converters. - Test `ConverterUtils`, `FactoryHelpers` and `JsonFormatDetector` with small synthetic inputs. - Prefer `TryCreate` in tests unless verifying suggestion/exception behavior. - Integration tests - Place small samples under `TestsApp/TestData/` and mark with `PreserveNewest`. - Gate licensed or expensive integration tests using environment flags such as `GISCONVERTER_ENABLE_INTEGRATION=1` and check `AsposeLicenseManager.ApplyLicense()` before exercising licensed flows. - Assertions - Use `FileExtensionHelpers.ToDotExtension(...)` to derive expected file suffixes. - Prefer case-insensitive substring checks for messages and log tokens to reduce brittleness. ## Extending the library (adding formats) 1. Implement an `IConverter` (or reuse `UniversalGisConverter` for simple wrappers). 2. Add option key and registration in `ConverterFactory`. 3. Map driver and file extension in `ConverterUtils` and `FileExtensionHelpers`. 4. Add unit tests for detection and factory routing and a small integration sample/test if applicable. ## Packaging & compatibility - Targets `.NET Standard 2.1` so the library can be consumed by `.NET 9` hosts. - Keep public APIs platform-agnostic to preserve portability. ## Troubleshooting - Missing converter / lookup failures: verify `FactoryHelpers.NormalizeKey` and review factory startup logs for supported keys. - Archive extraction problems: inspect archive listing helpers and zip-slip warnings; ensure test zips include expected entry paths (see TestData README for zip tips). - License-related failures: confirm embedded resource name or configured path and file permissions. ## Contribution guidance - Open issues for bugs, missing formats or flaky tests. Provide failing test name, stack trace, logs and sample artifacts when possible. - When contributing converters include unit tests for edge cases (ambiguous JSON, KMZ detection, zipped FileGDB) and minimal integration samples. ## Maintainer checklist - When adding a format: - Add option keys and registrations in `ConverterFactory`. - Update `FileExtensionHelpers` and `ConverterUtils` mappings. - Add unit tests for detection and behavior. - Add integration sample under `TestsApp/TestData/` and a gated test if appropriate. ## API documentation Per-type XML remarks are available in source files and surface in IDE IntelliSense. Key types: - `GisConverter.Lib.Converters.IConverter` - `GisConverter.Lib.Factories.ConverterFactory` - `GisConverter.Lib.Utils.ConverterUtils` - `GisConverter.Lib.Logging.Log` --- ## Nullable reference types and language guidance - Recommended C# and nullable settings - The library targets .NET Standard 2.1 and is consumed by .NET 9 hosts. We recommend enabling nullable reference types for new and updated C# files to improve null-safety and reduce runtime NREs. - Enable project-wide nullable annotations by adding the following to the library project file (`.csproj`): netstandard2.1 enable - Alternatively, opt files into nullable annotations on a per-file basis using `#nullable enable` at the top of the file when you cannot change the project file. - Why enable nullable annotations - Improves static analysis and communicates intent (which references may be null). - Prevents compiler warning CS8632 where `?` annotations are used outside a `#nullable` context. - Encourages safer APIs for library consumers across different target frameworks. - Migration tips - Start by enabling `enable` on feature branches and fix warnings incrementally. - For quick fixes in individual files add `#nullable enable` at the top of the file and annotate references as nullable (`string?`) or non-nullable (`string`). - Prefer adding nullability annotations to public APIs to make contracts explicit for consumers. - CI and consumers - Enabling nullable annotations is backward compatible with .NET 9 and other consumers; it only affects compile-time analysis. - Document the project-level setting in this README so contributors are aware of the preferred coding standard. ## Quickstart examples and runnable samples (optional) - Suggestion: add a `Quickstart.md` or an `examples/` folder with small, runnable host snippets showing: - Programmatic logging configuration via `Log.SetFile(...)` and `Log.Enable()`. - Applying an Aspose license with `AsposeLicenseManager.ApplyLicense()` and graceful fallback. - Minimal `ConverterFactory` usage demonstrating `TryCreate` and `Convert` with `ConverterUtils.PreparePaths`. Including small examples helps new contributors and makes integration tests easier to reproduce locally.