package org.geoserver.community.css.web; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.List; import java.util.ArrayList; import org.apache.wicket.PageParameters; import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.extensions.ajax.markup.html.tabs.AjaxTabbedPanel; import org.apache.wicket.extensions.markup.html.tabs.AbstractTab; import org.apache.wicket.extensions.markup.html.tabs.ITab; import org.apache.wicket.extensions.markup.html.tabs.PanelCachingTab; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.EmptyPanel; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.geoscript.geocss.CssParser; import org.geoscript.geocss.Translator; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.web.GeoServerSecuredPage; import org.geoserver.web.wicket.ParamResourceModel; import org.geotools.styling.Style; import org.geotools.styling.SLDTransformer; public class CssDemoPage extends GeoServerSecuredPage { /* * @TODO: externalize this string to a classpath resource */ public static String defaultStyle = "* { fill: lightgrey; }"; public OpenLayersMapPanel map = null; public Label sldPreview = null; private FeatureTypeInfo layer = null; private StyleInfo style = null; public CssDemoPage() { this(new PageParameters()); } public CssDemoPage(PageParameters params) { this.layer = extractLayer(params, getCatalog()); this.style = extractStyle(params, getCatalog(), this.layer); if (this.layer == null || this.style == null) { doErrorLayout(); } else { doMainLayout(); } } private static FeatureTypeInfo extractLayer(PageParameters params, Catalog catalog) { if (params.containsKey("layer")) { String[] name = params.getString("layer").split(":", 2); return catalog.getResourceByName(name[0], name[1], FeatureTypeInfo.class); } else { // TODO: Revisit this behavior // give some slight preference to the topp:states layer to make // demoing a bit more consistent. FeatureTypeInfo states = catalog.getResourceByName("topp", "states", FeatureTypeInfo.class); if (states != null) { return states; } else { List<FeatureTypeInfo> layers = catalog.getResources(FeatureTypeInfo.class); if (layers.size() > 0) { return layers.get(0); } else { return null; } } } } private static StyleInfo extractStyle(PageParameters params, Catalog catalog, FeatureTypeInfo layer) { if (params.containsKey("style")) { String style = params.getString("style"); return catalog.getStyleByName(style); } else { List<LayerInfo> styles = catalog.getLayers(layer); if (styles.size() > 0) { return styles.get(0).getDefaultStyle(); } else { return null; } } } public File findStyleFile(String name) { try { GeoServerDataDirectory datadir = new GeoServerDataDirectory(getCatalog().getResourceLoader()); File styleDir = datadir.findStyleDir(); return new File(styleDir, name); } catch (IOException ioe) { throw new WicketRuntimeException(ioe); } } public Catalog catalog() { return getCatalog(); } public StyleInfo getStyleInfo() { return this.style; } public FeatureTypeInfo getLayer() { return this.layer; } public String cssText2sldText(String css) { try { GeoServerDataDirectory datadir = new GeoServerDataDirectory(getCatalog().getResourceLoader()); File styleDir = datadir.findStyleDir(); scala.collection.Seq<org.geoscript.geocss.Rule> rules = CssParser.parse(css).get(); Translator translator = new Translator(scala.Option.apply(styleDir.toURI().toURL())); Style style = translator.css2sld(rules); SLDTransformer tx = new org.geotools.styling.SLDTransformer(); tx.setIndentation(2); ByteArrayOutputStream sldBytes = new java.io.ByteArrayOutputStream(); tx.transform(style, sldBytes); return sldBytes.toString(); } catch (Exception e) { throw new WicketRuntimeException(e); } } void createCssTemplate(String name) { try { Catalog catalog = getCatalog(); if (catalog.getStyleByName(name) == null) { StyleInfo style = catalog.getFactory().createStyle(); style.setName(name); style.setFilename(name + ".sld"); catalog.add(style); File sld = findStyleFile(style.getFilename()); if (sld == null || !sld.exists()) { catalog.getResourcePool().writeStyle( style, new ByteArrayInputStream(cssText2sldText(defaultStyle).getBytes()) ); } File css = findStyleFile(name + ".css"); if (!css.exists()) { FileWriter writer = new FileWriter(css); writer.write(defaultStyle); writer.close(); } } } catch (IOException ioe) { throw new WicketRuntimeException(ioe); } } private void doErrorLayout() { add(new Fragment("main-content", "loading-failure", this)); } private void doMainLayout() { Fragment mainContent = new Fragment("main-content", "normal", this); final ModalWindow popup = new ModalWindow("popup"); mainContent.add(popup); mainContent.add(new Label("style.name", new PropertyModel(style, "name"))); mainContent.add(new Label("layer.name", new PropertyModel(layer, "name"))); mainContent.add(new AjaxLink("change.style", new ParamResourceModel("CssDemoPage.changeStyle", this)) { public void onClick(AjaxRequestTarget target) { target.appendJavascript("Wicket.Window.unloadConfirmation = false;"); popup.setInitialHeight(400); popup.setInitialWidth(600); popup.setTitle(new Model("Choose style to edit")); popup.setContent(new StyleChooser(popup.getContentId(), CssDemoPage.this)); popup.show(target); } }); mainContent.add(new AjaxLink("change.layer", new ParamResourceModel("CssDemoPage.changeLayer", this)) { public void onClick(AjaxRequestTarget target) { target.appendJavascript("Wicket.Window.unloadConfirmation = false;"); popup.setInitialHeight(400); popup.setInitialWidth(600); popup.setTitle(new Model("Choose layer to edit")); popup.setContent(new LayerChooser(popup.getContentId(), CssDemoPage.this)); popup.show(target); } }); mainContent.add(new AjaxLink("create.style", new ParamResourceModel("CssDemoPage.createStyle", this)) { public void onClick(AjaxRequestTarget target) { target.appendJavascript("Wicket.Window.unloadConfirmation = false;"); popup.setInitialHeight(200); popup.setInitialWidth(300); popup.setTitle(new Model("Choose name for new style")); popup.setContent(new LayerNameInput(popup.getContentId(), CssDemoPage.this)); popup.show(target); } }); mainContent.add(new AjaxLink("associate.styles", new ParamResourceModel("CssDemoPage.associateStyles", this)) { public void onClick(AjaxRequestTarget target) { target.appendJavascript("Wicket.Window.unloadConfirmation = false;"); popup.setInitialHeight(400); popup.setInitialWidth(600); popup.setTitle(new Model("Choose layers to associate")); popup.setContent(new MultipleLayerChooser(popup.getContentId(), CssDemoPage.this)); popup.show(target); } }); final IModel<String> sldModel = new AbstractReadOnlyModel<String>() { public String getObject() { File file = findStyleFile(style.getFilename()); if (file != null && file.isFile()) { try { BufferedReader reader = new BufferedReader(new FileReader(file)); StringBuilder builder = new StringBuilder(); String line = null; do { line = reader.readLine(); builder.append(line); } while (line != null); reader.close(); return builder.toString(); } catch (IOException e) { throw new WicketRuntimeException(e); } } else { return "No SLD file found for this style. One will be generated automatically if you save the CSS."; } } }; final CompoundPropertyModel model = new CompoundPropertyModel<CssDemoPage>(CssDemoPage.this); List<ITab> tabs = new ArrayList<ITab>(); tabs.add(new AbstractTab(new Model("Collapse")) { public Panel getPanel(String id) { return new EmptyPanel(id); } }); tabs.add(new PanelCachingTab(new AbstractTab(new Model("Map")) { public Panel getPanel(String id) { return map = new OpenLayersMapPanel(id, layer, style); } })); tabs.add(new PanelCachingTab(new AbstractTab(new Model("Data")) { public Panel getPanel(String id) { try { return new DataPanel(id, model, layer); } catch (IOException e) { throw new WicketRuntimeException(e); } }; })); tabs.add(new PanelCachingTab(new AbstractTab(new Model("Generated SLD")) { public Panel getPanel(String id) { SLDPreviewPanel panel = new SLDPreviewPanel(id, sldModel); sldPreview = panel.getLabel(); return panel; } })); tabs.add(new AbstractTab(new Model("CSS Reference")) { public Panel getPanel(String id) { return new DocsPanel(id); } }); FeedbackPanel feedback2 = new FeedbackPanel("feedback-low"); feedback2.setOutputMarkupId(true); mainContent.add(feedback2); String cssSource = style.getFilename().replaceFirst("(\\.sld)?$", ".css"); mainContent.add(new StylePanel( "style.editing", model, CssDemoPage.this, getFeedbackPanel(), cssSource )); mainContent.add(new AjaxTabbedPanel("context", tabs)); add(mainContent); } }