All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openstreetmap.atlas.utilities.command.subcommands.AnyToGeoJsonCommand Maven / Gradle / Ivy

package org.openstreetmap.atlas.utilities.command.subcommands;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.openstreetmap.atlas.geography.atlas.Atlas;
import org.openstreetmap.atlas.geography.atlas.AtlasResourceLoader;
import org.openstreetmap.atlas.geography.boundary.CountryBoundaryMap;
import org.openstreetmap.atlas.geography.boundary.converters.CountryBoundaryMapGeoJsonConverter;
import org.openstreetmap.atlas.geography.sharding.Sharding;
import org.openstreetmap.atlas.streaming.resource.File;
import org.openstreetmap.atlas.streaming.resource.FileSuffix;
import org.openstreetmap.atlas.utilities.command.AtlasShellToolsException;
import org.openstreetmap.atlas.utilities.command.abstractcommand.CommandOutputDelegate;
import org.openstreetmap.atlas.utilities.command.abstractcommand.OptionAndArgumentDelegate;
import org.openstreetmap.atlas.utilities.command.parsing.OptionOptionality;
import org.openstreetmap.atlas.utilities.command.subcommands.templates.MultipleOutputCommand;

import com.google.gson.GsonBuilder;

/**
 * This command converts our many different file formats to a GeoJSON representation. This may be
 * useful for various visualization software.
 * 
 * @author lcram
 */
public class AnyToGeoJsonCommand extends MultipleOutputCommand
{
    private static final String ATLAS_OPTION_LONG = "atlas";
    private static final String ATLAS_OPTION_DESCRIPTION = "The path to an atlas file to be converted.";
    private static final String ATLAS_OPTION_HINT = "atlas-file";

    private static final String SHARDING_OPTION_LONG = "sharding";
    private static final String SHARDING_OPTION_DESCRIPTION = "The sharding to convert, e.g. dynamic@/Users/foo/my-tree.txt";
    private static final String SHARDING_OPTION_HINT = "type@parameter";

    private static final String COUNTRY_BOUNDARY_OPTION_LONG = "country-boundary";
    private static final String COUNTRY_BOUNDARY_OPTION_DESCRIPTION = "The path to a boundary file to be converted.";
    private static final String COUNTRY_BOUNDARY_OPTION_HINT = "boundary-file";

    private static final String COUNTRIES_OPTION_LONG = "countries";
    private static final Character COUNTRIES_OPTION_SHORT = 'c';
    private static final String COUNTRIES_OPTION_DESCRIPTION = "A comma separated list of allowlist country codes to exclusively include. Defaults to all.";
    private static final String COUNTRIES_OPTION_HINT = "included-countries";

    private static final String COUNTRIES_DENY_LIST_OPTION_LONG = "countries-denylist";
    private static final Character COUNTRIES_DENY_LIST_OPTION_SHORT = 'C';
    private static final String COUNTRIES_DENY_LIST_OPTION_DESCRIPTION = "A comma separated denylist of country codes to explicitly exclude. Defaults to none.";
    private static final String COUNTRIES_DENY_LIST_OPTION_HINT = "excluded-countries";

    private static final String POLYGONS_OPTION_LONG = "use-polygons";
    private static final Character POLYGONS_OPTION_SHORT = 'p';
    private static final String POLYGONS_OPTION_DESCRIPTION = "Use polygons instead of linestrings for the boundary GeoJSON. This may be better for certain visualization software.";

    private static final Integer ATLAS_CONTEXT = 3;
    private static final Integer SHARDING_CONTEXT = 4;
    private static final Integer BOUNDARY_CONTEXT = 5;

    private static final String OUTPUT_FILE = "output";
    private static final String ATLAS_FILE = OUTPUT_FILE + "-" + ATLAS_OPTION_LONG
            + FileSuffix.GEO_JSON;
    private static final String SHARDING_FILE = OUTPUT_FILE + "-" + SHARDING_OPTION_LONG
            + FileSuffix.GEO_JSON;
    private static final String BOUNDARY_FILE = OUTPUT_FILE + "-" + COUNTRY_BOUNDARY_OPTION_LONG
            + FileSuffix.GEO_JSON;

    private final OptionAndArgumentDelegate optionAndArgumentDelegate;
    private final CommandOutputDelegate outputDelegate;

    public static void main(final String[] args)
    {
        new AnyToGeoJsonCommand().runSubcommandAndExit(args);
    }

    public AnyToGeoJsonCommand()
    {
        this.optionAndArgumentDelegate = this.getOptionAndArgumentDelegate();
        this.outputDelegate = this.getCommandOutputDelegate();
    }

    @Override
    public int execute()
    {
        super.execute();

        if (this.optionAndArgumentDelegate.getParserContext() == ATLAS_CONTEXT)
        {
            return executeAtlasContext();
        }
        else if (this.optionAndArgumentDelegate.getParserContext() == SHARDING_CONTEXT)
        {
            return executeShardingContext();
        }
        else if (this.optionAndArgumentDelegate.getParserContext() == BOUNDARY_CONTEXT)
        {
            return executeBoundaryContext();
        }
        else
        {
            throw new AtlasShellToolsException();
        }
    }

    @Override
    public String getCommandName()
    {
        return "any2geojson";
    }

    @Override
    public String getSimpleDescription()
    {
        return "convert a custom file format to GeoJSON";
    }

    @Override
    public void registerManualPageSections()
    {
        addManualPageSection("DESCRIPTION", AtlasSearchCommand.class
                .getResourceAsStream("AnyToGeoJsonCommandDescriptionSection.txt"));
        addManualPageSection("EXAMPLES", AtlasSearchCommand.class
                .getResourceAsStream("AnyToGeoJsonCommandExamplesSection.txt"));
        super.registerManualPageSections();
    }

    @Override
    public void registerOptionsAndArguments()
    {
        registerOptionWithRequiredArgument(ATLAS_OPTION_LONG, ATLAS_OPTION_DESCRIPTION,
                OptionOptionality.REQUIRED, ATLAS_OPTION_HINT, ATLAS_CONTEXT);
        registerOptionWithRequiredArgument(SHARDING_OPTION_LONG, SHARDING_OPTION_DESCRIPTION,
                OptionOptionality.REQUIRED, SHARDING_OPTION_HINT, SHARDING_CONTEXT);
        registerOptionWithRequiredArgument(COUNTRY_BOUNDARY_OPTION_LONG,
                COUNTRY_BOUNDARY_OPTION_DESCRIPTION, OptionOptionality.REQUIRED,
                COUNTRY_BOUNDARY_OPTION_HINT, BOUNDARY_CONTEXT);
        registerOptionWithRequiredArgument(COUNTRIES_OPTION_LONG, COUNTRIES_OPTION_SHORT,
                COUNTRIES_OPTION_DESCRIPTION, OptionOptionality.OPTIONAL, COUNTRIES_OPTION_HINT,
                BOUNDARY_CONTEXT);
        registerOptionWithRequiredArgument(COUNTRIES_DENY_LIST_OPTION_LONG,
                COUNTRIES_DENY_LIST_OPTION_SHORT, COUNTRIES_DENY_LIST_OPTION_DESCRIPTION,
                OptionOptionality.OPTIONAL, COUNTRIES_DENY_LIST_OPTION_HINT, BOUNDARY_CONTEXT);
        registerOption(POLYGONS_OPTION_LONG, POLYGONS_OPTION_SHORT, POLYGONS_OPTION_DESCRIPTION,
                OptionOptionality.OPTIONAL, BOUNDARY_CONTEXT);
        super.registerOptionsAndArguments();
    }

    private int executeAtlasContext()
    {
        final File atlasFile = new File(this.optionAndArgumentDelegate
                .getOptionArgument(ATLAS_OPTION_LONG).orElseThrow(AtlasShellToolsException::new),
                this.getFileSystem());
        if (!atlasFile.exists())
        {
            this.outputDelegate
                    .printlnErrorMessage("file not found: " + atlasFile.getAbsolutePathString());
            return 1;
        }
        final Atlas atlas = new AtlasResourceLoader().load(atlasFile);
        final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),
                ATLAS_FILE);
        final File outputFile = new File(concatenatedPath.toAbsolutePath().toString(),
                this.getFileSystem());
        if (this.optionAndArgumentDelegate.hasVerboseOption())
        {
            this.outputDelegate.printlnCommandMessage(
                    "writing the atlas geojson file to " + outputFile.toAbsolutePath().toString());
        }
        atlas.saveAsLineDelimitedGeoJsonFeatures(outputFile, (entity, json) ->
        {
            // Dummy consumer, we don't need to mutate the JSON
        });
        return 0;
    }

    private int executeBoundaryContext()
    {
        Set countries = new HashSet<>();
        if (this.optionAndArgumentDelegate.hasOption(COUNTRIES_OPTION_LONG))
        {
            countries = this.optionAndArgumentDelegate
                    .getOptionArgument(COUNTRIES_OPTION_LONG, this::parseCommaSeparatedCountries)
                    .orElse(new HashSet<>());
        }
        final boolean usePolygons = this.optionAndArgumentDelegate.hasOption(POLYGONS_OPTION_LONG);
        Set countriesDenyList = new HashSet<>();
        if (this.optionAndArgumentDelegate.hasOption(COUNTRIES_DENY_LIST_OPTION_LONG))
        {
            countriesDenyList = this.optionAndArgumentDelegate
                    .getOptionArgument(COUNTRIES_DENY_LIST_OPTION_LONG,
                            this::parseCommaSeparatedCountries)
                    .orElse(new HashSet<>());
        }

        if (this.optionAndArgumentDelegate.hasVerboseOption())
        {
            this.outputDelegate.printlnCommandMessage("reading CountryBoundaryMap from file...");
        }
        final CountryBoundaryMap map = CountryBoundaryMap.fromPlainText(new File(
                this.optionAndArgumentDelegate.getOptionArgument(COUNTRY_BOUNDARY_OPTION_LONG)
                        .orElseThrow(AtlasShellToolsException::new),
                this.getFileSystem()));
        final String boundaryJson;
        if (countries.isEmpty())
        {
            boundaryJson = new CountryBoundaryMapGeoJsonConverter().prettyPrint(true)
                    .withCountryDenyList(countriesDenyList).usePolygons(usePolygons)
                    .convertToString(map);
        }
        else
        {
            boundaryJson = new CountryBoundaryMapGeoJsonConverter().withCountryAllowList(countries)
                    .withCountryDenyList(countriesDenyList).prettyPrint(true)
                    .usePolygons(usePolygons).convertToString(map);
        }
        if (this.optionAndArgumentDelegate.hasVerboseOption())
        {
            this.outputDelegate.printlnCommandMessage("converting boundary file to GeoJSON...");
        }

        final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),
                BOUNDARY_FILE);
        if (this.optionAndArgumentDelegate.hasVerboseOption())
        {
            this.outputDelegate.printlnCommandMessage("writing the boundary geojson file to "
                    + concatenatedPath.toAbsolutePath().toString());
        }
        new File(concatenatedPath.toAbsolutePath().toString(), this.getFileSystem())
                .writeAndClose(boundaryJson);

        return 0;
    }

    private int executeShardingContext()
    {
        final String shardingString = this.optionAndArgumentDelegate
                .getOptionArgument(SHARDING_OPTION_LONG).orElseThrow(AtlasShellToolsException::new);
        final Sharding sharding;
        try
        {
            sharding = Sharding.forString(shardingString, this.getFileSystem());
        }
        catch (final Exception exception)
        {
            this.outputDelegate.printlnErrorMessage(exception.getMessage());
            return 1;
        }

        final String shardingJson = new GsonBuilder().setPrettyPrinting().create()
                .toJson(sharding.asGeoJson());
        final Path concatenatedPath = Paths.get(getOutputPath().toAbsolutePath().toString(),
                SHARDING_FILE);
        if (this.optionAndArgumentDelegate.hasVerboseOption())
        {
            this.outputDelegate.printlnCommandMessage("writing the sharding geojson file to "
                    + concatenatedPath.toAbsolutePath().toString());
        }
        new File(concatenatedPath.toAbsolutePath().toString(), this.getFileSystem())
                .writeAndClose(shardingJson);
        return 0;
    }

    private Set parseCommaSeparatedCountries(final String countryString)
    {
        final Set countrySet = new HashSet<>();

        if (countryString.isEmpty())
        {
            return countrySet;
        }

        countrySet.addAll(Arrays.stream(countryString.split(",")).collect(Collectors.toSet()));
        return countrySet;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy