GIS - Convert from one format to any formats.

Dani_S 4,946 Reputation points
2025-12-12T10:13:08.8933333+00:00

Hi,

1.In continue to: https://learn.microsoft.com/en-us/answers/questions/5657151/gis-trycreateforinput

In this ticket the output was the selected converter.

Now the focus on the converter.

2.List of GIS converters:

EsriJson (.esrijson, .json)

GeoJson (.geojson,.json)

GeoJsonSeq (.json)

Kml (.kml)

Kmz (.kmz)

Shapefile (.shp, .shx, .dbf)

Osm (.osm)

Gpx (.gpx)

Gml (.gml)

Gdb (.gdb folder as a single file or .gdbtable and .gdbtablx as an archive file)

TopoJson (.json)

MapInfoInterchange (.mif)

MapInfoTab (.tab, .map, .dat, .id)

Csv (.csv)

GeoPackage (.gpkg)

3.All converters do the same aim: convert to all other formats include itself.

The method timestamp:

public ConversionResult Convert(string gisInputFilePath, string gisTargetFormatOption, string outputFolderPath, string tempFolderPath)

gisInputFilePath= input can be a single or an archive file.

I made converter to each format is it ok?

4.Can you please check the code and see if there are problem , or need refactor ?

5.I added ShapefileConverter and all its helper classes + tests(ShapefileConverterIntegrationTests.txt)??

6.Can you please check?

Thanks in advance,

The files:

ConversionResult.txt

AsposeLicenseManager.txt

Convert.txt

ConverterUtils.txt

FileExtension.txt

FileExtensionHelpers.txt

ShapefileConverter.txt

ShapefileConverterIntegrationTests.txt

Developer technologies | C#
Developer technologies | C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
{count} votes

1 answer

Sort by: Most helpful
  1. Michael Le (WICLOUD CORPORATION) 6,670 Reputation points Microsoft External Staff Moderator
    2025-12-15T04:23:55.55+00:00

    Hello @Dani_S ,

    I appreciate the effort you’ve put into this, but there are a few areas that could cause problems down the road.

    In summary, what I'd recommend

    These are architectural decisions you'll need to work through on your end since you know your requirements best.

    • Consolidate the converters into one that handles format detection and routing.
    • Refactor the large Convert method into smaller focused methods.
    • Replace switch statements with dictionaries. Separate validation from directory creation.
    • Move license initialization to startup. Strengthen tests to validate output contents, not just file existence.

    For more details, see below.

    The biggest issue: creating 14 separate converter classes

    I made converter to each format is it ok?

    That means you're writing essentially the same code 14 times, right?

    All your converters follow the same steps. The only difference is which driver you’re using, and you already have a helper that maps format names to drivers.

    A single converter using those helpers would let you maintain one codebase, fix bugs once, and add features once.

    The ShapefileConverter method does too much

    Can you please check the code and see if there are problem, or need refactor?

    Your Convert method is handling too many responsibilities in one place, which makes it hard to follow and test.

    Breaking it into smaller methods where each has one job—one validates archive contents, another extracts, another converts—would make the code easier to read, test, and debug.

    Switch statements instead of dictionaries

    Your FileExtensionHelpers class uses switch statements with 15+ cases. The runtime checks each case until it finds a match. Dictionaries do direct lookups, which are faster and cleaner. Adding new formats with a switch means modifying code, while with dictionaries it’s just one line of initialization.

    Something like this would be more efficient:

    
    public static class FileExtensionHelpers
    {
        private static readonly Dictionary<FileExtension, string> ExtensionMap = new()
        {
            { FileExtension.Csv, ". csv" },
            { FileExtension.EsriJson, ".json" },
            { FileExtension.FileGdb, ".gdbtable" },
            { FileExtension.GeoJson, ".json" },
            { FileExtension.GeoJsonSeq, ". json" },
            { FileExtension.GeoPackage, ".gpkg" },
            { FileExtension. Gml, ".gml" },
            { FileExtension.Gpx, ".gpx" },
            { FileExtension.Kml, ".kml" },
            { FileExtension.Kmz, ".kml" },
            { FileExtension.MapInfoInterchange, ".mid" },
            { FileExtension.MapInfoTab, ".tab" },
            { FileExtension. OsmXml, ".osm" },
            { FileExtension.Shapefile, ".shp" },
            { FileExtension.TopoJson, ".json" }
        };
    
        private static readonly Dictionary<string, FileExtension> OptionMap = new(StringComparer.OrdinalIgnoreCase)
        {
            { "geojson", FileExtension.GeoJson },
            { "geojsonseq", FileExtension.GeoJsonSeq },
            { "esrijson", FileExtension.EsriJson },
            { "gdb", FileExtension.FileGdb },
            { "filegdb", FileExtension.FileGdb },
            { "kml", FileExtension.Kml },
            { "kmz", FileExtension.Kmz },
            { "shapefile", FileExtension.Shapefile },
            { "topojson", FileExtension.TopoJson },
            { "osm", FileExtension.OsmXml },
            { "osmxml", FileExtension.OsmXml },
            { "gpx", FileExtension.Gpx },
            { "gml", FileExtension.Gml },
            { "mif", FileExtension.MapInfoInterchange },
            { "mapinfointerchange", FileExtension.MapInfoInterchange },
            { "tab", FileExtension.MapInfoTab },
            { "mapinfotab", FileExtension.MapInfoTab },
            { "csv", FileExtension.Csv },
            { "gpkg", FileExtension.GeoPackage },
            { "geopackage", FileExtension. GeoPackage }
        };
    
        // O(1) lookup instead of O(n) switch
        public static string ToDotExtension(this FileExtension ext) =>
            ExtensionMap.TryGetValue(ext, out var result) ? result : string.Empty;
    
        public static FileExtension?  FromOption(string option) =>
            string.IsNullOrWhiteSpace(option) ? null :
            OptionMap.TryGetValue(option. Trim(), out var result) ? result : (FileExtension?)null;
    }
    

    Mixing validation with side effects

    Your ValidateAndPreparePaths method both checks validity and creates directories. Validation should only report problems, while preparation should create resources. Keeping these separate means you can validate without creating anything, and test validation without touching the file system.

    Applying license on every conversion

    I added ShapefileConverter and all its helper classes + tests(ShapefileConverterIntegrationTests.txt)??

    You call AsposeLicenseManager.ApplyLicense in every conversion. The license doesn’t change between conversions.

    Apply it once at application startup, then every conversion will use the licensed library automatically.

    Tests don’t verify enough

    Your integration test checks if files exist, but not whether the conversion worked correctly. Did geometry get preserved? Are attributes intact? Is the output format correct? Stronger tests would load the output and verify that contents match expectations. They’d also test malformed archives, missing files, and invalid options.


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.