/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web.wicket; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.wicket.AttributeModifier; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.basic.MultiLineLabel; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponentPanel; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * A form component for a {@link CoordinateReferenceSystem} object. * <p> * This panel provides the following functionality/information: * <ul> * <li>The SRS (epsg code) of the CRS * <li>View the full WKT of the CRS. * <li>A mechanism to guess the SRS (epsg code) from the CRS * <li>A lookup for browsing for a particular CRS * </ul> * </p> * The panel * @author Justin Deoliveira, OpenGeo */ @SuppressWarnings("serial") public class CRSPanel extends FormComponentPanel<CoordinateReferenceSystem> { private static Logger LOGGER = Logging.getLogger(CRSPanel.class); private static final long serialVersionUID = -6677103383336166008L; private static Behavior READ_ONLY = new AttributeModifier("readonly", new Model<String>("readonly")); /** pop-up window for WKT and SRS list */ protected ModalWindow popupWindow; /** srs/epsg code text field */ protected TextField<String> srsTextField; /** find link */ protected AjaxLink<Void> findLink; /** wkt label */ protected Label wktLabel; /** the wkt link that contains the wkt label **/ protected GeoServerAjaxFormLink wktLink; /** * Constructs the CRS panel. * <p> * This constructor should be used if the panel is to inherit from a parent model * (ie a CompoundPropertyModel). If no such model is available the CRS will be left * uninitialized. To avoid inheriting from a parent model the constructor {@link #CRSPanel(String, IModel)} * should be used, specifying explicitly an uninitialized model. *</p> * @param id The component id. */ public CRSPanel(String id) { super(id); initComponents(); } /** * Constructs the CRS panel with an explicit model. * * @param id The component id. * @param model The model, usually a {@link PropertyModel}. */ public CRSPanel(String id, IModel<CoordinateReferenceSystem> model) { super(id, model); initComponents(); } /** * Constructs the CRS panel specifying the underlying CRS explicitly. * <p> * When this constructor is used the {@link #getCRS()} method should be used * after the form is submitted to retrieve the final value of the CRS. * </p> * @param id The component id. * @param crs The underlying CRS object. */ public CRSPanel(String id, CoordinateReferenceSystem crs ) { //JD: while the CoordinateReferenceSystem interface does not implement Serializable // all the CRS objects we use do, hence the cast super(id, new CRSModel(crs)); initComponents(); setConvertedInput(crs); } /* * helper for internally creating the panel. */ void initComponents() { popupWindow = new ModalWindow("popup"); add( popupWindow ); srsTextField = new TextField<String>( "srs", new Model<String>() ); add( srsTextField ); srsTextField.setOutputMarkupId( true ); srsTextField.add(new AjaxFormComponentUpdatingBehavior("blur") { @Override protected void onUpdate(AjaxRequestTarget target) { convertInput(); CoordinateReferenceSystem crs = (CoordinateReferenceSystem) getConvertedInput(); if(crs != null) { setModelObject(crs); wktLabel.setDefaultModelObject(crs.getName().toString()); wktLink.setEnabled(true); } else { wktLabel.setDefaultModelObject(null); wktLink.setEnabled(false); } target.add(wktLink); onSRSUpdated(toSRS(crs), target); } }); findLink = new AjaxLink<Void>( "find" ) { @Override public void onClick(AjaxRequestTarget target) { popupWindow.setContent(srsListPanel()); popupWindow.setTitle(new ParamResourceModel("selectSRS", CRSPanel.this)); popupWindow.show(target); } }; add(findLink); wktLink = new GeoServerAjaxFormLink("wkt") { @Override public void onClick(AjaxRequestTarget target, Form<?> form) { popupWindow.setInitialHeight( 375 ); popupWindow.setInitialWidth( 525 ); popupWindow.setContent(new WKTPanel( popupWindow.getContentId(), getCRS())); CoordinateReferenceSystem crs = (CoordinateReferenceSystem) CRSPanel.this.getModelObject(); if(crs != null) popupWindow.setTitle(crs.getName().toString()); popupWindow.show(target); } }; wktLink.setEnabled(getModelObject() != null); add(wktLink); wktLabel = new Label( "wktLabel", new Model<String>()); wktLink.add( wktLabel ); wktLabel.setOutputMarkupId( true ); } @Override protected void onBeforeRender() { CoordinateReferenceSystem crs = (CoordinateReferenceSystem) getModelObject(); if ( crs != null ) { srsTextField.setModelObject( toSRS(crs) ); wktLabel.setDefaultModelObject( crs.getName().toString() ); } else { wktLabel.setDefaultModelObject(null); wktLink.setEnabled(false); } super.onBeforeRender(); } @Override public void convertInput() { String srs = srsTextField.getInput(); CoordinateReferenceSystem crs = null; if ( srs != null && !"".equals(srs)) { if ( "UNKNOWN".equals( srs ) ) { //leave underlying crs unchanged if ( getModelObject() instanceof CoordinateReferenceSystem ) { setConvertedInput(getModelObject()); } return; } crs = fromSRS( srs ); } setConvertedInput(crs ); } /** * Subclasses can override to perform custom behaviors when the SRS is updated, which happens * either when the text field is left or when the find dialog returns * @param target */ protected void onSRSUpdated(String srs, AjaxRequestTarget target) { // do nothing by default } /** * Sets the panel to be read only. */ public CRSPanel setReadOnly( boolean readOnly ) { if(readOnly) srsTextField.add(READ_ONLY); else srsTextField.remove(READ_ONLY); findLink.setVisible( !readOnly ); return this; } /** * Returns the underlying CRS for the panel. * <p> * This method is convenience for: * <pre> * (CoordinateReferenceSystem) this.getModelObject(); * </pre> * </p> */ public CoordinateReferenceSystem getCRS() { // convertInput(); return getModelObject(); } /* * Goes from CRS to SRS. */ String toSRS( CoordinateReferenceSystem crs ) { try { if(crs != null) { Integer epsgCode = CRS.lookupEpsgCode(crs, false); return epsgCode != null ? "EPSG:" + epsgCode : "UNKNOWN"; } else { return "UNKNOWN"; } } catch (Exception e) { LOGGER.log(Level.WARNING, "Could not succesffully lookup an EPSG code", e); return null; } } /* * Goes from SRS to CRS. */ protected CoordinateReferenceSystem fromSRS( String srs ) { try { return CRS.decode( srs ); } catch (Exception e) { LOGGER.log(Level.WARNING, "Unknown EPSG code " + srs, e); return null; } } /* * Builds the srs list panel component. */ protected SRSListPanel srsListPanel() { SRSListPanel srsList = new SRSListPanel(popupWindow.getContentId()) { @Override protected void onCodeClicked(AjaxRequestTarget target, String epsgCode) { popupWindow.close(target); String srs = "EPSG:" + epsgCode ; srsTextField.setModelObject( srs ); target.add( srsTextField ); CoordinateReferenceSystem crs = fromSRS( srs ); CRSPanel.this.setModelObject(crs ); if (crs != null) { wktLabel.setDefaultModelObject(crs.getName().toString()); wktLink.setEnabled(true); } else { wktLabel.setDefaultModelObject(null); wktLink.setEnabled(false); } target.add( wktLink ); onSRSUpdated(srs, target); } }; srsList.setCompactMode(true); return srsList; } /* * Panel for displaying the well known text for a CRS. */ public static class WKTPanel extends Panel { public WKTPanel(String id, CoordinateReferenceSystem crs) { super(id); MultiLineLabel wktLabel = new MultiLineLabel("wkt"); add( wktLabel ); if ( crs != null ) { wktLabel.setDefaultModel( new Model<String>( crs.toString() ) ); } } } }