X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2Fsave%2FBaseImageConfigDialog.java;h=026556924f06408d3b735dcce2dce98ecf1757e6;hp=514ebd79ec46d8c8fa5203d9f460872ddbc66d49;hb=326f489e36aa7f235bc19409a57bf4955cd50f24;hpb=8c8868ae29b3252f02e094c02307384cf61ba667 diff --git a/tim/prune/save/BaseImageConfigDialog.java b/tim/prune/save/BaseImageConfigDialog.java index 514ebd7..0265569 100644 --- a/tim/prune/save/BaseImageConfigDialog.java +++ b/tim/prune/save/BaseImageConfigDialog.java @@ -1,7 +1,6 @@ package tim.prune.save; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.Component; import java.awt.FlowLayout; import java.awt.GridLayout; @@ -18,22 +17,23 @@ import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JProgressBar; -import tim.prune.DataSubscriber; import tim.prune.I18nManager; import tim.prune.config.Config; import tim.prune.data.Track; import tim.prune.gui.map.MapSource; import tim.prune.gui.map.MapSourceLibrary; +import tim.prune.threedee.ImageDefinition; /** * Dialog to let you choose the parameters for a base image - * (source and zoom) + * (source and zoom) including preview */ public class BaseImageConfigDialog implements Runnable { /** Parent to notify */ - private DataSubscriber _parent = null; + private BaseImageConsumer _parent = null; /** Parent dialog for position */ private JDialog _parentDialog = null; /** Track to use for preview image */ @@ -45,25 +45,27 @@ public class BaseImageConfigDialog implements Runnable /** Panel to hold the other controls */ private JPanel _mainPanel = null; /** Dropdown for map source */ - private JComboBox _mapSourceDropdown = null; + private JComboBox _mapSourceDropdown = null; /** Dropdown for zoom levels */ - private JComboBox _zoomDropdown = null; - /** Warning label that image is incomplete */ - private JLabel _imageIncompleteLabel = null; + private JComboBox _zoomDropdown = null; + /** Button to trigger a download of the missing map tiles */ + private JButton _downloadTilesButton = null; + /** Progress bar for downloading additional tiles */ + private JProgressBar _progressBar = null; /** Label for number of tiles found */ private JLabel _tilesFoundLabel = null; /** Label for image size in pixels */ private JLabel _imageSizeLabel = null; /** Image preview panel */ private ImagePreviewPanel _previewPanel = null; + /** Grouter, used to avoid regenerating images */ + private MapGrouter _grouter = new MapGrouter(); /** OK button, needs to be enabled/disabled */ private JButton _okButton = null; /** Flag for rebuilding dialog, don't bother refreshing and recalculating */ private boolean _rebuilding = false; /** Cached values to allow cancellation of dialog */ - private boolean _useImage = false; - private int _sourceIndex = 0; - private int _zoomLevel = 0; + private ImageDefinition _imageDef = new ImageDefinition(); /** @@ -72,7 +74,7 @@ public class BaseImageConfigDialog implements Runnable * @param inParentDialog parent dialog * @param inTrack track object */ - public BaseImageConfigDialog(DataSubscriber inParent, JDialog inParentDialog, Track inTrack) + public BaseImageConfigDialog(BaseImageConsumer inParent, JDialog inParentDialog, Track inTrack) { _parent = inParent; _parentDialog = inParentDialog; @@ -83,6 +85,17 @@ public class BaseImageConfigDialog implements Runnable _track = inTrack; } + /** + * @param inDefinition image definition object from previous dialog + */ + public void setImageDefinition(ImageDefinition inDefinition) + { + _imageDef = inDefinition; + if (_imageDef == null) { + _imageDef = new ImageDefinition(); + } + } + /** * Begin the function */ @@ -110,26 +123,29 @@ public class BaseImageConfigDialog implements Runnable private void initDialog() { _rebuilding = true; - _useImageCheckbox.setSelected(_useImage); + _useImageCheckbox.setSelected(_imageDef.getUseImage()); // Populate the dropdown of map sources from the library in case it has changed _mapSourceDropdown.removeAllItems(); for (int i=0; i= _mapSourceDropdown.getItemCount()) { - _sourceIndex = 0; + int sourceIndex = _imageDef.getSourceIndex(); + if (sourceIndex < 0 || sourceIndex >= _mapSourceDropdown.getItemCount()) { + sourceIndex = 0; } - _mapSourceDropdown.setSelectedIndex(_sourceIndex); + _mapSourceDropdown.setSelectedIndex(sourceIndex); // Zoom level - if (_useImage) + int zoomLevel = _imageDef.getZoom(); + if (_imageDef.getUseImage()) { for (int i=0; i<_zoomDropdown.getItemCount(); i++) { String item = _zoomDropdown.getItemAt(i).toString(); try { - if (Integer.parseInt(item) == _zoomLevel) { + if (Integer.parseInt(item) == zoomLevel) + { _zoomDropdown.setSelectedIndex(i); break; } @@ -154,8 +170,11 @@ public class BaseImageConfigDialog implements Runnable currentZoom = Integer.parseInt(_zoomDropdown.getSelectedItem().toString()); } catch (Exception nfe) {} + // First time in, the dropdown might be empty but we still might have a zoom in the definition + if (_zoomDropdown.getItemCount() == 0) { + currentZoom = _imageDef.getZoom(); + } // Get the extent of the track so we can work out how big the images are going to be for each zoom level - // System.out.println("Ranges are: x=" + _track.getXRange().getRange() + ", y=" + _track.getYRange().getRange()); final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange()); int zoomToSelect = -1; @@ -248,24 +267,10 @@ public class BaseImageConfigDialog implements Runnable /** - * @return true if image has been selected + * @return image definition object */ - public boolean useImage() { - return _useImage; - } - - /** - * @return index of selected image source - */ - public int getSourceIndex() { - return _sourceIndex; - } - - /** - * @return selected zoom level - */ - public int getZoomLevel() { - return _zoomLevel; + public ImageDefinition getImageDefinition() { + return _imageDef; } /** @@ -296,7 +301,7 @@ public class BaseImageConfigDialog implements Runnable JLabel sourceLabel = new JLabel(I18nManager.getText("dialog.baseimage.mapsource") + ": "); sourceLabel.setHorizontalAlignment(JLabel.RIGHT); controlsPanel.add(sourceLabel); - _mapSourceDropdown = new JComboBox(); + _mapSourceDropdown = new JComboBox(); _mapSourceDropdown.addItem("name of map source"); // Add listener to dropdown to change zoom levels _mapSourceDropdown.addActionListener(new ActionListener() { @@ -309,7 +314,7 @@ public class BaseImageConfigDialog implements Runnable JLabel zoomLabel = new JLabel(I18nManager.getText("dialog.baseimage.zoom") + ": "); zoomLabel.setHorizontalAlignment(JLabel.RIGHT); controlsPanel.add(zoomLabel); - _zoomDropdown = new JComboBox(); + _zoomDropdown = new JComboBox(); // Add action listener to enable ok button when zoom changed _zoomDropdown.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { @@ -331,10 +336,21 @@ public class BaseImageConfigDialog implements Runnable // Label panel on right JPanel labelPanel = new JPanel(); labelPanel.setLayout(new BorderLayout()); - _imageIncompleteLabel = new JLabel(I18nManager.getText("dialog.baseimage.incomplete")); - _imageIncompleteLabel.setForeground(Color.RED); - _imageIncompleteLabel.setVisible(false); - labelPanel.add(_imageIncompleteLabel, BorderLayout.NORTH); + JPanel downloadPanel = new JPanel(); + downloadPanel.setLayout(new BorderLayout(4, 4)); + _downloadTilesButton = new JButton(I18nManager.getText("button.load")); + _downloadTilesButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + downloadRemainingTiles(); + } + }); + _downloadTilesButton.setVisible(false); + downloadPanel.add(_downloadTilesButton, BorderLayout.NORTH); + _progressBar = new JProgressBar(); + _progressBar.setIndeterminate(true); + _progressBar.setVisible(false); + downloadPanel.add(_progressBar, BorderLayout.SOUTH); + labelPanel.add(downloadPanel, BorderLayout.NORTH); JPanel labelGridPanel = new JPanel(); labelGridPanel.setLayout(new GridLayout(0, 2, 10, 4)); labelGridPanel.add(new JLabel(I18nManager.getText("dialog.baseimage.tiles") + ": ")); @@ -401,7 +417,7 @@ public class BaseImageConfigDialog implements Runnable private void updateImagePreview() { // Clear labels - _imageIncompleteLabel.setVisible(false); + _downloadTilesButton.setVisible(false); _tilesFoundLabel.setText(""); _imageSizeLabel.setText(""); if (_useImageCheckbox.isSelected() && _mapSourceDropdown.getSelectedIndex() >= 0 @@ -423,17 +439,11 @@ public class BaseImageConfigDialog implements Runnable private void storeValues() { // Store values of controls in variables - _useImage = _useImageCheckbox.isSelected(); - _sourceIndex = _mapSourceDropdown.getSelectedIndex(); - try { - String zoomStr = _zoomDropdown.getSelectedItem().toString(); - _zoomLevel = Integer.parseInt(zoomStr); - } - catch (Exception nfe) { - _zoomLevel = 0; - } - // Call parent to retrieve values - _parent.dataUpdated(DataSubscriber.ALL); + _imageDef.setUseImage(_useImageCheckbox.isSelected(), + _mapSourceDropdown.getSelectedIndex(), + getSelectedZoomLevel()); + // Inform parent that details have changed + _parent.baseImageChanged(); } /** @@ -447,16 +457,11 @@ public class BaseImageConfigDialog implements Runnable final int zoomIndex = _zoomDropdown.getSelectedIndex(); if (!_useImageCheckbox.isSelected() || mapIndex < 0 || zoomIndex < 0) {return;} - // Get the map source and zoom level + // Get the map source from the index MapSource mapSource = MapSourceLibrary.getSource(mapIndex); - int zoomLevel = 0; - try { - zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString()); - } - catch (Exception e) {} // Use the Grouter to create an image (slow, blocks thread) - GroutedImage groutedImage = MapGrouter.createMapImage(_track, mapSource, zoomLevel); + GroutedImage groutedImage = _grouter.createMapImage(_track, mapSource, getSelectedZoomLevel()); // If the dialog hasn't changed, pass the generated image to the preview panel if (_useImageCheckbox.isSelected() @@ -465,8 +470,11 @@ public class BaseImageConfigDialog implements Runnable && groutedImage != null) { _previewPanel.setImage(groutedImage); + final int numTilesRemaining = groutedImage.getNumTilesTotal() - groutedImage.getNumTilesUsed(); + final boolean offerDownload = numTilesRemaining > 0 && numTilesRemaining < 50; // Set values of labels - _imageIncompleteLabel.setVisible(!groutedImage.isComplete()); + _downloadTilesButton.setVisible(offerDownload); + _downloadTilesButton.setEnabled(offerDownload); _tilesFoundLabel.setText(groutedImage.getNumTilesUsed() + " / " + groutedImage.getNumTilesTotal()); if (groutedImage.getImageSize() > 0) { _imageSizeLabel.setText("" + groutedImage.getImageSize()); @@ -479,17 +487,78 @@ public class BaseImageConfigDialog implements Runnable { _previewPanel.setImage(null); // Clear labels - _imageIncompleteLabel.setVisible(false); + _downloadTilesButton.setVisible(false); _tilesFoundLabel.setText(""); _imageSizeLabel.setText(""); } } + /** + * @return zoom level selected in the dropdown + */ + private int getSelectedZoomLevel() + { + int zoomLevel = 0; + try { + zoomLevel = Integer.parseInt(_zoomDropdown.getSelectedItem().toString()); + } + catch (Exception e) { + System.err.println("Exception: " + e.getClass().getName() + " : " + e.getMessage()); + } + return zoomLevel; + } + /** * @return true if any map data has been found for the image */ public boolean getFoundData() { - return _useImage && _zoomLevel > 0 && _previewPanel != null && _previewPanel.getTilesFound(); + return _imageDef.getUseImage() && _imageDef.getZoom() > 0 + && _previewPanel != null && _previewPanel.getTilesFound(); + } + + /** + * @return true if selected zoom is valid for the current track (based only on pixel size) + */ + public boolean isSelectedZoomValid() + { + final double xyExtent = Math.max(_track.getXRange().getRange(), _track.getYRange().getRange()); + // How many pixels does this give? + final int zoomFactor = 1 << _imageDef.getZoom(); + final int pixCount = (int) (xyExtent * zoomFactor * 256); + return (pixCount > 100 // less than this isn't worth it + && pixCount < 4000); // don't want to run out of memory + } + + /** + * @return the map grouter for retrieval of generated image + */ + public MapGrouter getGrouter() + { + return _grouter; + } + + /** + * @return method triggered by "download" button, to asynchronously download the missing tiles + */ + private void downloadRemainingTiles() + { + _downloadTilesButton.setEnabled(false); + new Thread(new Runnable() { + public void run() + { + _progressBar.setVisible(true); + // Use a grouter to get all tiles from the TileManager, including downloading + MapGrouter grouter = new MapGrouter(); + final int mapIndex = _mapSourceDropdown.getSelectedIndex(); + if (!_useImageCheckbox.isSelected() || mapIndex < 0) {return;} + MapSource mapSource = MapSourceLibrary.getSource(mapIndex); + grouter.createMapImage(_track, mapSource, getSelectedZoomLevel(), true); + _progressBar.setVisible(false); + // And then refresh the dialog + _grouter.clearMapImage(); + updateImagePreview(); + } + }).start(); } }