/* The MIT License (MIT) * * Copyright (c) 2015 Reinventing Geospatial, Inc. * * 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.rgi.suite.tilestoreadapter.geopackage; import java.io.File; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.activation.MimeType; import javax.activation.MimeTypeParseException; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JTextField; import com.rgi.common.Range; import com.rgi.common.tile.scheme.TileScheme; import com.rgi.common.tile.scheme.ZoomTimesTwo; import com.rgi.common.util.FileUtility; import com.rgi.store.tiles.TileHandle; import com.rgi.store.tiles.TileStoreException; import com.rgi.store.tiles.TileStoreReader; import com.rgi.store.tiles.TileStoreWriter; import com.rgi.store.tiles.geopackage.GeoPackageWriter; import com.rgi.suite.Settings; import com.rgi.suite.tilestoreadapter.ImageFormatTileStoreAdapter; import com.rgi.suite.tilestoreadapter.TileStoreWriterAdapter; /** * {@link TileStoreWriterAdapter} for the GeoPackage tile store format * * @author Luke Lambert * */ public class GeoPackageTileStoreWriterAdapter extends ImageFormatTileStoreAdapter { private static final String GeoPackageOutputLocationSettingName = "ui.gpkg.outputLocation"; private static final String DefaultGeoPackageOutputLocation = System.getProperty("user.home"); private final JTextField tileSetName = new JTextField(); private final JTextField tileSetDescription = new JTextField(); private final JTextField filename = new JTextField(); private final JButton outputFileNameSelector = new JButton("\u2026"); private final Collection<Collection<JComponent>> writerParameterControls = Arrays.asList(Arrays.asList(new JLabel("Image format:"), this.imageFormat), Arrays.asList(new JLabel("Compression type:"), this.imageCompressionType), Arrays.asList(new JLabel("Compression quality:"), this.compressionQuality), Arrays.asList(new JLabel("Tile set name:"), this.tileSetName), Arrays.asList(new JLabel("Tile set description:"), this.tileSetDescription), Arrays.asList(new JLabel("File name:"), this.filename, this.outputFileNameSelector)); /** * Constructor * * @param settings * Handle to the application's settings object */ public GeoPackageTileStoreWriterAdapter(final Settings settings) { super(settings); this.outputFileNameSelector.addActionListener(e -> { final String startDirectory = this.settings.get(GeoPackageOutputLocationSettingName, DefaultGeoPackageOutputLocation); final JFileChooser fileChooser = new JFileChooser(new File(startDirectory)); fileChooser.setMultiSelectionEnabled(false); fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); final int option = fileChooser.showOpenDialog(null); if(option == JFileChooser.APPROVE_OPTION) { final File file = fileChooser.getSelectedFile(); if(file.isDirectory()) { final File oldFileSelection = new File(this.filename.getText()); this.filename.setText(String.format("%s%c%s", file.getPath(), File.separatorChar, (oldFileSelection.isDirectory() ? "geopackage.gpkg" : oldFileSelection.getName()))); } else { this.filename.setText(file.getPath()); } this.settings.set(GeoPackageOutputLocationSettingName, (new File(this.filename.getText()).getParent())); this.settings.save(); } }); } @Override public String toString() { return "GeoPackage"; } @Override protected Collection<MimeType> getSupportedImageFormats() { return GeoPackageWriter.SupportedImageFormats; } @Override protected MimeType getInitialImageFormat() { try { return new MimeType("image/png"); } catch(final MimeTypeParseException ex) { // This can't happen so long as GeoPackages support PNGs return null; } } @Override public void hint(final File inputFile) throws TileStoreException { String name = FileUtility.nameWithoutExtension(inputFile); name = name.replaceAll("[^_a-zA-Z0-9]", "_"); name = name.replaceAll("^gpkg_", ""); if(name.matches("^[0-9].*")) { name = "_" + name; } this.filename.setText(FileUtility.appendForUnique(String.format("%s%c%s.gpkg", this.settings.get(GeoPackageOutputLocationSettingName, DefaultGeoPackageOutputLocation), File.separatorChar, name))); this.tileSetName.setText(name); this.tileSetDescription.setText(String.format("Tile store %s packaged by %s at %s", name, System.getProperty("user.name"), new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()))); } @Override public Collection<Collection<JComponent>> getWriterParameterControls() { return this.writerParameterControls; } @Override public TileStoreWriter getTileStoreWriter(final TileStoreReader tileStoreReader) throws TileStoreException { final File file = new File(this.filename.getText()); final String parent = file.getParent(); if(parent != null) { this.settings.set(GeoPackageOutputLocationSettingName, parent); this.settings.save(); } final MimeType mimeType = (MimeType)this.imageFormat.getSelectedItem(); return new GeoPackageWriter(file, tileStoreReader.getCoordinateReferenceSystem(), this.tileSetName.getText(), this.tileSetName.getText(), this.tileSetDescription.getText(), tileStoreReader.getBounds(), getRelativeZoomTimesTwoTileScheme(tileStoreReader), mimeType, this.getImageWriteParameter()); } @Override public void removeStore() throws TileStoreException { final File file = new File(this.filename.getText()); if(file.delete() == false) { throw new TileStoreException(String.format("Unable to remove file '%s'", file.getAbsolutePath())); } } private static TileScheme getRelativeZoomTimesTwoTileScheme(final TileStoreReader tileStoreReader) throws TileStoreException { final Set<Integer> zoomLevels = tileStoreReader.getZoomLevels(); if(zoomLevels.size() == 0) { throw new TileStoreException("Input tile store contains no zoom levels"); } final Range<Integer> zoomLevelRange = new Range<>(zoomLevels, Integer::compare); final List<TileHandle> tiles = tileStoreReader.stream(zoomLevelRange.getMinimum()).collect(Collectors.toList()); final Range<Integer> columnRange = new Range<>(tiles, tile -> tile.getColumn(), Integer::compare); final Range<Integer> rowRange = new Range<>(tiles, tile -> tile.getRow(), Integer::compare); final int minZoomLevelMatrixWidth = columnRange.getMaximum() - columnRange.getMinimum() + 1; final int minZoomLevelMatrixHeight = rowRange.getMaximum() - rowRange.getMinimum() + 1; return new ZoomTimesTwo(zoomLevelRange.getMinimum(), zoomLevelRange.getMaximum(), minZoomLevelMatrixWidth, minZoomLevelMatrixHeight); } }