/******************************************************************************* * Copyright (c) 2010 Stefan A. Tzeggai. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * 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.geopublisher; import java.awt.Font; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.URL; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.regex.Pattern; import javax.swing.tree.TreeNode; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.geopublishing.atlasViewer.AtlasCancelException; import org.geopublishing.atlasViewer.AtlasConfig; import org.geopublishing.atlasViewer.GpCoreUtil; import org.geopublishing.atlasViewer.dp.DpEntry; import org.geopublishing.atlasViewer.dp.DpRef; import org.geopublishing.atlasViewer.dp.Group; import org.geopublishing.atlasViewer.dp.layer.DpLayer; import org.geopublishing.atlasViewer.dp.layer.DpLayerRaster; import org.geopublishing.atlasViewer.dp.layer.DpLayerVectorFeatureSource; import org.geopublishing.atlasViewer.dp.layer.LayerStyle; import org.geopublishing.atlasViewer.dp.media.DpMediaPDF; import org.geopublishing.atlasViewer.dp.media.DpMediaPICTURE; import org.geopublishing.atlasViewer.dp.media.DpMediaVideo; import org.geopublishing.atlasViewer.exceptions.AtlasException; import org.geopublishing.atlasViewer.exceptions.AtlasFatalException; import org.geopublishing.atlasViewer.internal.AMLUtil; import org.geopublishing.atlasViewer.map.Map; import org.geopublishing.atlasViewer.map.MapPool; import org.geopublishing.atlasViewer.map.MapRef; import org.geopublishing.atlasViewer.swing.AVSwingUtil; import org.geopublishing.geopublisher.exceptions.AtlasExportException; import org.geotools.data.DataUtilities; import org.geotools.filter.text.cql2.CQL; import org.geotools.styling.Style; import org.jfree.util.Log; import org.opengis.filter.Filter; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.vividsolutions.jts.geom.Envelope; import de.schmitzm.geotools.data.amd.AttributeMetadataImpl; import de.schmitzm.geotools.io.GeoExportUtil; import de.schmitzm.geotools.io.GeoImportUtil; import de.schmitzm.geotools.styling.StylingUtil; import de.schmitzm.i18n.Translation; import de.schmitzm.io.FilterUtil; import de.schmitzm.io.IOUtil; import de.schmitzm.jfree.chart.style.ChartStyle; import de.schmitzm.jfree.chart.style.ChartStyleUtil; import de.schmitzm.jfree.feature.style.FeatureChartStyle; import de.schmitzm.jfree.feature.style.FeatureChartUtil; import de.schmitzm.swing.swingworker.AtlasStatusDialogInterface; import de.schmitzm.versionnumber.ReleaseUtil; /** * This class can export the Atlas-objects to AtlasMarkupLanguage (AtlasML) * * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a> * */ public class AMLExporter { private final String CHARTSTYLE_FILEENDING = ".chart"; final private Logger LOGGER = Logger.getLogger(AMLExporter.class); /** * This tag is replaced with the atlas base name when copying the build.xml * template **/ final static public String ATLASBASENAME_TAG_IN_BUILDXML = "ATLASBASENAME"; /** * Creates a <code>build.xml</code> in the AWC folder, which allows * automatic pblishing on http://atlas.geopublishing.org * * @param targetDir * @param atlasBaseName * @return */ public File writeBuildxml(File targetDir, String atlasBaseName) { if (atlasBaseName == null || atlasBaseName.equals(AtlasConfig.DEFAULTBASENAME) || atlasBaseName.equals("null")) { LOGGER.info("Not writing a build.xml for automatic publishing, because the basename is " + atlasBaseName + ". Please change it."); return null; } // prepare the new content: if (atlasBaseName.endsWith("/")) { atlasBaseName = atlasBaseName.substring(0, atlasBaseName.length() - 1); } String resLoc = "/autoPublish/build.xml"; URL templateBuildXml = GpUtil.class.getResource(resLoc); if (templateBuildXml == null) { LOGGER.error("Could not find " + resLoc + ". build.xml has not been created!"); return null; } String templString = IOUtil.readURLasString(templateBuildXml); String buildXmlContent = templString.replaceAll( Pattern.quote(ATLASBASENAME_TAG_IN_BUILDXML), atlasBaseName); // Prepare for writing targetDir.mkdirs(); File buildFile = new File(targetDir, "build.xml"); // Only recreat the file, if it would change to be more svn friendly if (buildFile.exists()) { String existing; try { existing = IOUtil.readFileAsString(buildFile); if (buildXmlContent.equals(existing)) return buildFile; } catch (IOException e) { LOGGER.warn("Error reading existing build.xml, it will be recreated..."); } // Recreate the build.xml file... if (!buildFile.delete()) { LOGGER.error("Could not delete exitsing " + buildFile.getAbsolutePath() + ", to update it."); } } try { FileWriter fw = new FileWriter(buildFile); fw.write(buildXmlContent); fw.flush(); fw.close(); } catch (Exception e) { LOGGER.error( "Could not write build.xml for automatic publishing on http://atlas.geopublishing.org", e); return null; } return buildFile; } /** * If <code>true</code>, only layers that are acessible in the exported * atlas will be described in the atlas.xml **/ private boolean exportMode = false; private AtlasStatusDialogInterface statusWindow = null; /** * The {@link AtlasConfigEditable} that is being exported by this * {@link AMLExporter} instance **/ private final AtlasConfigEditable ace; private boolean atlasXmlhasBeenBackupped = false; private File atlasXml = null; void info(final String msg) { if (statusWindow != null) statusWindow.setDescription(msg); } void info(final Translation msg) { info(msg.toString()); } public AMLExporter(final AtlasConfigEditable ace) { this.ace = ace; } /** * Saves the {@link AtlasConfigEditable} to projdir/atlas.xml or whatever * has been defined via {@link #setAtlasXml(File)} * * @return true if no exceptions where thrown. */ public boolean saveAtlasConfigEditable() throws Exception { return saveAtlasConfigEditable(null); } /** * Saves the {@link AtlasConfigEditable} to projdir/atlas.xml or whatever * has been defined via {@link #setAtlasXml(File)} * * @return true if no exceptions where thrown. */ public boolean saveAtlasConfigEditable( final AtlasStatusDialogInterface statusWindow) throws Exception { this.statusWindow = statusWindow; try { // **************************************************************************** // Trying to make a backup // **************************************************************************** atlasXmlhasBeenBackupped = backupAtlasXML(); /** * Saves the default CRS of that atlas to a file called * {@link AtlasConfig#DEFAULTCRS_FILENAME} in <code>ad</code> * folder. Prefers to write the EPSG code of the CRS. Does not * change the file it the contents are the same (SVN friendly). */ GeoExportUtil.writeProjectionFilePrefereEPSG(GeoImportUtil .getDefaultCRS(), new File(getAce().getAd(), AtlasConfig.DEFAULTCRS_FILENAME)); // Prepare the DOM document for writing final Source source = new DOMSource(exportAtlasConfig()); if (!getAtlasXml().exists() && atlasXmlhasBeenBackupped) { getAtlasXml().createNewFile(); } FileOutputStream fileOutputStream = new FileOutputStream( getAtlasXml()); try { // fileOutputStream.close(); OutputStreamWriter outputStreamWriter = new OutputStreamWriter( fileOutputStream, "utf-8"); try { // close outputStreamWriter.close(); // **************************************************************************** // Create the XML // **************************************************************************** final Result result = new StreamResult(outputStreamWriter); // with indenting to make it human-readable final TransformerFactory tf = TransformerFactory .newInstance(); // TODO Ging mit xerces, geht nicht mehr mit xalan // tf.setAttribute("indent-number", new Integer(2)); final Transformer xformer = tf.newTransformer(); xformer.setOutputProperty(OutputKeys.INDENT, "yes"); // // TODO ??? Try with AMLURI, makes more sense... // xformer.setOutputProperty( // "{"+AMLUtil.AMLSCHEMALOCATION+"}indent-amount", // "4"); xformer.setOutputProperty( "{http://xml.apache.org/xalan}indent-amount", "2"); // Write the DOM document to the file xformer.transform(source, result); } finally { outputStreamWriter.close(); } } finally { fileOutputStream.close(); } // Creating build.xml writeBuildxml(ace.getAtlasDir(), ace.getBaseName()); return true; } catch (Exception e) { if (atlasXmlhasBeenBackupped) try { LOGGER.warn("copying " + AtlasConfig.ATLAS_XML_FILENAME + ".bak to " + AtlasConfig.ATLAS_XML_FILENAME); IOUtil.copyFile(LOGGER, new File(getAce().getAd(), AtlasConfig.ATLAS_XML_FILENAME + ".bak"), getAtlasXml(), false); } catch (final IOException ioEx) { LOGGER.error("error copying " + AtlasConfig.ATLAS_XML_FILENAME + ".bak back.", ioEx); throw (ioEx); } if (e instanceof AtlasCancelException) return false; else throw e; } } /** * @return A XML {@link Document} that fully represents the * {@link AtlasConfig} of this atlas * @throws AtlasException * @author Stefan Alfons Tzeggai * @throws IOException */ private final Document exportAtlasConfig() throws Exception { // Create a DOM builder and parse the fragment final DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); Document document = factory.newDocumentBuilder().newDocument(); // XML root element final Element atlas = document.createElementNS(AMLUtil.AMLURI, "atlas"); // Linking this XML to the AtlasML Schema // final Attr namespaces = document.createAttributeNS( // "http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"); // namespaces.setValue(AMLUtil.AMLURI + " http://localhost:" // + Webserver.DEFAULTPORT // + "/AtlasML.xsd"); // atlas.setAttributeNode(namespaces); // Storing the version this atlas.xml is being created with inside the // atlas.xml atlas.setAttribute(AMLUtil.ATT_majVersion, String.valueOf(ReleaseUtil.getVersionMaj(GpCoreUtil.class))); atlas.setAttribute(AMLUtil.ATT_minVersion, String.valueOf(ReleaseUtil.getVersionMin(GpCoreUtil.class))); atlas.setAttribute(AMLUtil.ATT_buildVersion, String.valueOf(ReleaseUtil.getVersionBuild(GpCoreUtil.class))); // Store the atlasBaseName attribute, since 1.7 atlas.setAttribute(AMLUtil.ATT_atlasBasename, getAce().getBaseName()); // Store the JWS export jnlp baseurl parameter, since 1.6 atlas.setAttribute(AMLUtil.ATT_jnlpBaseUrl, String.valueOf(getAce().getJnlpBaseUrl())); // Store the position of the maplogo, since 1.7 atlas.setAttribute(AMLUtil.ATT_maplogoPosition, String.valueOf(getAce().getMaplogoPosition())); // <aml:name, desc, creator, copyright atlas.appendChild(exportTranslation(document, "name", getAce() .getTitle())); atlas.appendChild(exportTranslation(document, "desc", getAce() .getDesc())); atlas.appendChild(exportTranslation(document, "creator", getAce() .getCreator())); atlas.appendChild(exportTranslation(document, "copyright", getAce() .getCopyright())); // <aml:atlasversion> final Element atlasversion = document.createElementNS(AMLUtil.AMLURI, "atlasversion"); atlasversion.appendChild(document.createTextNode(getAce() .getAtlasversion().toString())); atlas.appendChild(atlasversion); // <aml:gphoster auth=...> Element gphosterauth = document.createElementNS(AMLUtil.AMLURI, AMLUtil.TAG_GPHOSTER); gphosterauth.setAttribute(AMLUtil.ATT_AUTH, getAce().getGpHosterAuth() + ""); atlas.appendChild(gphosterauth); // <aml:supportedLanguages> // Loops over List of supported Languagecodes final Element supportedLanguages = document.createElementNS( AMLUtil.AMLURI, "supportedLanguages"); for (final String langcode : getAce().getLanguages()) { final Element language = document.createElementNS(AMLUtil.AMLURI, "language"); language.setAttribute("lang", langcode); supportedLanguages.appendChild(language); } atlas.appendChild(supportedLanguages); List<DpEntry<? extends ChartStyle>> unusedDpes = getAce() .getUnusedDpes(); // Loop over all data pool entries and add them to the AML Document for (final DpEntry de : getAce().getDataPool().values()) { if (exportMode && unusedDpes.contains(de)) continue; Node exDpe = null; checkCancel(); if (de instanceof DpLayer) { // Save the SLD for this layer final DpLayer dpl = (DpLayer) de; try { final Style style = dpl.getStyle(); StylingUtil.saveStyleToSld(style, DataUtilities .urlToFile(DataUtilities.changeUrlExt( AVSwingUtil.getUrl(dpl, statusWindow), "sld"))); } catch (final Exception e) { LOGGER.error("Could not transform Style for " + dpl, e); if (statusWindow != null) statusWindow.exceptionOccurred(e); } finally { // dpl.dispose(); } } if (de instanceof DpLayerVectorFeatureSource) { exDpe = exportDatapoolLayerVector(document, (DpLayerVectorFeatureSource) de); } else if (de instanceof DpLayerRaster) { exDpe = exportDatapoolLayerRaster(document, (DpLayerRaster) de); } else if (de instanceof DpMediaVideo) { exDpe = exportDatapoolMediaVideo(document, (DpMediaVideo) de); } else if (de instanceof DpMediaPDF) { exDpe = exportDatapoolMediaPdf(document, (DpMediaPDF) de); } else if (de instanceof DpMediaPICTURE) { exDpe = exportDatapoolMediaPicture(document, (DpMediaPICTURE) de); } atlas.appendChild(exDpe); } // The <aml:maps> tag atlas.appendChild(exportMapPool(document)); // The <aml:group> tag atlas.appendChild(exportGroup(document, getAce().getRootGroup())); // The <aml:fonts> tag atlas.appendChild(exportFonts(document)); document.appendChild(atlas); // LOGGER.debug("AtlasConfig converted to JDOM Document"); return document; } private Node exportDatapoolMediaPicture(Document document, final DpMediaPICTURE dpe) { // LOGGER.debug("exportDatapoolMediaPDF " + dpe + " to AML"); final Element element = document.createElementNS(AMLUtil.AMLURI, "pictureMedia"); element.setAttribute("id", dpe.getId()); element.setAttribute("exportable", dpe.isExportable().toString()); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", dpe.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", dpe.getDesc())); // Creating optinal aml:keywords tag if (!dpe.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", dpe.getKeywords())); // Creating a aml:dataDirname tag... final Element datadirname = document.createElementNS(AMLUtil.AMLURI, "dataDirname"); datadirname.appendChild(document.createTextNode(dpe.getDataDirname())); element.appendChild(datadirname); // Creating a aml:filename tag... final Element filename = document.createElementNS(AMLUtil.AMLURI, "filename"); filename.appendChild(document.createTextNode(dpe.getFilename())); element.appendChild(filename); return element; } /** * Creates an aml:fonts tag that lists the names of all the fonts files in * ad/font folder. The list of fonts is determined while */ protected Node exportFonts(Document document) { final Element fontsElement = document.createElementNS(AMLUtil.AMLURI, AMLUtil.TAG_FONTS); File fontsDir = getAce().getFontsDir(); Collection<File> listFiles = FileUtils.listFiles(fontsDir, GpUtil.FontsFilesFilter, FilterUtil.BlacklistedFoldersFilter); for (File f : listFiles) { // Test whether the font is readable String relPath = f.getAbsolutePath().substring( fontsDir.getAbsolutePath().length() + 1); try { Font.createFont(Font.TRUETYPE_FONT, f); final Element fontElement = document.createElementNS( AMLUtil.AMLURI, AMLUtil.TAG_FONT); fontElement.setAttribute(AMLUtil.ATT_FONT_FILENAME, relPath); fontsElement.appendChild(fontElement); } catch (Exception e) { LOGGER.info("Not exporting a reference to broken font " + relPath + " in " + fontsDir, e); } } return fontsElement; } private void checkCancel() throws AtlasCancelException { if (statusWindow != null && statusWindow.isCanceled()) throw new AtlasCancelException(); } private Node exportDatapoolMediaPdf(final Document document, final DpMediaPDF dpe) { // LOGGER.debug("exportDatapoolMediaPDF " + dpe + " to AML"); final Element element = document.createElementNS(AMLUtil.AMLURI, "pdfMedia"); element.setAttribute("id", dpe.getId()); element.setAttribute("exportable", dpe.isExportable().toString()); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", dpe.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", dpe.getDesc())); // Creating optinal aml:keywords tag if (!dpe.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", dpe.getKeywords())); // Creating a aml:dataDirname tag... final Element datadirname = document.createElementNS(AMLUtil.AMLURI, "dataDirname"); datadirname.appendChild(document.createTextNode(dpe.getDataDirname())); element.appendChild(datadirname); // Creating a aml:filename tag... final Element filename = document.createElementNS(AMLUtil.AMLURI, "filename"); filename.appendChild(document.createTextNode(dpe.getFilename())); element.appendChild(filename); return element; } /** * Exports all the {@link Map}s that are defined in {@link MapPool} * * @param document * {@link Document} to create the element for * @param mapPool * {@link MapPool} * @throws AtlasExportException * @throws DOMException * @throws AtlasCancelException */ private Node exportMapPool(final Document document) throws DOMException, AtlasExportException, AtlasCancelException { final MapPool mapPool = getAce().getMapPool(); final Element element = document.createElementNS(AMLUtil.AMLURI, "mapPool"); // Test whether a start map exists. When no start-map is defined, the // user can not export. if (mapPool.getStartMapID() != null && mapPool.get(mapPool.getStartMapID()) != null) { element.setAttribute("startMap", mapPool.getStartMapID()); } else { if (exportMode) throw new AtlasExportException( GpUtil.R("ExportAtlas.NeedAtLeastOneMap")); } // maps MUST contain at least one map final Collection<Map> maps = mapPool.values(); List<String> notReferencedDpeIDs = getAce() .lisIdsNotReferencedInGroupTree(); for (final Map map : maps) { checkCancel(); if (exportMode && notReferencedDpeIDs.contains(map.getId()) && !map.getId().equals(mapPool.getStartMapID())) { // Only export maps that are referenced in the group tree or are // marked as the start map continue; } element.appendChild(exportMap(document, map)); } return element; } /** * Exports a {@link Map} to AtlasML * * @param document * @param map * The {@link Map} to export * @return * @throws AtlasExportException */ private Node exportMap(final Document document, final Map map) throws AtlasExportException { final Element element = document.createElementNS(AMLUtil.AMLURI, "map"); element.setAttribute("id", map.getId()); // Save the ratio of the left and the right components of the mapview final Double preferredRatio = map.getLeftRightRatio(); if (preferredRatio != null && preferredRatio > 0) { element.setAttribute("leftRightRatio", preferredRatio.toString()); } // whether any maxMapExtend should be applied in Geopublisher's // MapComposer element.setAttribute(AMLUtil.ATT_PREVIEW_MAX_MAPEXTEND_IN_GP, String.valueOf(map.isPreviewMapExtendInGeopublisher())); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", map.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", map.getDesc())); // Creating optinal aml:keywords tag if (!map.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", map.getKeywords())); // Creating optional startViewEnvelope tag ... if (map.getDefaultMapArea() != null) { final Element startViewEnvelope = document.createElementNS( AMLUtil.AMLURI, "startViewEnvelope"); final Envelope area = map.getDefaultMapArea(); startViewEnvelope.appendChild(document.createTextNode(area .getMinX() + "," + area.getMinY() + " " + area.getMaxX() + "," + area.getMaxY())); element.appendChild(startViewEnvelope); } // Creating optional maxMapExtend tag ... if (map.getMaxExtend() != null) { final Element maxExtend = document.createElementNS(AMLUtil.AMLURI, "maxExtend"); final Envelope area = map.getMaxExtend(); maxExtend.appendChild(document.createTextNode(area.getMinX() + "," + area.getMinY() + " " + area.getMaxX() + "," + area.getMaxY())); element.appendChild(maxExtend); } // Is the ScalePanel visible in this map? element.setAttribute(AMLUtil.ATT_MAP_SCALE_VISIBLE, Boolean.valueOf(map.isScaleVisible()).toString()); // The units used in the ScalePanel? element.setAttribute(AMLUtil.ATT_MAP_SCALE_UNITS, map.getScaleUnits() .toString()); // Are the vert. and hor. GridPanels visible in this map? element.setAttribute("gridPanelVisible", Boolean.valueOf(map.isGridPanelVisible()).toString()); // Which formatter to use for the map grid? element.setAttribute("gridPanelFormatter", map.getGridPanelFormatter() .getId()); // That .prj file store the CRS used in the map grid panel (de.: // Kartenrand) final File outputMapGridPanelPrjFile = new File(getAce().getHtmlDirFor( map), Map.GRIDPANEL_CRS_FILENAME); try { GeoExportUtil.writeProjectionFilePrefereEPSG(map.getGridPanelCRS(), outputMapGridPanelPrjFile); } catch (final IOException e1) { throw new AtlasExportException("Failed to save CRS: " + map.getGridPanelCRS() + " to " + outputMapGridPanelPrjFile, e1); } // Creating aml:datapoolRef tags for the layers and media of this map for (final DpRef dpr : map.getLayers()) { element.appendChild(exportDatapoolRef(document, dpr, map)); } for (final DpRef dpr : map.getMedia()) { element.appendChild(exportDatapoolRef(document, dpr)); } /*********************************************************************** * Exporting map.getAdditionalStyles and the selected style ID s */ for (final String layerID : map.getAdditionalStyles().keySet()) { final List<String> styles = map.getAdditionalStyles().get(layerID); if (styles.size() == 0) continue; final Element additionalStyles = document.createElementNS( AMLUtil.AMLURI, AMLUtil.TAG_ADDITIONAL_STYLES); additionalStyles.setAttribute("layerID", layerID); final String selectedStyleID = map.getSelectedStyleID(layerID); if (selectedStyleID != null) additionalStyles.setAttribute("selectedStyleID", selectedStyleID); for (final String styleID : styles) { final Element styleidElement = document.createElementNS( AMLUtil.AMLURI, "styleid"); styleidElement.appendChild(document.createTextNode(styleID)); additionalStyles.appendChild(styleidElement); } element.appendChild(additionalStyles); } /*********************************************************************** * Exporting map.getAvailableCharts */ for (final String layerID : map.getAvailableCharts().keySet()) { if (!map.getAc().getDataPool().containsKey(layerID)) { // The corresponing DPE has been deleted?! continue; } final List<String> chartIDs = map.getAvailableChartIDsFor(layerID); if (chartIDs.size() == 0) continue; final Element availableCharts = document.createElementNS( AMLUtil.AMLURI, "availableCharts"); availableCharts.setAttribute("layerID", layerID); for (final String chartID : chartIDs) { final Element styleidElement = document.createElementNS( AMLUtil.AMLURI, "chartID"); styleidElement.appendChild(document.createTextNode(chartID)); availableCharts.appendChild(styleidElement); } element.appendChild(availableCharts); } return element; } /** * Creates an < aml:desc > JDOM {@link Node} for the given * {@link Translation} */ private final Element exportTranslation(final Document document, final String tagname, final Translation translation) { final Element element = document.createElementNS(AMLUtil.AMLURI, tagname); // Creating a sequence of <aml:translation> tags, minOccurs=1 if (translation == null) { final String warning = "No translation given for " + tagname + "\nAtlasMarkupLanguage will probably not be valid."; if (statusWindow != null) { statusWindow.warningOccurred(this.getClass().getSimpleName(), null, warning); } else Log.warn(warning); } else { if (translation.size() == 0) { for (final String code : getAce().getLanguages()) { translation.put(code, ""); } } for (final String key : translation.keySet()) { final Element descTranslation = document.createElementNS( AMLUtil.AMLURI, "translation"); descTranslation.setAttribute("lang", key); String string = translation.get(key); if (string == null) string = ""; descTranslation.appendChild(document.createTextNode(string)); element.appendChild(descTranslation); } } return element; } private final Element exportDatapoolMediaVideo(final Document document, final DpMediaVideo dpe) { // LOGGER.debug("exportDatapoolMediaVideo " + dpe + " to AML"); final Element element = document.createElementNS(AMLUtil.AMLURI, "videoMedia"); element.setAttribute("id", dpe.getId()); element.setAttribute("exportable", dpe.isExportable().toString()); // info(dpe.getTitle()); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", dpe.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", dpe.getDesc())); // Creating optinal aml:keywords tag if (!dpe.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", dpe.getKeywords())); // Creating a aml:dataDirname tag... final Element datadirname = document.createElementNS(AMLUtil.AMLURI, "dataDirname"); datadirname.appendChild(document.createTextNode(dpe.getDataDirname())); element.appendChild(datadirname); // Creating a aml:filename tag... final Element filename = document.createElementNS(AMLUtil.AMLURI, "filename"); filename.appendChild(document.createTextNode(dpe.getFilename())); element.appendChild(filename); return element; } /** * Exports the DatapoolEntry to an AtlasML (XML) document branch * * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a> * @throws IOException * Thrown if e.g. saving the .cpg fails */ private final Element exportDatapoolLayerVector(final Document document, final DpLayerVectorFeatureSource dpe) throws IOException { if (dpe.isBroken()) { LOGGER.info("Trying to save a broken layer..." + dpe); } /* * Saving the Charset to a .cpg file */ GpUtil.writeCpg(dpe); // Creating a aml:rasterLayer tag... final Element element = document.createElementNS(AMLUtil.AMLURI, "vectorLayer"); element.setAttribute("id", dpe.getId()); element.setAttribute("exportable", dpe.isExportable().toString()); /*********************************************************************** * The "showStylerInLegend" attribute is optional and defaults to true ***********************************************************************/ if (dpe.isStylerInLegend()) { element.setAttribute("showStylerInLegend", "true"); } else { element.setAttribute("showStylerInLegend", "false"); } /*********************************************************************** * The "showTableInLegend" attribute is optional and defaults to false ***********************************************************************/ if (dpe.isTableVisibleInLegend()) { element.setAttribute("showTableInLegend", "true"); } else { element.setAttribute("showTableInLegend", "false"); } /*********************************************************************** * The "filterInLegend" attribute is optional and defaults to false ***********************************************************************/ if (dpe.isFilterInLegend()) { element.setAttribute("showFilterInLegend", "true"); } else { element.setAttribute("showFilterInLegend", "false"); } // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", dpe.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", dpe.getDesc())); // Creating optinal aml:keywords tag if (!dpe.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", dpe.getKeywords())); // Creating a aml:dataDirname tag... final Element datadirname = document.createElementNS(AMLUtil.AMLURI, "dataDirname"); datadirname.appendChild(document.createTextNode(dpe.getDataDirname())); element.appendChild(datadirname); // Creating a aml:filename tag... final Element filename = document.createElementNS(AMLUtil.AMLURI, "filename"); filename.appendChild(document.createTextNode(dpe.getFilename())); element.appendChild(filename); for (final AttributeMetadataImpl attrib : dpe.getAttributeMetaDataMap() .values()) { final Element att = exportAttributeMetadata(dpe, document, attrib); if (att != null) element.appendChild(att); } /** * Exporting the additional and optional LayerStyles */ for (final LayerStyle ls : dpe.getLayerStyles()) { element.appendChild(exportLayerStyle(document, ls)); } /** * This parameter is optional and is only created if needed. */ final Filter filter = dpe.getFilter(); if (filter != Filter.INCLUDE) { // Creating a aml:filename tag... final Element filterRuleElement = document.createElementNS( AMLUtil.AMLURI, "filterRule"); filterRuleElement.appendChild(document.createTextNode(CQL .toCQL(filter))); element.appendChild(filterRuleElement); } /** * Exporting the list of charts for this layer if any exist. This has to * be called, AFTER the attribute meta data has been exported. (When * parsing the XML, we need the NODATA values first) */ exportChartStyleDescriptions(document, dpe, element); return element; } /** * Exports the list of charts for this layer. Any old chart style files are * first deleted. */ private void exportChartStyleDescriptions(final Document document, final DpLayerVectorFeatureSource dpe, final Element element) { final AtlasConfigEditable ace = (AtlasConfigEditable) dpe .getAtlasConfig(); final File chartsFolder = new File(ace.getFileFor(dpe).getParentFile(), "charts"); chartsFolder.mkdirs(); /* * Delete all .cs file before saving the charts that actually exist */ { final String[] oldChartFilenames = chartsFolder .list(new FilenameFilter() { @Override public boolean accept(final File dir, final String name) { if (name.endsWith(CHARTSTYLE_FILEENDING)) return true; return false; } }); for (final String oldChartFilename : oldChartFilenames) { final File chartSTyeFile = new File(chartsFolder, oldChartFilename); if (!chartSTyeFile.delete()) throw new IllegalArgumentException( "Unable to delte the old chart description file " + chartSTyeFile.getAbsolutePath()); } } /* Iterate over all charts and create .chart files */ for (final FeatureChartStyle chartStyle : dpe.getCharts()) { final String csFilename = chartStyle.getID() + CHARTSTYLE_FILEENDING; final File chartFile = new File(chartsFolder, csFilename); try { /* * Write the Chart to XML */ // System.out.println("when saving\n"+" "+chartStyle); if (chartStyle instanceof FeatureChartStyle) { FeatureChartUtil.FEATURE_CHART_STYLE_FACTORY .writeStyleToFile(chartStyle, "chartStyle", chartFile); } else { ChartStyleUtil.CHART_STYLE_FACTORY.writeStyleToFile( chartStyle, "chartStyle", chartFile); } } catch (final Exception e) { LOGGER.error("Error writing the ChartStyle to XML ", e); } // Create an aml:chart tag... final Element chartElement = document.createElementNS( AMLUtil.AMLURI, "chart"); chartElement.setAttribute("filename", csFilename); element.appendChild(chartElement); } } /** * Exports a <layerStyle> tag * * @param ac * @param document * @param ls * {@link LayerStyle} to export * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a> */ private Element exportLayerStyle(final Document document, final LayerStyle ls) { final Element element = document.createElementNS(AMLUtil.AMLURI, AMLUtil.TAG_ADDITIONAL_STYLE); element.setAttribute("filename", ls.getFilename()); element.setAttribute("id", ls.getFilename()); element.appendChild(exportTranslation(document, "title", ls.getTitle())); element.appendChild(exportTranslation(document, "desc", ls.getDesc())); return element; } /** * Exports one single {@link AttributeMetadataImpl} to an aml:dataAttribute * tag. * * @return {@link org.w3c.dom.Element} that represents the XML tag * * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a> */ private Element exportAttributeMetadata( final DpLayerVectorFeatureSource dpe, final Document document, final AttributeMetadataImpl attrib) { final Element element = document.createElementNS(AMLUtil.AMLURI, AMLUtil.TAG_attributeMetadata); element.setAttribute(AMLUtil.ATT_namespace, attrib.getName() .getNamespaceURI()); element.setAttribute(AMLUtil.ATT_localname, attrib.getName() .getLocalPart()); element.setAttribute(AMLUtil.ATT_weight, new Integer(new Double(attrib.getWeight()).intValue()) .toString()); element.setAttribute(AMLUtil.ATT_functionA, new Double(attrib.getFunctionA()).toString()); element.setAttribute(AMLUtil.ATT_functionX, new Double(attrib.getFunctionX()).toString()); element.setAttribute("visible", String.valueOf(attrib.isVisible())); if (attrib.getUnit() != null && !attrib.getUnit().isEmpty()) element.setAttribute("unit", attrib.getUnit()); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", attrib.getTitle())); // Creating a aml:desc tag... element.appendChild(exportTranslation(document, "desc", attrib.getDesc())); // Storing the NODATA values for (Object nodatavalue : attrib.getNodataValues()) { if (nodatavalue == null) continue; Element ndValue = document.createElementNS(AMLUtil.AMLURI, AMLUtil.TAG_nodataValue); ndValue.setTextContent(nodatavalue.toString()); element.appendChild(ndValue); } return element; } /** * Exports the {@link DpLayerRaster} to a AtlasML (XML) Document branch * * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a> */ private final Element exportDatapoolLayerRaster(final Document document, final DpLayerRaster<? extends Object, ChartStyle> dpe) { // Creating a aml:rasterLayer tag... final Element element = document.createElementNS(AMLUtil.AMLURI, "rasterLayer"); element.setAttribute("id", dpe.getId()); element.setAttribute("exportable", dpe.isExportable().toString()); if (dpe.getNodataValue() != null) element.setAttribute(AMLUtil.ATT_NODATA, String.valueOf(dpe.getNodataValue())); // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", dpe.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", dpe.getDesc())); // Creating optinal aml:keywords tag if (!dpe.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", dpe.getKeywords())); // Creating optional aml:bands tag if (dpe.getBandNames().length != 0) { Integer bandCount = 0; for (Translation a : dpe.getBandNames()) { Element translation = exportTranslation(document, "band", a); translation.setAttribute("number", bandCount.toString()); element.appendChild(translation); bandCount++; } } // Creating a aml:dataDirname tag... final Element datadirname = document.createElementNS(AMLUtil.AMLURI, "dataDirname"); datadirname.appendChild(document.createTextNode(dpe.getDataDirname())); element.appendChild(datadirname); // Creating a aml:filename tag... final Element filename = document.createElementNS(AMLUtil.AMLURI, "filename"); filename.appendChild(document.createTextNode(dpe.getFilename())); element.appendChild(filename); /** * Exporting the additional and optional LayerStyles */ for (final LayerStyle ls : dpe.getLayerStyles()) { element.appendChild(exportLayerStyle(document, ls)); } // // // Creating aml:rasterLegendData // element.appendChild(exportRasterLegendData(document, // dpe.getLegendMetaData())); return element; } // /** // * Export a aml:rasterLegendData tag // * // * @param document // * @param legendMetaData // * @return // * // * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons // Tzeggai</a> // */ // private Node exportRasterLegendData(final Document document, // final RasterLegendData legendMetaData) { // final Element element = document.createElementNS(AMLUtil.AMLURI, // "rasterLegendData"); // element.setAttribute(AMLUtil.ATT_paintGaps, legendMetaData // .isPaintGaps().toString()); // // for (final Double key : legendMetaData.getSortedKeys()) { // final Element item = document.createElementNS(AMLUtil.AMLURI, // "rasterLegendItem"); // item.setAttribute("value", key.toString()); // item.appendChild(exportTranslation(document, "label", // legendMetaData.get(key))); // element.appendChild(item); // } // return element; // } /** * Create a tree of <aml:group> and <aml:datapoolRef> * * @param group * The {@link Group} to start with * @throws AtlasFatalException */ private final Element exportGroup(final Document document, final Group group) throws AtlasFatalException { // LOGGER.debug("exportGroup " + group + " to AML"); final Element element = document.createElementNS(AMLUtil.AMLURI, "group"); // Store whether this is marked as the Help menu in an optional // attribute if (group.isHelpMenu()) { final String isHelpString = Boolean.valueOf(group.isHelpMenu()) .toString(); element.setAttribute("isHelpMenu", isHelpString); } // Store whether this is marked as the File menu in an optional // attribute if (group.isFileMenu()) { final String isFileString = Boolean.valueOf(group.isFileMenu()) .toString(); element.setAttribute("isFileMenu", isFileString); } // Creating a aml:name tag... element.appendChild(exportTranslation(document, "name", group.getTitle())); // Creating aml:desc tag element.appendChild(exportTranslation(document, "desc", group.getDesc())); // Creating optional aml:keywords tag if (!group.getKeywords().isEmpty()) element.appendChild(exportTranslation(document, "keywords", group.getKeywords())); final Enumeration<TreeNode> children = group.children(); while (children.hasMoreElements()) { final TreeNode nextElement = children.nextElement(); if (nextElement instanceof Group) { final Group subGroup = (Group) nextElement; element.appendChild(exportGroup(document, subGroup)); } else if (nextElement instanceof DpRef) { final DpRef mref = (DpRef) nextElement; element.appendChild(exportDatapoolRef(document, mref)); } else if (nextElement instanceof MapRef) { final MapRef mref = (MapRef) nextElement; final Element datapoolRef = document.createElementNS( AMLUtil.AMLURI, "mapRef"); datapoolRef.setAttribute("id", mref.getTargetId()); element.appendChild(datapoolRef); } else { throw new AtlasFatalException("Can't export Group " + group + " because of an unknown TreeNode " + nextElement + " of class " + nextElement.getClass().getSimpleName()); } } return element; } /** * Creates an aml:datapoolRef tag * * @param ref * {@link DpRef} * @return A node that can be inserted into XML */ private Element exportDatapoolRef(final Document document, final DpRef ref) { final Element datapoolRef = document.createElementNS(AMLUtil.AMLURI, "datapoolRef"); datapoolRef.setAttribute("id", ref.getTargetId()); return datapoolRef; } /** * DataPoolRefs used inside a <code>aml:map</code> definition can have two * more attributes which are saved inside a {@link java.util.Map} in the * {@link Map}. * * @param document * @param dpr * @param map * @return A node that can be inserted into XML */ private Node exportDatapoolRef(final Document document, final DpRef dpr, final Map map) { final Element datapoolRef = exportDatapoolRef(document, dpr); { // Add the optional hideInLegend attribute if it is set. false = // default final Boolean hideme = map.getHideInLegendMap().get( dpr.getTargetId()); if (hideme != null && hideme == true) { datapoolRef.setAttribute("hideInLegend", "true"); } } { // Add the optional minimizeInLegend attribute if it is set. false = // default final Boolean minimizeMe = map.getMinimizedInLegendMap().get( dpr.getTargetId()); if (minimizeMe != null && minimizeMe == true) { datapoolRef.setAttribute("minimizeInLegend", "true"); } } { // Add the optional hidden attribute if it is set. false = // default final Boolean hidden = map.getHiddenFor(dpr.getTargetId()); if (hidden != null && hidden == true) { datapoolRef.setAttribute("hidden", "true"); } } { // Add the optional selectable attribute if it is set. true = // default final Boolean selectable = map.isSelectableFor(dpr.getTargetId()); if (selectable != null && selectable == false) { datapoolRef.setAttribute("selectable", "false"); } } return datapoolRef; } /** * not in use since 2009 */ private void copyAtlasMLSchemaFile() { try { // Copy Schema AtlasML.xsd to projectDir LOGGER.debug("Copy Schema AtlasML.xsd into " + getAce().getAd()); URL resourceSchema = AtlasConfig.class .getResource("resource/AtlasML.xsd"); LOGGER.debug("schemaURL = " + resourceSchema); if (resourceSchema == null) { LOGGER.debug("schemaURL == null, try the new way"); final String location = "skrueger/atlas/resource/AtlasML.xsd"; resourceSchema = getAce().getResLoMan().getResourceAsUrl( location); // LOGGER.debug("schemaURL (new) = " + resourceSchema); } // File schemaFile = new File(resourceSchema.toURI()); org.apache.commons.io.FileUtils.copyURLToFile(resourceSchema, new File(getAtlasXml().getParentFile(), "AtlasML.xsd")); } catch (final Exception e) { LOGGER.debug(" Error while copying AtlasML.xsd... ignoring"); // if (statusWindow != null)statusWindow.exceptionOccurred(new // AtlasException(e)); } } /** * Keeps up to three three backups of the atlas.xml file. Returns true if * atlas.xml existed. * * @throws IOException * if files can't be created. */ private boolean backupAtlasXML() throws IOException { if (isExportMode()) return false; File atlasXml = getAtlasXml(); File bak1 = new File(atlasXml.getParentFile(), atlasXml.getName() + ".bak"); File bak2 = new File(atlasXml.getParentFile(), atlasXml.getName() + ".bak.bak"); File bak3 = new File(atlasXml.getParentFile(), atlasXml.getName() + ".bak.bak.bak"); if (bak2.exists()) IOUtil.copyFile(LOGGER, bak2, bak3, false); if (bak1.exists()) IOUtil.copyFile(LOGGER, bak1, bak2, false); if (atlasXml.exists()) { IOUtil.copyFile(LOGGER, atlasXml, bak1, false); return true; } return false; } /** * If set to <code>true</code>, the created atlas.xml only describes maps * and layers that are actually used/referenced in the atlas. To save the * atlas as an 'atlas working copy' with all the information in it, it has * to be set to <code>false</code>. */ public void setExportMode(boolean exportMode) { this.exportMode = exportMode; } /** * If set to <code>true</code>, the created atlas.xml only describes maps * and layers that are actually used/referenced in the atlas. To save the * atlas as an 'atlas working copy' with all the information in it, it has * to be set to <code>false</code>. */ public boolean isExportMode() { return exportMode; } /** * The {@link File} to write the {@link AtlasConfig} to. Defaults to * ad/atlas.xml in the {@link AtlasConfig}'s dir. */ public void setAtlasXml(File atlasXml) { this.atlasXml = atlasXml; } /** * @return The {@link File} the {@link AtlasConfig} will be written to in * AtlasML. Defaults to ad/atlas.xml in the {@link AtlasConfig}'s * dir. */ public File getAtlasXml() { if (atlasXml == null) { atlasXml = new File(getAce().getAd(), AtlasConfig.ATLAS_XML_FILENAME); } return atlasXml; } /** * Retusn the {@link AtlasConfigEditable} that is being exported by this * {@link AMLExporter} instance **/ public AtlasConfigEditable getAce() { return ace; } }