/* * Copyright (c) 2014 Oculus Info Inc. * http://www.oculusinfo.com/ * * Released under the MIT License. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.oculusinfo.binning.util; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.*; import com.oculusinfo.binning.TileData; import com.oculusinfo.binning.TileIndex; import com.oculusinfo.binning.io.serialization.DefaultTileSerializerFactoryProvider; import com.oculusinfo.binning.io.serialization.TileSerializer; import com.oculusinfo.binning.properties.TileIndexProperty; import com.oculusinfo.factory.ConfigurationException; import com.oculusinfo.factory.ConfigurationProperty; import com.oculusinfo.factory.properties.IntegerProperty; import com.oculusinfo.factory.properties.ListProperty; import com.oculusinfo.factory.properties.StringProperty; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.oculusinfo.binning.io.DefaultPyramidIOFactoryProvider; import com.oculusinfo.binning.io.PyramidIO; import com.oculusinfo.factory.ConfigurableFactory; import com.oculusinfo.factory.UberFactory; import com.oculusinfo.factory.providers.FactoryProvider; import com.oculusinfo.factory.providers.StandardUberFactoryProvider; /** * Copy a tile pyramid from one location to another. */ public class CopyPyramid<T> { public static void main (String[] args) { if (args.length > 0 && new File(args[0]).exists()) { File propFile = new File(args[0]); JSONObject props = null; try { props = readJSONFile(propFile); } catch (IOException | JSONException e1) { // Try to read as a properties file. try { Properties rawProps = new Properties(); rawProps.load(new FileInputStream(propFile)); props = JsonUtilities.propertiesObjToJSON(rawProps); } catch (FileNotFoundException e2) { System.err.println("Can't open properties file " + propFile); System.exit(1); } catch (IOException e2) { System.err.println("Error reading properties file " + propFile); System.exit(1); } } try { FactoryProvider<PyramidIO> pioFactoryProvider = getPyramidIOFactoryProvider(); FactoryProvider<TileSerializer<?>> tsFactoryProvider = getTileSerializerFactoryProvider(); ConfigurableFactory<? extends PyramidIO> sourceFactory = pioFactoryProvider.createFactory(Arrays.asList("source")); sourceFactory.readConfiguration(props); PyramidIO source = sourceFactory.produce(PyramidIO.class); ConfigurableFactory<? extends PyramidIO> destinationFactory = pioFactoryProvider.createFactory(Arrays.asList("destination")); destinationFactory.readConfiguration(props); PyramidIO destination = destinationFactory.produce(PyramidIO.class); ConfigurableFactory<? extends TileSerializer<?>> serializerFactory = tsFactoryProvider.createFactory(Arrays.asList("serializer")); serializerFactory.readConfiguration(props); TileSerializer<?> serializer = serializerFactory.produce(TileSerializer.class); ConfigurableFactory<CopyParameters> paramFactory = new CopyParametersFactory(null, Arrays.asList("copy")); paramFactory.readConfiguration(props); CopyParameters params = paramFactory.produce(CopyParameters.class); CopyPyramid<?> copier = new CopyPyramid(source, destination, serializer, params); System.out.println("Starting pyramid copy at "+new Date()); copier.copy(params._indices); System.out.println("Pyramid copy finished at "+new Date()); } catch (ConfigurationException e) { System.err.println("Error reading configuration:"); e.printStackTrace(); System.exit(1); } catch (IOException e) { System.err.println("Error copying tile pyramid:"); e.printStackTrace(); System.exit(1); } } else { System.out.println("Usage:"); System.out.println("\tCopyPyramid <property file>"); System.out.println("The property file should contain four root nodes:"); System.out.println("\tsource: The source pyramid's characteristics"); System.out.println("\tdestination: The destination pyramid's characteristics"); System.out.println("\tserializer: The tile serializer's characteristics"); System.out.println("\tlevels: A list of levels to copy."); System.out.println("\tindices: An optional list of tile indices to use to restrict the "+ "area of copied tiles. Tiles that don't intersect any of the indicated indices are "+ "not exported."); System.out.println("tileWidth and tileHeight may also be specified, but default to 256."); } } private PyramidIO _source; private PyramidIO _destination; private TileSerializer<T> _serializer; private CopyParameters _parameters; public CopyPyramid (PyramidIO source, PyramidIO destination, TileSerializer<T> serializer, CopyParameters parameters) { _source = source; _destination = destination; _serializer = serializer; _parameters = parameters; } public void copy (List<TileIndex> rootTiles) throws IOException { _source.initializeForRead(_parameters._sourceId, _parameters._width, _parameters._height, null); _destination.initializeForWrite(_parameters._destinationId); String metaData = _source.readMetaData(_parameters._sourceId); if (null != metaData) _destination.writeMetaData(_parameters._destinationId, metaData); List<TileIndex> toCopy = new ArrayList<>(); for (TileIndex root: rootTiles) { toCopy = copyUp(root, toCopy); toCopy = copyDown(root, toCopy); } doCopy(toCopy); } private List<TileIndex> copyUp (TileIndex root, List<TileIndex> indexBuffer) throws IOException { if (_parameters._minLevel <= root.getLevel()) { indexBuffer.add(root); indexBuffer = checkBuffer(indexBuffer); indexBuffer = copyUp(TileIndex.getParent(root), indexBuffer); } return indexBuffer; } private List<TileIndex> copyDown (TileIndex root, List<TileIndex> indexBuffer) throws IOException { if (_parameters._maxLevel >= root.getLevel()) { indexBuffer.add(root); indexBuffer = checkBuffer(indexBuffer); for (TileIndex child: TileIndex.getChildren(root)) { indexBuffer = copyDown(child, indexBuffer); } } return indexBuffer; } private List<TileIndex> checkBuffer (List<TileIndex> indexBuffer) throws IOException { if (indexBuffer.size() >= _parameters._blockSize) { doCopy(indexBuffer); return new ArrayList<>(); } else { return indexBuffer; } } private void doCopy(List<TileIndex> toCopy) throws IOException { List<TileData<T>> tiles = _source.readTiles(_parameters._sourceId, _serializer, toCopy); _destination.writeTiles(_parameters._destinationId, _serializer, tiles); } private static FactoryProvider<PyramidIO> getPyramidIOFactoryProvider () { Set<FactoryProvider<PyramidIO>> subFactories = new HashSet<>(); for (FactoryProvider<PyramidIO> subFactory: DefaultPyramidIOFactoryProvider.values()) subFactories.add(subFactory); final String defaultType = DefaultPyramidIOFactoryProvider.values()[0].name(); return new StandardUberFactoryProvider<PyramidIO>(subFactories) { @Override public ConfigurableFactory<? extends PyramidIO> createFactory(String name, ConfigurableFactory<?> parent, List<String> path) { return new UberFactory<PyramidIO>(name, PyramidIO.class, parent, path, createChildren(parent, path), defaultType); } }; } private static FactoryProvider<TileSerializer<?>> getTileSerializerFactoryProvider () { Set<FactoryProvider<TileSerializer<?>>> subFactories = new HashSet<>(); for (FactoryProvider<TileSerializer<?>> subFactory: DefaultTileSerializerFactoryProvider.values()) subFactories.add(subFactory); final String defaultType = DefaultPyramidIOFactoryProvider.values()[0].name(); final Class<TileSerializer<?>> factoryType = (Class) TileSerializer.class; return new StandardUberFactoryProvider<TileSerializer<?>>(subFactories) { @Override public ConfigurableFactory<? extends TileSerializer<?>> createFactory(String name, ConfigurableFactory<?> parent, List<String> path) { return new UberFactory<TileSerializer<?>>(name, factoryType, parent, path, createChildren(parent, path), defaultType); } }; } private static JSONObject readJSONFile (File file) throws IOException, JSONException { BufferedReader reader = new BufferedReader(new FileReader(file)); String line; StringBuffer whole = new StringBuffer(); while ((line = reader.readLine()) != null) { whole.append(line); } reader.close(); return new JSONObject(whole.toString()); } static class CopyParameters { String _sourceId; String _destinationId; List<TileIndex> _indices; int _width; int _height; int _minLevel; int _maxLevel; int _blockSize; CopyParameters (String sourceId, String destinationId, List<TileIndex> indices, int width, int height, int minLevel, int maxLevel, int blockSize) { _sourceId = sourceId; _destinationId = destinationId; if (null == indices || indices.isEmpty()) { _indices = Arrays.asList(new TileIndex(0, 0, 0, width, height)); } else { _indices = indices; } _width = width; _height = height; _minLevel = minLevel; _maxLevel = maxLevel; _blockSize = blockSize; } } private static class CopyParametersFactory extends ConfigurableFactory<CopyParameters> { private static StringProperty SOURCE_ID = new StringProperty("sourceId", "The id of the source pyramid", ""); private static StringProperty DESTINATION_ID = new StringProperty("destinationId", "The id of the destination pyramid", ""); private static ConfigurationProperty<List<TileIndex>> INDICES = new ListProperty<TileIndex>( new TileIndexProperty( "index", "The index of a root tile from which to look up and down the pyramid", new TileIndex(0, 0, 0) ), "indices", "A list of root tiles that describe a necessary containment area of what is to be "+ "exported. Default is to export the whole pyramid." ); private static IntegerProperty WIDTH = new IntegerProperty("width", "The tile width, in bins", 256); private static IntegerProperty HEIGHT = new IntegerProperty("height", "The tile height, in bins", 256); private static IntegerProperty MIN_LEVEL = new IntegerProperty("minimum", "The numerically minimum level to copy", 0); private static IntegerProperty MAX_LEVEL = new IntegerProperty("maximum", "The numerically maximum level to copy", 18); private static IntegerProperty BLOCK_SIZE = new IntegerProperty("blockSize", "The number of tiles to copy at a time", 100); protected CopyParametersFactory(ConfigurableFactory<?> parent, List<String> path) { this(null, parent, path); } protected CopyParametersFactory(String name, ConfigurableFactory<?> parent, List<String> path) { super(name, CopyParameters.class, parent, path); addProperty(SOURCE_ID); addProperty(DESTINATION_ID); addProperty(INDICES); addProperty(WIDTH); addProperty(HEIGHT); addProperty(MIN_LEVEL, Arrays.asList("level")); addProperty(MAX_LEVEL, Arrays.asList("level")); addProperty(BLOCK_SIZE); } @Override protected CopyParameters create() throws ConfigurationException { return new CopyParameters( getPropertyValue(SOURCE_ID), getPropertyValue(DESTINATION_ID), getPropertyValue(INDICES), getPropertyValue(WIDTH), getPropertyValue(HEIGHT), getPropertyValue(MIN_LEVEL), getPropertyValue(MAX_LEVEL), getPropertyValue(BLOCK_SIZE) ); } } }