package com.revolsys.swing.map.layer; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; import java.awt.TextArea; import java.awt.Window; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Predicate; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.swingx.ScrollableSizeHint; import org.jdesktop.swingx.VerticalLayout; import com.revolsys.beans.KeyedPropertyChangeEvent; import com.revolsys.beans.PropertyChangeSupportProxy; import com.revolsys.collection.EmptyReference; import com.revolsys.collection.map.LinkedHashMapEx; import com.revolsys.collection.map.MapEx; import com.revolsys.collection.map.MapSerializerMap; import com.revolsys.datatype.DataType; import com.revolsys.datatype.DataTypes; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.esri.EsriCoordinateSystems; import com.revolsys.geometry.cs.esri.EsriCsWktWriter; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.io.BaseCloseable; import com.revolsys.io.FileUtil; import com.revolsys.io.map.MapObjectFactory; import com.revolsys.io.map.MapSerializer; import com.revolsys.logging.Logs; import com.revolsys.properties.BaseObjectWithProperties; import com.revolsys.swing.Borders; import com.revolsys.swing.Icons; import com.revolsys.swing.Panels; import com.revolsys.swing.SwingUtil; import com.revolsys.swing.component.BasePanel; import com.revolsys.swing.component.TabbedValuePanel; import com.revolsys.swing.component.ValueField; import com.revolsys.swing.field.Field; import com.revolsys.swing.layout.GroupLayouts; import com.revolsys.swing.listener.BeanPropertyListener; import com.revolsys.swing.map.MapPanel; import com.revolsys.swing.map.ProjectFrame; import com.revolsys.swing.map.ProjectFramePanel; import com.revolsys.swing.map.layer.menu.TreeItemScaleMenu; import com.revolsys.swing.map.layer.record.style.panel.LayerStylePanel; import com.revolsys.swing.menu.MenuFactory; import com.revolsys.swing.menu.Menus; import com.revolsys.swing.parallel.Invoke; import com.revolsys.swing.preferences.PreferencesDialog; import com.revolsys.util.Booleans; import com.revolsys.util.CaseConverter; import com.revolsys.util.OS; import com.revolsys.util.Property; import com.revolsys.util.ToolTipProxy; import com.revolsys.value.ThreadBooleanValue; public abstract class AbstractLayer extends BaseObjectWithProperties implements Layer, PropertyChangeListener, PropertyChangeSupportProxy, ProjectFramePanel, ToolTipProxy { public static final Icon ICON_LAYER = Icons.getIcon("map"); private static final AtomicLong ID_GEN = new AtomicLong(); public static final String PLUGIN_TABLE_VIEW = "tableView"; public static final String PREFERENCE_NEW_LAYERS_SHOW_TABLE_VIEW = "newLayersShowTableView"; public static final String PREFERENCE_NEW_LAYERS_VISIBLE = "newLayersVisible"; public static final String PREFERENCE_PATH = "/com/revolsys/gis/layer"; static { final MenuFactory menu = MenuFactory.getMenu(AbstractLayer.class); Menus.addMenuItem(menu, "zoom", "Zoom to Layer", "magnifier", AbstractLayer::isZoomToLayerEnabled, AbstractLayer::zoomToLayer, true); final Predicate<AbstractLayer> hasGeometry = AbstractLayer::isHasGeometry; menu.addComponentFactory("scale", new TreeItemScaleMenu<>(true, hasGeometry, AbstractLayer::getMinimumScale, AbstractLayer::setMinimumScale)); menu.addComponentFactory("scale", new TreeItemScaleMenu<>(false, hasGeometry, AbstractLayer::getMaximumScale, AbstractLayer::setMaximumScale)); final Predicate<AbstractLayer> exists = AbstractLayer::isExists; Menus.<AbstractLayer> addMenuItem(menu, "refresh", "Refresh", "arrow_refresh", AbstractLayer::refreshAll, true); Menus.<AbstractLayer> addMenuItem(menu, "layer", "Delete", "delete", AbstractLayer::deleteWithConfirm, false); Menus.<AbstractLayer> addMenuItem(menu, "layer", "Layer Properties", "information", exists, AbstractLayer::showProperties, false); final PreferencesDialog preferencesDialog = PreferencesDialog.get(); preferencesDialog.addPreference("Layers", "com.revolsys.gis", PREFERENCE_PATH, PREFERENCE_NEW_LAYERS_VISIBLE, DataTypes.BOOLEAN, false); preferencesDialog.addPreference("Layers", "com.revolsys.gis", PREFERENCE_PATH, PREFERENCE_NEW_LAYERS_SHOW_TABLE_VIEW, DataTypes.BOOLEAN, false); } private String errorMessage; private boolean deleted = false; private boolean open = false; private PropertyChangeListener beanPropertyListener = new BeanPropertyListener(this); private BoundingBox boundingBox = BoundingBox.empty(); private boolean editable = false; private ThreadBooleanValue eventsEnabled = new ThreadBooleanValue(true); private boolean exists = true; private GeometryFactory geometryFactory; private Icon icon = ICON_LAYER; private long id = ID_GEN.incrementAndGet(); private boolean initialized; private Reference<LayerGroup> layerGroup; private long maximumScale = 0; private long minimumScale = Long.MAX_VALUE; private String name; private Map<String, Map<String, Object>> pluginConfigByName = new TreeMap<>(); private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private boolean queryable = true; private boolean querySupported = true; private boolean readOnly = false; private LayerRenderer<AbstractLayer> renderer; private boolean selectable = true; private boolean selectSupported = true; private Object sync = new Object(); private String type; private boolean visible = OS.getPreferenceBoolean("com.revolsys.gis", PREFERENCE_PATH, PREFERENCE_NEW_LAYERS_VISIBLE, false); protected AbstractLayer(final String type) { this.type = type; getSync(); } @Override public void activatePanelComponent(final Component component, final Map<String, Object> config) { setProperty("bottomTabOpen", config); } protected void addParent(final List<Layer> path) { final LayerGroup parent = getLayerGroup(); if (parent != null) { path.add(0, parent); parent.addParent(path); } } public int addRenderer(final LayerRenderer<?> child) { return addRenderer(child, 0); } public int addRenderer(final LayerRenderer<?> child, final int index) { setRenderer(child); return 0; } public boolean canSaveSettings(final Path directory) { if (directory != null) { if (!Files.exists(directory)) { Logs.error(this, "Unable to save layer " + getPath() + " directory does not exist " + directory); } else if (!Files.isDirectory(directory)) { Logs.error(this, "Unable to save layer " + getPath() + " file is not a directory " + directory); } else if (!Files.isWritable(directory)) { Logs.error(this, "Unable to save layer " + getPath() + " directory is not writable " + directory); } else { return true; } } return false; } protected boolean checkShowProperties() { boolean show = true; synchronized (this) { if (Booleans.getBoolean(getProperty("INTERNAL_PROPERTIES_VISIBLE"))) { show = false; } else { setProperty("INTERNAL_PROPERTIES_VISIBLE", true); } } return show; } public void clearPluginConfig(final String pluginName) { this.pluginConfigByName.remove(pluginName); } @Override public AbstractLayer clone() { final AbstractLayer clone = (AbstractLayer)super.clone(); clone.beanPropertyListener = new BeanPropertyListener(clone); clone.eventsEnabled = new ThreadBooleanValue(true); clone.id = this.id = ID_GEN.incrementAndGet(); clone.initialized = false; clone.layerGroup = null; clone.propertyChangeSupport = new PropertyChangeSupport(clone); if (clone.renderer != null) { clone.renderer = clone.renderer.clone(); } clone.sync = new Object(); return clone; } @Override public int compareTo(final Layer layer) { return getName().compareTo(layer.getName()); } @Override public void delete() { this.deleted = true; setExists(false); this.beanPropertyListener = null; final ProjectFrame projectFrame = ProjectFrame.get(this); if (projectFrame != null) { projectFrame.removeBottomTab(this); } firePropertyChange("deleted", false, true); final LayerGroup layerGroup = getLayerGroup(); if (layerGroup != null) { layerGroup.removeLayer(this); this.layerGroup = new EmptyReference<>(); } this.eventsEnabled.closeable(false); final PropertyChangeSupport propertyChangeSupport = this.propertyChangeSupport; if (propertyChangeSupport != null) { Property.removeAllListeners(propertyChangeSupport); this.propertyChangeSupport = null; } if (this.renderer != null) { this.renderer.setLayer(null); } } @Override public void deletePanelComponent(final Component component) { clearPluginConfig(AbstractLayer.PLUGIN_TABLE_VIEW); } public void deleteWithConfirm() { final int confirm = JOptionPane.showConfirmDialog(getMapPanel(), "Delete the layer and any child layers? This action cannot be undone.", "Delete Layer", JOptionPane.OK_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE); if (confirm == JOptionPane.OK_OPTION) { delete(); } } public BaseCloseable eventsDisabled() { return this.eventsEnabled.closeable(false); } public BaseCloseable eventsEnabled() { return this.eventsEnabled.closeable(true); } protected void fireGeometryFactoryChanged(final GeometryFactory oldGeometryFactory, final GeometryFactory newGeometryFactory) { firePropertyChange("geometryFactory", oldGeometryFactory, this.geometryFactory); final int coordinateSystemId = newGeometryFactory.getCoordinateSystemId(); firePropertyChange("srid", -2, coordinateSystemId); } protected void fireIndexedPropertyChange(final String propertyName, final int index, final Object oldValue, final Object newValue) { if (this.propertyChangeSupport != null) { this.propertyChangeSupport.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); } } @Override public void firePropertyChange(final String propertyName, final Object oldValue, final Object newValue) { if (this.propertyChangeSupport != null && this.eventsEnabled.isTrue()) { this.propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue); } } public PropertyChangeListener getBeanPropertyListener() { return this.beanPropertyListener; } @Override public BoundingBox getBoundingBox() { return this.boundingBox; } @Override public BoundingBox getBoundingBox(final boolean visibleLayersOnly) { if (this.visible || !visibleLayersOnly) { return getBoundingBox(); } else { final GeometryFactory geometryFactory = getGeometryFactory(); return geometryFactory.newBoundingBoxEmpty(); } } @Override public Collection<Class<?>> getChildClasses() { return Collections.emptySet(); } public Path getDirectory() { final LayerGroup layerGroup = getLayerGroup(); if (layerGroup == null) { return null; } else { return layerGroup.getDirectory(); } } @Override public GeometryFactory getGeometryFactory() { return this.geometryFactory; } @Override public Icon getIcon() { return this.icon; } @Override public long getId() { return this.id; } @Override public LayerGroup getLayerGroup() { if (this.layerGroup == null) { return null; } else { return this.layerGroup.get(); } } @Override public long getMaximumScale() { return this.maximumScale; } @Override public long getMinimumScale() { return this.minimumScale; } @Override public String getName() { return this.name; } @SuppressWarnings("unchecked") @Override public <V extends LayerGroup> V getParent() { return (V)getLayerGroup(); } @Override public String getPath() { final LayerGroup layerGroup = getLayerGroup(); if (layerGroup == null) { return "/"; } else { final String path = layerGroup.getPath(); if ("/".equals(path)) { return "/" + getName(); } else { return path + "/" + getName(); } } } @Override public List<Layer> getPathList() { final List<Layer> path = new ArrayList<>(); path.add(this); addParent(path); return path; } public Map<String, Object> getPluginConfig(final String pluginName) { final Map<String, Object> pluginConfig = this.pluginConfigByName.get(pluginName); if (pluginConfig == null) { return Collections.emptyMap(); } else { return new LinkedHashMap<>(pluginConfig); } } @Override public Project getProject() { final LayerGroup layerGroup = getLayerGroup(); if (layerGroup == null) { return null; } else { return layerGroup.getProject(); } } @Override public PropertyChangeSupport getPropertyChangeSupport() { return this.propertyChangeSupport; } @SuppressWarnings("unchecked") @Override public <L extends LayerRenderer<? extends Layer>> L getRenderer() { return (L)this.renderer; } @Override public BoundingBox getSelectedBoundingBox() { final GeometryFactory geometryFactory = getGeometryFactory(); if (geometryFactory == null) { return BoundingBox.empty(); } else { return geometryFactory.newBoundingBoxEmpty(); } } protected String getSettingsFileName() { final String name = getName(); return FileUtil.getSafeFileName(name) + ".rgobject"; } public Object getSync() { if (this.sync == null) { this.sync = new Object(); } return this.sync; } @Override public String getToolTip() { return this.errorMessage; } @Override public String getType() { return this.type; } @Override public final synchronized void initialize() { if (!isInitialized()) { initializeForce(); } } protected boolean initializeDo() { return true; } protected void initializeForce() { try { final boolean exists; try ( BaseCloseable eventsDisabled = eventsDisabled()) { exists = initializeDo(); } setExists(exists); if (exists && Property.getBoolean(this, "showTableView")) { Invoke.later(this::showTableView); } } catch (final Throwable e) { Logs.error(this, getPath() + ": Unable to initialize layer", e); setExists(false); } finally { setInitialized(true); } } @Override public boolean isClonable() { return false; } @Override public boolean isDeleted() { if (this.deleted) { return true; } else { final LayerGroup parent = getLayerGroup(); if (parent == null) { return false; } else { return parent.isDeleted(); } } } @Override public boolean isEditable() { return this.editable; } @Override public boolean isEditable(final double scale) { return isVisible(scale) && isEditable(); } public boolean isEventsEnabled() { return this.eventsEnabled.isTrue(); } @Override public boolean isExists() { return isInitialized() && this.exists; } @Override public boolean isHasSelectedRecords() { return false; } @Override public boolean isInitialized() { return this.initialized; } @Override public boolean isOpen() { return this.open; } @Override public boolean isQueryable() { return this.querySupported && this.queryable; } @Override public boolean isQuerySupported() { return isExists() && this.querySupported; } @Override public boolean isReadOnly() { return !isExists() || this.readOnly; } @Override public boolean isSelectable() { return isExists() && isVisible() && (isSelectSupported() && this.selectable || isEditable()); } @Override public boolean isSelectable(final double scale) { return isSelectable() && isVisible(scale); } @Override public boolean isSelectSupported() { return this.selectSupported; } @Override public boolean isVisible() { final LayerGroup parent = getParent(); return this.visible && (parent == null || parent.isVisible()); } @Override public boolean isVisible(final double scale) { final LayerGroup parent = getParent(); if (isExists() && isVisible() && (parent == null || parent.isVisible(scale))) { final long longScale = (long)scale; final long minimumScale = getMinimumScale(); final long maximumScale = getMaximumScale(); if (minimumScale >= longScale && longScale >= maximumScale) { return true; } } return false; } public boolean isZoomToLayerEnabled() { if (isHasGeometry()) { if (!getBoundingBox().isEmpty()) { return true; } } return false; } @Override public Component newPanelComponent(final Map<String, Object> config) { if (isInitialized()) { if (isExists()) { return newTableViewComponent(config); } else { return new BasePanel(new BorderLayout()); } } else { final BasePanel basePanel = new BasePanel(new BorderLayout()); addPropertyChangeListener("initialized", (event) -> { if (isExists()) { Invoke.later(() -> { final Component tableViewComponent = newTableViewComponent(config); if (tableViewComponent != null) { basePanel.add(tableViewComponent, BorderLayout.CENTER); removePropertyChangeListener("initialized", this); } }); } }); if (isInitialized()) { firePropertyChange("initialized", false, true); } return basePanel; } } @Override public TabbedValuePanel newPropertiesPanel() { final TabbedValuePanel tabPanel = new TabbedValuePanel("Layer " + this + " Properties", this); newPropertiesTabGeneral(tabPanel); newPropertiesTabCoordinateSystem(tabPanel); return tabPanel; } protected JPanel newPropertiesTabCoordinateSystem(final TabbedValuePanel tabPanel) { final GeometryFactory geometryFactory = getGeometryFactory(); if (geometryFactory != null) { final JPanel panel = new JPanel(new VerticalLayout(5)); tabPanel.addTab("Spatial", "world", panel); final JPanel extentPanel = Panels.titledTransparent("Extent"); final BoundingBox boundingBox = getBoundingBox(); if (boundingBox == null || boundingBox.isEmpty()) { extentPanel.add(new JLabel("Unknown")); } else { final JLabel extentLabel = new JLabel("<html><table cellspacing=\"3\" style=\"margin:0px\">" + "<tr><td> </td><th style=\"text-align:left\">Top:</th><td style=\"text-align:right\">" + DataTypes.toString(boundingBox.getMaximum(1)) + "</td><td> </td></tr><tr>" + "<td><b>Left</b>: " + DataTypes.toString(boundingBox.getMinimum(0)) + "</td><td> </td><td> </td>" + "<td><b>Right</b>: " + DataTypes.toString(boundingBox.getMaximum(0)) + "</td></tr>" + "<tr><td> </td><th>Bottom:</th><td style=\"text-align:right\">" + DataTypes.toString(boundingBox.getMinimum(1)) + "</td><td> </td></tr><tr>" + "</tr></table></html>"); extentLabel.setFont(SwingUtil.FONT); extentPanel.add(extentLabel); } GroupLayouts.makeColumns(extentPanel, 1, true); panel.add(extentPanel); final JPanel coordinateSystemPanel = Panels.titledTransparent("Coordinate System"); final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); if (coordinateSystem == null) { coordinateSystemPanel.add(new JLabel("Unknown")); } else { final int axisCount = geometryFactory.getAxisCount(); SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "ID", coordinateSystem.getCoordinateSystemId(), 10); SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "axisCount", axisCount, 10); final double scaleXY = geometryFactory.getScaleXY(); if (scaleXY > 0) { SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "scaleXy", scaleXY, 10); } else { SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "scaleXy", "Floating", 10); } if (axisCount > 2) { final double scaleZ = geometryFactory.getScaleZ(); if (scaleZ > 0) { SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "scaleZ", scaleZ, 10); } else { SwingUtil.addLabelledReadOnlyTextField(coordinateSystemPanel, "scaleZ", "Floating", 10); } } final CoordinateSystem esriCoordinateSystem = EsriCoordinateSystems .getCoordinateSystem(coordinateSystem); SwingUtil.addLabel(coordinateSystemPanel, "ESRI WKT"); final TextArea wktTextArea = new TextArea(EsriCsWktWriter.toString(esriCoordinateSystem), 10, 80); wktTextArea.setEditable(false); wktTextArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11)); coordinateSystemPanel.add(wktTextArea); GroupLayouts.makeColumns(coordinateSystemPanel, 2, true); } panel.add(coordinateSystemPanel); return panel; } return null; } protected BasePanel newPropertiesTabGeneral(final TabbedValuePanel tabPanel) { final BasePanel generalPanel = new BasePanel(new VerticalLayout(5)); generalPanel.setScrollableHeightHint(ScrollableSizeHint.FIT); tabPanel.addTab("General", generalPanel); newPropertiesTabGeneralPanelGeneral(generalPanel); final ValueField sourcePanel = newPropertiesTabGeneralPanelSource(generalPanel); if (sourcePanel.getComponentCount() == 0) { generalPanel.remove(sourcePanel); } return generalPanel; } protected ValueField newPropertiesTabGeneralPanelGeneral(final BasePanel parent) { final ValueField panel = new ValueField(this); Borders.titled(panel, "General"); final Field nameField = (Field)SwingUtil.addObjectField(panel, this, "name"); Property.addListener(nameField, "name", this.beanPropertyListener); final String type = Property.get(this, "type"); final String typeLabel = CaseConverter.toCapitalizedWords(type); SwingUtil.addLabelledReadOnlyTextField(panel, "Type", typeLabel); GroupLayouts.makeColumns(panel, 2, true); parent.add(panel); return panel; } protected ValueField newPropertiesTabGeneralPanelSource(final BasePanel parent) { final ValueField panel = new ValueField(this); Borders.titled(panel, "Source"); parent.add(panel); return panel; } protected Component newTableViewComponent(final Map<String, Object> config) { return null; } @Override public void propertyChange(final PropertyChangeEvent event) { if (this.propertyChangeSupport != null && this.eventsEnabled.isTrue()) { this.propertyChangeSupport.firePropertyChange(event); } } @Override public final void refresh() { Invoke.background("Refresh Layer " + getName(), () -> { try { refreshDo(); } catch (final Throwable e) { Logs.error(this, "Unable to refresh layer: " + getName(), e); } finally { refreshPostDo(); } }); } @Override public final void refreshAll() { if (isInitialized() && isExists()) { try { refreshAllDo(); } catch (final Throwable e) { Logs.error(this, "Unable to refresh layer: " + getName(), e); } refreshPostDo(); } else { initializeForce(); } } protected void refreshAllDo() { refreshDo(); } protected void refreshDo() { } protected void refreshPostDo() { firePropertyChange("refresh", false, true); } @Override public boolean saveChanges() { boolean saved = true; if (isHasChanges()) { saved &= saveChangesDo(); } return saved; } protected boolean saveChangesDo() { return true; } public boolean saveSettings() { final Path directory = getDirectory(); return saveSettings(directory); } @Override public boolean saveSettings(final Path directory) { if (directory != null) { if (canSaveSettings(directory)) { return saveSettingsDo(directory); } } return false; } protected boolean saveSettingsDo(final java.nio.file.Path directory) { final String settingsFileName = getSettingsFileName(); final java.nio.file.Path settingsFile = directory.resolve(settingsFileName); MapObjectFactory.write(settingsFile, this); return true; } protected void setBoundingBox(final BoundingBox boundingBox) { this.boundingBox = boundingBox; } @Override public void setEditable(final boolean editable) { final boolean old = isEditable(); this.editable = editable; firePropertyChange("editable", old, isEditable()); } public void setExists(final boolean exists) { if (exists) { this.errorMessage = null; } final boolean old = this.exists; this.exists = exists; firePropertyChange("exists", old, this.exists); } protected void setGeometryFactory(final GeometryFactory geometryFactory) { final GeometryFactory oldGeometryFactory = this.geometryFactory; if (setGeometryFactoryDo(geometryFactory)) { fireGeometryFactoryChanged(oldGeometryFactory, geometryFactory); } } protected boolean setGeometryFactoryDo(final GeometryFactory geometryFactory) { if (geometryFactory == null) { return false; } else if (geometryFactory.equals(this.geometryFactory)) { return false; } else { this.geometryFactory = geometryFactory; if (Property.isEmpty(this.boundingBox)) { final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); if (coordinateSystem != null) { this.boundingBox = coordinateSystem.getAreaBoundingBox(); } } else if (this.boundingBox != null && !this.boundingBox.getGeometryFactory().isHasCoordinateSystem() && geometryFactory.isHasCoordinateSystem()) { this.boundingBox = this.boundingBox.convert(geometryFactory); } return true; } } public void setIcon(final Icon icon) { this.icon = icon; } protected void setInitialized(final boolean initialized) { this.initialized = initialized; firePropertyChange("initialized", !initialized, this.initialized); } @Override public void setLayerGroup(final LayerGroup layerGroup) { final LayerGroup old = getLayerGroup(); if (old != layerGroup) { if (old != null) { Property.removeListener(this, old); } this.layerGroup = new WeakReference<>(layerGroup); Property.addListener(this, layerGroup); firePropertyChange("layerGroup", old, layerGroup); } } @Override public void setMaximumScale(long maximumScale) { if (maximumScale < 0) { maximumScale = 0; } final long oldValue = this.maximumScale; this.maximumScale = maximumScale; firePropertyChange("maximumScale", oldValue, this.minimumScale); } @Override public void setMinimumScale(long minimumScale) { if (minimumScale <= 0) { minimumScale = Long.MAX_VALUE; } final long oldValue = this.minimumScale; this.minimumScale = minimumScale; firePropertyChange("minimumScale", oldValue, this.minimumScale); } @Override public void setName(final String name) { final Object oldValue = this.name; final LayerGroup layerGroup = getLayerGroup(); String newName = name; if (layerGroup != null) { int i = 1; while (layerGroup.hasLayerWithSameName(this, newName)) { newName = name + i; i++; } } this.name = newName; firePropertyChange("name", oldValue, this.name); } public boolean setNotExists(final String errorMessage) { this.errorMessage = errorMessage; setExists(false); return false; } @Override public void setOpen(final boolean open) { final boolean oldValue = this.open; this.open = open; firePropertyChange("open", oldValue, this.open); } public void setPluginConfig(final Map<String, Map<String, Object>> pluginConfig) { this.pluginConfigByName = pluginConfig; } public void setPluginConfig(final String pluginName, final Map<String, Object> config) { this.pluginConfigByName.put(pluginName, config); } public void setPluginConfig(final String pluginName, final MapSerializer serializer) { setPluginConfig(pluginName, new MapSerializerMap(serializer)); } @Override public void setProperties(final Map<String, ? extends Object> properties) { if (properties == null || !this.getProperties().equals(properties)) { super.setProperties(properties); firePropertyChange("properties", null, properties); } } @Override public void setProperty(final String name, final Object value) { // TODO see if we can get the JavaBeanUtil set property to work with // conversions if (name.equals("type")) { } else if (name.equals("minimumScale")) { setMinimumScale(((Number)value).longValue()); } else if (name.equals("open")) { setOpen((Boolean)value); } else if (name.equals("maximumScale")) { setMaximumScale(((Number)value).longValue()); } else { final Object oldValue = getProperty(name); try { super.setProperty(name, value); } catch (final Throwable e) { Logs.error(this, "Unable to set property:" + name, e); } if (!DataType.equal(oldValue, value)) { final KeyedPropertyChangeEvent event = new KeyedPropertyChangeEvent(this, "property", oldValue, value, name); if (this.propertyChangeSupport != null) { this.propertyChangeSupport.firePropertyChange(event); } } } } @Override public void setQueryable(final boolean queryable) { this.queryable = queryable; } protected void setQuerySupported(final boolean querySupported) { this.querySupported = querySupported; } @Override public void setReadOnly(final boolean readOnly) { this.readOnly = readOnly; } @SuppressWarnings("unchecked") @Override public void setRenderer(final LayerRenderer<? extends Layer> renderer) { final LayerRenderer<?> oldValue = this.renderer; if (oldValue != null) { oldValue.setLayer(null); Property.removeListener(renderer, this); } this.renderer = (LayerRenderer<AbstractLayer>)renderer; if (renderer != null) { ((AbstractLayerRenderer<?>)this.renderer).setEditing(false); this.renderer.setLayer(this); Property.addListener(renderer, this); } firePropertyChange("renderer", oldValue, this.renderer); fireIndexedPropertyChange("renderer", 0, oldValue, this.renderer); } @Override public void setSelectable(final boolean selectable) { final boolean oldValue = this.selectable; this.selectable = selectable; firePropertyChange("selectable", oldValue, selectable); } public void setSelectSupported(final boolean selectSupported) { this.selectSupported = selectSupported; } protected void setType(final String type) { this.type = type; } @Override public void setVisible(final boolean visible) { final boolean oldVisible = this.visible; this.visible = visible; firePropertyChange("visible", oldVisible, visible); } @Override public void showProperties() { showProperties(null); } @Override public void showProperties(final String tabName) { final MapPanel map = getMapPanel(); if (map != null) { if (this.exists) { if (checkShowProperties()) { try { final Window window = SwingUtilities.getWindowAncestor(map); final TabbedValuePanel panel = newPropertiesPanel(); panel.setSelectdTab(tabName); panel.showDialog(window); refresh(); } finally { removeProperty("INTERNAL_PROPERTIES_VISIBLE"); } } } } } @Override public void showRendererProperties(final LayerRenderer<?> renderer) { final MapPanel map = getMapPanel(); if (map != null) { if (this.exists) { if (checkShowProperties()) { try { final Window window = SwingUtilities.getWindowAncestor(map); final TabbedValuePanel panel = newPropertiesPanel(); panel.setSelectdTab("Style"); final LayerStylePanel stylePanel = panel.getTab("Style"); stylePanel.setSelectedRenderer(renderer); panel.showDialog(window); refresh(); } finally { removeProperty("INTERNAL_PROPERTIES_VISIBLE"); } } } } } @Override public void showTableView() { showTableView(Collections.emptyMap()); } @Override public void showTableView(final Map<String, Object> config) { final ProjectFrame projectFrame = ProjectFrame.get(this); if (projectFrame != null) { projectFrame.addBottomTab(this, config); } } public void toggleEditable() { final boolean editable = isEditable(); setEditable(!editable); } @SuppressWarnings("unchecked") @Override public MapEx toMap() { final MapEx map = new LinkedHashMapEx(); addTypeToMap(map, this.type); addToMap(map, "name", this.name); addToMap(map, "visible", this.visible); addToMap(map, "open", this.open); addToMap(map, "querySupported", this.querySupported); if (this.querySupported) { addToMap(map, "queryable", this.queryable); } addToMap(map, "readOnly", this.readOnly); if (!this.readOnly) { addToMap(map, "editable", this.editable); } if (this.selectSupported) { addToMap(map, "selectable", this.selectable); } addToMap(map, "selectSupported", this.selectSupported); addToMap(map, "maximumScale", this.maximumScale); addToMap(map, "minimumScale", this.minimumScale); addToMap(map, "style", this.renderer); addToMap(map, "pluginConfig", this.pluginConfigByName); final Map<String, Object> properties = (Map<String, Object>)toMapValue(getProperties()); if (properties != null) { for (final Entry<String, Object> entry : properties.entrySet()) { final String name = entry.getKey(); if (!map.containsKey(name) && !name.startsWith("INTERNAL")) { final Object value = entry.getValue(); if (!(value instanceof Component)) { map.put(name, value); } } } } map.remove("showTableView"); return map; } @Override public String toString() { return getName(); } public void zoomToLayer() { final Project project = getProject(); if (project != null) { final GeometryFactory geometryFactory = project.getGeometryFactory(); final BoundingBox layerBoundingBox = getBoundingBox(); final BoundingBox boundingBox = layerBoundingBox.convert(geometryFactory) .clipToCoordinateSystem() .expandPercent(0.1); project.setViewBoundingBox(boundingBox); } } }