Java tutorial
/******************************************************************************* * Copyright (c) 2010 Stefan A. Tzeggai. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Stefan A. Tzeggai - initial API and implementation ******************************************************************************/ package org.geopublishing.atlasViewer.dp.layer; import java.awt.Component; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.geopublishing.atlasViewer.AtlasConfig; import org.geopublishing.atlasViewer.dp.AMLImport; import org.geopublishing.atlasViewer.swing.AVDialogManager; import org.geopublishing.atlasViewer.swing.AVSwingUtil; import org.geopublishing.atlasViewer.swing.AtlasViewerGUI; import org.geopublishing.atlasViewer.swing.MapLegend; import org.geopublishing.atlasViewer.swing.internal.AtlasExportTask; import org.geotools.data.DataAccess; import org.geotools.data.DataStore; import org.geotools.data.FeatureSource; import org.geotools.feature.FeatureCollection; import org.geotools.feature.NameImpl; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; import org.opengis.referencing.crs.CoordinateReferenceSystem; import de.schmitzm.geotools.data.amd.AttributeMetadataImpl; import de.schmitzm.geotools.data.amd.AttributeMetadataImplMap; import de.schmitzm.geotools.data.amd.AttributeMetadataMap; import de.schmitzm.geotools.feature.CQLFilterParser; import de.schmitzm.geotools.io.GeoImportUtil; import de.schmitzm.geotools.io.GeoImportUtil.SHP_POSTFIXES; import de.schmitzm.geotools.styling.StyledFeatureSourceInterface; import de.schmitzm.geotools.styling.StyledFeaturesInterface; import de.schmitzm.geotools.styling.StyledLayerUtil; import de.schmitzm.i18n.Translation; import de.schmitzm.io.IOUtil; import de.schmitzm.jfree.feature.style.FeatureChartStyle; import de.schmitzm.lang.LangUtil; public abstract class DpLayerVectorFeatureSource extends DpLayerVector<FeatureSource<SimpleFeatureType, SimpleFeature>, FeatureChartStyle> implements StyledFeatureSourceInterface { static private final Logger LOGGER = Logger.getLogger(DpLayerVectorFeatureSource.class); @Override public ReferencedEnvelope getReferencedEnvelope() { if (getEnvelope() == null) return null; if (getCrs() == null) return null; return new ReferencedEnvelope(getEnvelope(), getCrs()); } /** * The {@link Name} of the {@link SimpleFeatureType} of the * {@link DataStore} that is accessed. */ private Name typeName; /** * Has the attribute meta-data been checked against the schema yet? this * must be done to ensure that add/deleted columns are correctly presented. */ private boolean attribMetadataChecked = false; /** * Returns the Name of the {@link SimpleFeatureType} of the * {@link DataStore} that is accessed. */ public Name getTypeName() { return typeName; } /** * The cached {@link FeatureSource} where the {@link SimpleFeature}s come * from. */ protected FeatureSource<SimpleFeatureType, SimpleFeature> featureSource; /** * The cached {@link DataStore} where the {@link #featureSource} comes from. */ protected DataAccess<SimpleFeatureType, SimpleFeature> dataStore; private Filter filter = Filter.INCLUDE; private SimpleFeatureType schema; /** * The constructor used when loading a {@link DpLayerVectorFeatureSource} * description in {@link AMLImport} * * @param ac */ public DpLayerVectorFeatureSource(AtlasConfig ac) { super(ac); attributeMetaDataMap = new AttributeMetadataImplMap(ac.getLanguages()); } /** * Releases the {@link FeatureSource}. */ @Override public void uncache() { super.uncache(); attribMetadataChecked = false; schema = null; /** Close any open attribute table for this layer */ AVDialogManager.dm_AttributeTable.disposeInstanceFor(this); /** Close any open atlas styler for this layer */ AVDialogManager.dm_AtlasStyler.disposeInstanceFor(this); /** Close any open styler for this layer */ AVDialogManager.dm_Styler.disposeInstanceFor(this); if (featureSource != null) { featureSource = null; } if (dataStore != null) { /** * java.util.ConcurrentModificationException at * java.util.AbstractList$Itr * .checkForComodification(AbstractList.java:372) at * java.util.AbstractList$Itr.next(AbstractList.java:343) at * org.geotools * .data.shapefile.ShpFiles.logCurrentLockers(ShpFiles.java:234) at * org.geotools.data.shapefile.ShpFiles.dispose(ShpFiles.java:221) * at org.geotools.data.shapefile.ShapefileDataStore.dispose( * ShapefileDataStore.java:1075) at * skrueger.atlas.dp.layer.DpLayerVectorFeatureSource * .uncache(DpLayerVectorFeatureSource.java:127) at * skrueger.atlas.map.Map.uncache(Map.java:218) at * skrueger.creator.AtlasConfigEditable * .uncache(AtlasConfigEditable.java:947) at * skrueger.creator.UncacheAtlasAction$1 * .doInBackground(UncacheAtlasAction.java:71) at * javax.swing.SwingWorker$1.call(SwingWorker.java:278) at * java.util. * concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at * java.util.concurrent.FutureTask.run(FutureTask.java:138) at * javax.swing.SwingWorker.run(SwingWorker.java:317) * * But what can i do against it? */ /** * Wenn eine andere Karte mit dem selben Layer im GP noch offen ist, * dann sorgt #dispose() dafr, dass dort eine NPE kommt. * * Deshalb hier nur auf <code>null</code> setzen und hoffen, dass * finalize das macht. * * #88 (Wenn ein Datastore in einer offenen Karte disposed wird, * gibts in einer anderen offenen Map NPE) */ // dataStore.dispose(); dataStore = null; LangUtil.gc(); } } @Override public GeometryDescriptor getDefaultGeometry() { return getGeoObject().getSchema().getGeometryDescriptor(); } /** * If crs == null, it will first try to parse a .prj file. If it doesn't * exist, it calls {@link #getGeoObject()} and hopes that the crs will be * set there * * This is overwritten by DpLayerFeatureSource, because e.g. a WFS doens't * not have a .prj file * * @return <code>null</code> if not working */ @Override public final CoordinateReferenceSystem getCrs() { if (crs == null && !isBroken()) { crs = super.getCrs(); // We have to call get getGeoObject to determine the type: line, // string, poly.. getGeoObject(); if (crs == null && !isBroken()) { /** * Trying to reread the CRS by accessing the GeoObject */ try { crs = getGeoObject().getSchema().getGeometryDescriptor().getCoordinateReferenceSystem(); } catch (Exception ex) { setBrokenException(ex); return null; } } } return crs; } /** * Returns the features of this {@link FeatureSource}. The {@link Filter} * associated with the {@link StyledFeaturesInterface} is automatically * applied. */ @Override public FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatureCollectionFiltered() { FeatureCollection<SimpleFeatureType, SimpleFeature> features; try { features = getGeoObject().getFeatures(getFilter()); } catch (IOException e) { throw new RuntimeException("Error getting the features of the FeatureSource"); } return features; } /** * Returns the features of this {@link FeatureSource}. The {@link Filter} * associated with the {@link StyledFeaturesInterface} is not automatically * applied. * * @see {@link StyledFeaturesInterface} * @see #getFeatureCollectionFiltered() */ @Override public FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatureCollection() { FeatureCollection<SimpleFeatureType, SimpleFeature> features; try { features = getGeoObject().getFeatures(); } catch (IOException e) { throw new RuntimeException("Error getting the features of the FeatureSource"); } return features; } /** * Same as {@link #getGeoObject()} method, but complies to the * {@link StyledFeaturesInterface} * * @see {@link StyledFeaturesInterface} */ @Override public FeatureSource<SimpleFeatureType, SimpleFeature> getFeatureSource() { return getGeoObject(); } /** * Opens a ChartFrame. The selection buttons of the corresponding * {@link MapLegend} are updated if not <code>null</code>. * * @param chartID * The ID/Filename of the chart to display * @param mapLegend * May be <code>null</code> if the chart is just opened without a * connection to a {@link JSMapPane} */ public void openChart(Component owner, String chartID, final MapLegend mapLegend) { FeatureChartStyle chart = getChartForID(chartID); AVDialogManager.dm_Charts.getInstanceFor(chart, owner, chart, mapLegend, this); // TODO How does a filter change affect the chart?? // final MapLayerAdapter mapLayerChangedListener = new MapLayerAdapter() // { // // @Override // public void layerChanged(MapLayerEvent arg0) { // // if (arg0.getReason() == MapLayerEvent.FILTER_CHANGED) { // // // // TODO compare to old filter to see if anything has // // chnaged // // openTableDialog.getModel().getFilter().c // // StyledLayerSelectionModel<?> styledLayerSelectionModel = // layerPanel.rememberSelection // .get(arg0.getSource()); // if (styledLayerSelectionModel != null) { // // If there exists a SelectionModel for this MapLayer, // // we have two options: Clear all selections, or // // unselect the excluded elements.. We do the first // // here. // styledLayerSelectionModel.setValueIsAdjusting(true); // styledLayerSelectionModel.clearSelection(); // // } // chartDialog.getModel().setFilter( // mapLayer.getQuery().getFilter()); // // if (styledLayerSelectionModel != null) { // styledLayerSelectionModel.setValueIsAdjusting(false); // } // } // } // }; // // mapLayer.addMapLayerListener(mapLayerChangedListener); } public void setTypeName(Name typeName) { this.typeName = typeName; } @Override public void setFilter(Filter filter) { this.filter = filter; } @Override public Filter getFilter() { return filter; } public void setFilterRule(String ruleString) { filter = new CQLFilterParser().parseFilter(ruleString); } /** * Returns a cached schema... */ @Override public SimpleFeatureType getSchema() { if (schema == null && !isBroken()) { try { schema = getFeatureSource().getSchema(); } catch (Exception e) { setBrokenException(e); return null; } } return schema; } @Override public DpLayer<FeatureSource<SimpleFeatureType, SimpleFeature>, FeatureChartStyle> copyTo( DpLayer<FeatureSource<SimpleFeatureType, SimpleFeature>, FeatureChartStyle> target) { DpLayerVectorFeatureSource copy = (DpLayerVectorFeatureSource) super.copyTo(target); // DuplicatingFilterVisitor dfv = new DuplicatingFilterVisitor(); copy.setFilter(getFilter()); copy.setTypeName(getTypeName()); return copy; } /** * @return A value between 0 and 1 which describes how good much metadata * has been provided. 1 is great. This method uses * super.getQuality() and "adds" the quality of the column * translations. */ @Override public Double getQuality() { if (isBroken()) return 0.; Double result; final Double layerQM = super.getQuality(); result = (layerQM * 4. + attributeMetaDataMap.getQuality(getAtlasConfig().getLanguages()) * 1.) / 5.; return result; } /** * A {@link Map} that holds the {@link AttributeMetadataImpl}, e.g. * {@link Translation}s and unit */ final AttributeMetadataMap attributeMetaDataMap; /** * @return a {@link Map} with {@link Name} -> {@link AttributeMetadataImpl}. */ @Override public AttributeMetadataMap<AttributeMetadataImpl> getAttributeMetaDataMap() { if (!attribMetadataChecked && !AtlasViewerGUI.isRunning()) { /** * checkAttribMetaData requires all Geoobjects to be available. * Exported atlases have more information in the atlas.xml than they * need. So we may only do this when we are running from * Geopublisher (!AtlasViewer.isRunning) */ try { StyledLayerUtil.checkAttribMetaData(attributeMetaDataMap, getSchema()); attribMetadataChecked = true; } catch (Exception e) { String msg = "Error while validating the described attributes against the \"physical\" schema of the datafile " + getFilename() + "\n. Will continue without validation."; LOGGER.error(msg, e); } } // I don't know how "the_geom" is getting into it again and again if (attributeMetaDataMap.containsKey(new NameImpl("the_geom"))) { attributeMetaDataMap.remove(new NameImpl("the_geom")); } return attributeMetaDataMap; } @Override public void exportWithGUI(Component owner) throws IOException { AtlasExportTask exportTask = new AtlasExportTask(owner, getTitle().toString()) { @Override protected Boolean doInBackground() throws Exception { setPrefix("Exporting "); try { exportDir = AVSwingUtil.selectExportDir(owner, getAtlasConfig()); if (exportDir == null) { return false; } URL url = AVSwingUtil.getUrl(DpLayerVectorFeatureSource.this, owner); final File file = new File(exportDir, getFilename()); // **************************************************************************** // Copy main file and possibly throw an Exception // **************************************************************************** publish(file.getAbsolutePath()); FileUtils.copyURLToFile(AVSwingUtil.getUrl(DpLayerVectorFeatureSource.this, owner), file); // Try to copy any pending files... for (SHP_POSTFIXES pf : GeoImportUtil.SHP_POSTFIXES.values()) { final File changeFileExt = IOUtil.changeFileExt(file, pf.toString()); publish(changeFileExt.getAbsolutePath()); AtlasConfig.exportURLtoFileNoEx(IOUtil.changeUrlExt(url, pf.toString()), changeFileExt); } AtlasConfig.exportURLtoFileNoEx(IOUtil.changeUrlExt(url, "prj"), IOUtil.changeFileExt(file, "prj")); AtlasConfig.exportURLtoFileNoEx(IOUtil.changeUrlExt(url, "sld"), IOUtil.changeFileExt(file, "sld")); AtlasConfig.exportURLtoFileNoEx(IOUtil.changeUrlExt(url, "shp.xml"), IOUtil.changeFileExt(file, "shp.xml")); // publish("done"); success = true; } catch (Exception e) { done(); } return success; } }; exportTask.execute(); } }