/* (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 org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.FormComponentPanel;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope3D;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import com.vividsolutions.jts.geom.Envelope;
/**
* A form component for a {@link Envelope} object.
*
* @author Justin Deoliveira, OpenGeo
* @author Andrea Aime, OpenGeo
*/
public class EnvelopePanel extends FormComponentPanel<ReferencedEnvelope> {
private static final long serialVersionUID = -2975427786330616705L;
protected Label minXLabel, minYLabel, maxXLabel, maxYLabel, minZLabel, maxZLabel;
protected Double minX,minY,maxX,maxY,minZ,maxZ;
protected DecimalTextField minXInput, minYInput, maxXInput, maxYInput, minZInput, maxZInput;
protected CoordinateReferenceSystem crs;
protected WebMarkupContainer crsContainer;
protected CRSPanel crsPanel;
protected boolean crsRequired;
public EnvelopePanel(String id ) {
super(id);
initComponents();
}
public EnvelopePanel(String id, ReferencedEnvelope e) {
this(id, new Model<ReferencedEnvelope>(e));
}
public EnvelopePanel(String id, IModel<ReferencedEnvelope> model) {
super(id, model);
initComponents();
}
public void setCRSFieldVisible(boolean visible) {
crsContainer.setVisible(visible);
}
public boolean isCRSFieldVisible() {
return crsContainer.isVisible();
}
public boolean isCrsRequired() {
return crsRequired;
}
/**
* Makes the CRS bounds a required component of the envelope.
* It is warmly suggested that the crs field be made visible too
* @param crsRequired
*/
public void setCrsRequired(boolean crsRequired) {
this.crsRequired = crsRequired;
}
public boolean is3D(){
return crs != null && crs.getCoordinateSystem().getDimension() >= 3;
}
public void setLabelsVisibility(boolean visible) {
minXLabel.setVisible(visible);
minYLabel.setVisible(visible);
maxXLabel.setVisible(visible);
maxYLabel.setVisible(visible);
minZLabel.setVisible(visible && is3D());
maxZLabel.setVisible(visible && is3D());
}
void initComponents() {
updateFields();
add(minXLabel = new Label("minXL", new ResourceModel("minX")));
add(minYLabel = new Label("minYL", new ResourceModel("minY")));
add(minZLabel = new Label("minZL", new ResourceModel("minZ")));
add(maxXLabel = new Label("maxXL", new ResourceModel("maxX")));
add(maxYLabel = new Label("maxYL", new ResourceModel("maxY")));
add(maxZLabel = new Label("maxZL", new ResourceModel("maxZ")));
add( minXInput = new DecimalTextField( "minX", new PropertyModel<Double>(this, "minX")) );
add( minYInput = new DecimalTextField( "minY", new PropertyModel<Double>(this, "minY")) );
add( minZInput = new DecimalTextField( "minZ", new PropertyModel<Double>(this, "minZ")) );
add( maxXInput = new DecimalTextField( "maxX", new PropertyModel<Double>(this, "maxX") ));
add( maxYInput = new DecimalTextField( "maxY", new PropertyModel<Double>(this, "maxY")) );
add( maxZInput = new DecimalTextField( "maxZ", new PropertyModel<Double>(this, "maxZ")) );
minZInput.setVisible(is3D());
minZLabel.setVisible(is3D());
maxZInput.setVisible(is3D());
maxZLabel.setVisible(is3D());
crsContainer = new WebMarkupContainer("crsContainer");
crsContainer.setVisible(false);
crsPanel = new CRSPanel("crs", new PropertyModel<CoordinateReferenceSystem>(this, "crs"));
crsContainer.add(crsPanel);
add(crsContainer);
}
@Override
protected void onBeforeRender() {
updateFields();
super.onBeforeRender();
}
private void updateFields() {
ReferencedEnvelope e = (ReferencedEnvelope) getModelObject();
if(e != null) {
this.minX = e.getMinX();
this.minY = e.getMinY();
this.maxX = e.getMaxX();
this.maxY = e.getMaxY();
this.crs = e.getCoordinateReferenceSystem();
if( is3D() ){
if( e instanceof ReferencedEnvelope3D ){
this.minZ = ((ReferencedEnvelope3D)e).getMinZ();
this.maxZ = ((ReferencedEnvelope3D)e).getMaxZ();
}
else {
this.minZ = Double.NaN;
this.maxZ = Double.NaN;
}
}
else {
this.minZ = Double.NaN;
this.maxZ = Double.NaN;
}
}
}
public EnvelopePanel setReadOnly( final boolean readOnly ) {
visitChildren(TextField.class, (component, visit) -> {
component.setEnabled(!readOnly);
});
crsPanel.setReadOnly(readOnly);
return this;
}
@SuppressWarnings("unchecked")
@Override
public void convertInput() {
visitChildren(TextField.class, (component, visit) -> {
((TextField<String>) component).processInput();
});
if(isCRSFieldVisible()) {
crsPanel.processInput();
}
// update the envelope model
if(minX != null && maxX != null && minY != null && maxY != null) {
if(crsRequired && crs == null) {
setConvertedInput(null);
} else {
if( is3D() ){
double minZsafe = minZ == null ? Double.NaN : minZ;
double maxZsafe = maxZ == null ? Double.NaN : maxZ;
setConvertedInput(new ReferencedEnvelope3D(minX, maxX, minY, maxY, minZsafe, maxZsafe,crs));
}
else {
setConvertedInput(new ReferencedEnvelope(minX, maxX, minY, maxY, crs));
}
}
} else {
setConvertedInput(null);
}
}
@SuppressWarnings("unchecked")
@Override
protected void onModelChanged() {
// when the client programmatically changed the model, update the fields
// so that the textfields will change too
updateFields();
visitChildren(TextField.class, (component, visit) -> {
((TextField<String>) component).clearInput();
});
}
/**
* Returns the coordinate reference system added by the user in the GUI, if any and valid
*
*/
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
return crs;
}
}