diff --git a/nbproject/configs/BulkDownloadCli.properties b/nbproject/configs/BulkDownloadCli.properties new file mode 100644 index 0000000000..67f431cdef --- /dev/null +++ b/nbproject/configs/BulkDownloadCli.properties @@ -0,0 +1 @@ +main.class=gov.nasa.worldwindx.examples.util.BulkDownloadCli diff --git a/src/config/Earth/OpenStreetMap2.xml b/src/config/Earth/OpenStreetMap2.xml new file mode 100644 index 0000000000..7ef7a8f339 --- /dev/null +++ b/src/config/Earth/OpenStreetMap2.xml @@ -0,0 +1,74 @@ + + + + + + Open Street Map + + https://worldwind47.arc.nasa.gov/mapcache + https://worldwind47.arc.nasa.gov/mapcache + osm + + 0.40 + true + + 01 11 2022 22:52:00 GMT + Earth/OpenStreetMap2 + image/jpeg + + image/jpeg + + .jpg + + + + + + + + + + + + + + + + + + + false + false + true + + + + + diff --git a/src/gov/nasa/worldwind/layers/BasicTiledImageLayer.java b/src/gov/nasa/worldwind/layers/BasicTiledImageLayer.java index 1b73e7d839..57dffc22bb 100644 --- a/src/gov/nasa/worldwind/layers/BasicTiledImageLayer.java +++ b/src/gov/nasa/worldwind/layers/BasicTiledImageLayer.java @@ -55,7 +55,8 @@ public class BasicTiledImageLayer extends TiledImageLayer implements BulkRetrievable { protected final Object fileLock = new Object(); - + protected long averageFileSize=BasicTiledImageLayerBulkDownloader.DEFAULT_AVERAGE_FILE_SIZE; // The average file size of an individual tile to use for size estimates. + // Layer resource properties. protected static final int RESOURCE_ID_OGC_CAPABILITIES = 1; @@ -523,12 +524,18 @@ public long getEstimatedMissingDataSize(Sector sector, double resolution, FileSt BasicTiledImageLayerBulkDownloader downloader = new BasicTiledImageLayerBulkDownloader(this, sector, resolution, fileStore != null ? fileStore : this.getDataFileStore(), null); - + downloader.setAverageFileSize(this.averageFileSize); return downloader.getEstimatedMissingDataSize(); } - // *** Tile download *** - // *** Tile download *** + /** + * Set the average size of a tile image to use in estimating download sizes. + * @param value The average size of a tile image. + */ + public void setAverageFileSize(long value) { + this.averageFileSize=value; + } + // *** Tile download *** protected void retrieveTexture(TextureTile tile, DownloadPostProcessor postProcessor) diff --git a/src/gov/nasa/worldwind/layers/BasicTiledImageLayerBulkDownloader.java b/src/gov/nasa/worldwind/layers/BasicTiledImageLayerBulkDownloader.java index fd343df397..8139a15e82 100644 --- a/src/gov/nasa/worldwind/layers/BasicTiledImageLayerBulkDownloader.java +++ b/src/gov/nasa/worldwind/layers/BasicTiledImageLayerBulkDownloader.java @@ -58,6 +58,7 @@ public class BasicTiledImageLayerBulkDownloader extends BulkRetrievalThread protected final BasicTiledImageLayer layer; protected final int level; protected ArrayList missingTiles; + protected long averageFileSize=DEFAULT_AVERAGE_FILE_SIZE; /** * Constructs a downloader to retrieve imagery not currently available in the WorldWind file cache. @@ -472,7 +473,7 @@ public boolean accept(File file) } } - Long averageTileSize = DEFAULT_AVERAGE_FILE_SIZE; + Long averageTileSize = this.averageFileSize; if (count > 0 && size > 0) { averageTileSize = size / count; @@ -481,6 +482,14 @@ public boolean accept(File file) return averageTileSize; } + + /** + * Set the average size of a tile image to use in estimating download sizes. + * @param value The average size of a tile image. + */ + public void setAverageFileSize(long value) { + this.averageFileSize=value; + } protected long computeAverageTileSize(File dir) { diff --git a/src/gov/nasa/worldwind/wms/WMSTiledImageLayer.java b/src/gov/nasa/worldwind/wms/WMSTiledImageLayer.java index 315533bfee..7a012a3524 100644 --- a/src/gov/nasa/worldwind/wms/WMSTiledImageLayer.java +++ b/src/gov/nasa/worldwind/wms/WMSTiledImageLayer.java @@ -196,18 +196,16 @@ public URLBuilder(AVList params) String version = params.getStringValue(AVKey.WMS_VERSION); String coordSystemKey; - String defaultCS; + String defaultCS = "EPSG:4326"; if (version == null || WWUtil.compareVersion(version, "1.3.0") >= 0) { this.wmsVersion = MAX_VERSION; coordSystemKey = "&crs="; - defaultCS = "CRS:84"; // would like to do EPSG:4326 but that's incompatible with our old WMS server, see WWJ-474 } else { this.wmsVersion = version; coordSystemKey = "&srs="; - defaultCS = "EPSG:4326"; } String coordinateSystem = params.getStringValue(AVKey.COORDINATE_SYSTEM); @@ -220,7 +218,6 @@ public URL getURL(Tile tile, String altImageFormat) throws MalformedURLException if (this.URLTemplate == null) { sb = new StringBuffer(WWXML.fixGetMapString(tile.getLevel().getService())); - if (!sb.toString().toLowerCase().contains("service=wms")) sb.append("service=WMS"); sb.append("&request=GetMap"); diff --git a/src/gov/nasa/worldwindx/examples/util/BulkDownloadCli.java b/src/gov/nasa/worldwindx/examples/util/BulkDownloadCli.java new file mode 100644 index 0000000000..7d6d6bd7ec --- /dev/null +++ b/src/gov/nasa/worldwindx/examples/util/BulkDownloadCli.java @@ -0,0 +1,192 @@ +/* + * Copyright 2006-2009, 2017, 2020 United States Government, as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All rights reserved. + * + * The NASA World Wind Java (WWJ) platform is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * NASA World Wind Java (WWJ) also contains the following 3rd party Open Source + * software: + * + * Jackson Parser – Licensed under Apache 2.0 + * GDAL – Licensed under MIT + * JOGL – Licensed under Berkeley Software Distribution (BSD) + * Gluegen – Licensed under Berkeley Software Distribution (BSD) + * + * A complete listing of 3rd Party software notices and licenses included in + * NASA World Wind Java (WWJ) can be found in the WorldWindJava-v2.2 3rd-party + * notices and licenses PDF found in code directory. + */ +package gov.nasa.worldwindx.examples.util; + +import gov.nasa.worldwind.cache.BasicDataFileStore; +import gov.nasa.worldwind.geom.LatLon; +import gov.nasa.worldwind.geom.Sector; +import gov.nasa.worldwind.globes.Earth; +import gov.nasa.worldwind.globes.Globe; +import gov.nasa.worldwind.layers.BasicLayerFactory; +import gov.nasa.worldwind.layers.BasicTiledImageLayer; +import gov.nasa.worldwind.layers.BasicTiledImageLayerBulkDownloader; +import gov.nasa.worldwind.retrieve.Progress; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.StringTokenizer; + +public class BulkDownloadCli { + + private static class FileUtils extends SimpleFileVisitor { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException ex) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + + static void deleteDirectory(File f) throws IOException { + Files.walkFileTree(Path.of(f.getAbsolutePath()), new FileUtils()); + } + } + + private static final String VERSION = "1.1"; + private static final long OSM_AVERAGE_TILE_SIZE = 15000; + + public static String makeSizeDescription(long size) { + double sizeInMegaBytes = size / 1024 / 1024; + if (sizeInMegaBytes < 1024) { + return String.format("%,.1f MB", sizeInMegaBytes); + } else if (sizeInMegaBytes < 1024 * 1024) { + return String.format("%,.1f GB", sizeInMegaBytes / 1024); + } + return String.format("%,.1f TB", sizeInMegaBytes / 1024 / 1024); + } + + public static void cliDownload(String[] args) { + System.out.println("WorldWind Bulk Download Tool v" + VERSION); + Globe globe = new Earth(); + String usage = "Usage: BulkDownload -sector [centerLat,centerLon,radius meters] -path [path for download] -estimate"; + String outputPath = null; + Sector sector = null; + boolean estimate = false; + int i = 0; + while (i < args.length) { + switch (args[i]) { + case "-help": + System.out.println(usage); + return; + case "-sector": + i++; + StringTokenizer st = new StringTokenizer(args[i], ","); + if (st.countTokens() != 3) { + System.out.println("Error: Invalid sector specification."); + System.out.println(usage); + return; + } + double lat = Double.parseDouble(st.nextToken()); + double lon = Double.parseDouble(st.nextToken()); + double radius = Double.parseDouble(st.nextToken()); + sector = Sector.boundingSector(globe, LatLon.fromDegrees(lat, lon), radius); + break; + case "-path": + i++; + outputPath = args[i]; + break; + case "-estimate": + estimate = true; + break; + default: + System.out.println("Unknown argument: " + args[i]); + System.out.println(usage); + break; + } + i++; + } + + if (sector == null) { + System.out.println("Error: Sector not specified."); + System.out.println(usage); + return; + } + + System.out.println("Sector: " + sector); + BasicLayerFactory factory = new BasicLayerFactory(); + BasicTiledImageLayer layer = (BasicTiledImageLayer) factory.createFromConfigSource("config/Earth/OpenStreetMap2.xml", null); + layer.setAverageFileSize(OSM_AVERAGE_TILE_SIZE); + if (estimate) { + System.out.println(layer.getName() + " estimated download size: " + makeSizeDescription(layer.getEstimatedMissingDataSize(sector, 0))); + return; + } + + if (outputPath == null) { + System.out.println("Error: Output path not specified."); + System.out.println(usage); + return; + } + + try { + String earthPath = outputPath + "/Earth"; + String osmPath = earthPath + "/OpenStreetMap2"; + File previousTilePath = new File(earthPath); + File previousOsmPath = new File(osmPath); + if (previousTilePath.exists() && previousTilePath.isDirectory() && previousOsmPath.exists()) { + FileUtils.deleteDirectory(previousTilePath); + } + + BasicDataFileStore cache = new BasicDataFileStore(new File(outputPath)); + BasicTiledImageLayerBulkDownloader downloadThread = (BasicTiledImageLayerBulkDownloader) layer.makeLocal(sector, 0, cache, null); + downloadThread.setAverageFileSize(OSM_AVERAGE_TILE_SIZE); + Progress progress = downloadThread.getProgress(); + System.out.println("Downloading " + layer.getName() + " Tiles"); + int lastPercent = -1; + long lastCurrentSize = -1; + while (downloadThread.isAlive()) { + int percent = 0; + if (progress.getTotalCount() > 0) { + percent = (int) ((float) progress.getCurrentCount() / progress.getTotalCount() * 100f); + } + if (lastPercent != percent || lastCurrentSize != progress.getCurrentSize()) { + lastPercent = percent; + lastCurrentSize = progress.getCurrentSize(); + String text = percent + "% "; + text += " (" + makeSizeDescription(lastCurrentSize) + + " / " + makeSizeDescription(progress.getTotalSize()) + + ")"; + System.out.println(text); + } + Thread.sleep(1000); + } + int nRemoveLevels = 9; + for (i = 0; i < nRemoveLevels; i++) { + File levelPath = new File(osmPath + "/" + i); + if (levelPath.exists() && levelPath.isDirectory()) { + FileUtils.deleteDirectory(levelPath); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public static void main(String[] args) { + cliDownload(args); + } +}