/* (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.gwc.web.gridset;
import java.util.logging.Logger;
import javax.measure.unit.Unit;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.IValidator;
import org.apache.wicket.validation.ValidationError;
import org.apache.wicket.validation.validator.RangeValidator;
import org.geoserver.gwc.GWC;
import org.geoserver.web.GeoServerSecuredPage;
import org.geoserver.web.data.store.panel.TextParamPanel;
import org.geoserver.web.wicket.CRSPanel;
import org.geoserver.web.wicket.EnvelopePanel;
import org.geoserver.web.wicket.GeoServerAjaxFormLink;
import org.geoserver.web.wicket.SRSListPanel;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.logging.Logging;
import org.geowebcache.grid.GridSet;
import org.geowebcache.grid.GridSetBroker;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
abstract class AbstractGridSetPage extends GeoServerSecuredPage {
private static final long serialVersionUID = 2977633539319630433L;
protected static final Logger LOGGER = Logging.getLogger(AbstractGridSetPage.class);
/**
* Name of the page parameter that determines which gridset to edit
*/
public static final String GRIDSET_NAME = "gridSet";
/**
* Name of page parameter that holds the name of an existing gridset to use as template to
* create a new one
*/
public static final String GRIDSET_TEMPLATE_NAME = "template";
protected final Form<GridSetInfo> form;
protected final TextParamPanel name;
protected final Component description;
protected final GridSetCRSPanel crs;
protected final TextParamPanel tileWidth;
protected final TextParamPanel tileHeight;
protected final EnvelopePanel bounds;
protected final Component computeBoundsLink;
protected final Component cancelLink;
protected final Component saveLink;
protected final TileMatrixSetEditor tileMatrixSetEditor;
protected final Component addLevelLink;
protected final FeedbackPanel feedback;
public AbstractGridSetPage(final PageParameters parameters) {
final String gridSetName;
final String templateName;
if (parameters == null) {
gridSetName = null;
templateName = null;
} else {
gridSetName = parameters.get(GRIDSET_NAME).toOptionalString();
templateName = parameters.get(GRIDSET_TEMPLATE_NAME).toOptionalString();
}
GridSetInfo gridsetInfo;
if (templateName != null) {
gridsetInfo = getInfo(templateName);
gridsetInfo.setName("My_" + gridsetInfo.getName());
gridsetInfo.setInternal(false);
} else if (gridSetName != null) {
gridsetInfo = getInfo(gridSetName);
} else {
gridsetInfo = getInfo(null);
}
IModel<GridSetInfo> model = new Model<GridSetInfo>(gridsetInfo);
form = new Form<GridSetInfo>("gridSetForm", model);
feedback = new FeedbackPanel("feedback");
feedback.setOutputMarkupId(true);
form.add(feedback);
form.add(name = name(model));
form.add(description = new TextArea<String>("description", new PropertyModel<String>(model,
"description")));
form.add(crs = crs(model));
form.add(bounds = bounds(model));
form.add(computeBoundsLink = computeBoundsLink(form));
form.add(tileWidth = tileWidth(model));
form.add(tileHeight = tileHeight(model));
form.add(tileMatrixSetEditor = new TileMatrixSetEditor("tileMatrixSetEditor", model));
tileMatrixSetEditor.setOutputMarkupId(true);
cancelLink = new BookmarkablePageLink<>("cancel", GridSetsPage.class);
form.add(cancelLink);
saveLink = saveLink(form);
form.add(saveLink);
add(form);
tileWidth.getFormComponent().add(new AjaxFormComponentUpdatingBehavior("blur") {
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(tileMatrixSetEditor);
}
});
tileHeight.getFormComponent().add(new AjaxFormComponentUpdatingBehavior("blur") {
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(tileMatrixSetEditor);
}
});
addLevelLink = new GeoServerAjaxFormLink("addZoomLevel", form) {
private static final long serialVersionUID = 1202251941625034786L;
@Override
protected void onClick(AjaxRequestTarget target, Form<?> form) {
crs.processInput();
bounds.processInput();
tileWidth.getFormComponent().processInput();
tileHeight.getFormComponent().processInput();
addZoomLevel(target);
target.add(tileMatrixSetEditor);
target.add(feedback);
}
};
form.add(addLevelLink);
}
void addZoomLevel(AjaxRequestTarget target) {
ReferencedEnvelope bbox = (ReferencedEnvelope) bounds.getModelObject();
if (null == bbox) {
String message = new StringResourceModel("AbstractGridSetPage.cantAddZoomLevel").getString();
feedback.error(message);
return;
}
Integer width = (Integer) tileWidth.getFormComponent().getModelObject();
Integer height = (Integer) tileHeight.getFormComponent().getModelObject();
tileMatrixSetEditor.addZoomLevel(bbox, width == null ? 256 : width, height == null ? 256
: height);
}
private Component computeBoundsLink(Form<GridSetInfo> form) {
GeoServerAjaxFormLink link = new GeoServerAjaxFormLink("computeBounds", form) {
private static final long serialVersionUID = 1L;
@Override
protected void onClick(AjaxRequestTarget target, Form<?> form) {
crs.processInput();
computeBounds();
target.add(bounds);
target.add(feedback);
target.add(tileMatrixSetEditor);
}
};
return link;
}
void computeBounds() {
// perform manual processing of the required fields
CoordinateReferenceSystem coordSys;
coordSys = (CoordinateReferenceSystem) crs.getModelObject();
if (coordSys == null) {
bounds.error(new StringResourceModel("AbstractGridsetPage.computeBounds.crsNotSet").getString());
return;
}
GWC mediator = GWC.get();
ReferencedEnvelope aov = mediator.getAreaOfValidity(coordSys);
if (aov == null) {
bounds.error(new StringResourceModel("AbstractGridsetPage.computeBounds.aovNotSet").getString());
} else {
bounds.setModelObject(aov);
}
}
private EnvelopePanel bounds(IModel<GridSetInfo> model) {
class UpdatingEnvelopePanel extends EnvelopePanel {
private static final long serialVersionUID = 1L;
public UpdatingEnvelopePanel(String id, IModel<ReferencedEnvelope> e) {
super(id, e);
class UpdateTableBehavior extends AjaxFormSubmitBehavior {
private static final long serialVersionUID = 1L;
public UpdateTableBehavior() {
super(form, "blur");
}
@Override
protected void onSubmit(AjaxRequestTarget target) {
target.add(AbstractGridSetPage.this.tileMatrixSetEditor);
}
@Override
protected void onError(AjaxRequestTarget target) {
UpdatingEnvelopePanel.this.setModelObject(null);
target.add(AbstractGridSetPage.this.tileMatrixSetEditor);
}
}
minXInput.add(new UpdateTableBehavior());
minYInput.add(new UpdateTableBehavior());
minZInput.add(new UpdateTableBehavior());
maxXInput.add(new UpdateTableBehavior());
maxYInput.add(new UpdateTableBehavior());
maxZInput.add(new UpdateTableBehavior());
}
}
PropertyModel<ReferencedEnvelope> boundsModel;
boundsModel = new PropertyModel<ReferencedEnvelope>(model, "bounds");
EnvelopePanel panel = new UpdatingEnvelopePanel("bounds", boundsModel);
panel.setRequired(true);
panel.setOutputMarkupId(true);
return panel;
}
private TextParamPanel tileHeight(IModel<GridSetInfo> model) {
TextParamPanel panel = new TextParamPanel("tileHeight", new PropertyModel<Integer>(model,
"tileHeight"), new StringResourceModel("AbstractGridSetPage.tileHeight"), true,
new RangeValidator<Integer>(16, 2048));
return panel;
}
private TextParamPanel tileWidth(IModel<GridSetInfo> model) {
TextParamPanel panel = new TextParamPanel("tileWidth", new PropertyModel<Integer>(model,
"tileWidth"), new StringResourceModel("AbstractGridSetPage.tileWidth"), true,
new RangeValidator<Integer>(16, 2048));
return panel;
}
/**
* @param model
*
*/
private GridSetCRSPanel crs(IModel<GridSetInfo> model) {
GridSetCRSPanel crsPanel = new GridSetCRSPanel("crs", model);
return crsPanel;
}
/**
* @author groldan
*
*/
protected static class GridSetCRSPanel extends CRSPanel {
private static final long serialVersionUID = 1L;
private Label units;
private Label metersPerUnit;
private IModel<GridSetInfo> infoModel;
public GridSetCRSPanel(String id, IModel<GridSetInfo> model) {
super(id, new PropertyModel<CoordinateReferenceSystem>(model, "crs"));
this.infoModel = model;
units = new Label("units", new Model<String>());
metersPerUnit = new Label("metersPerUnit", new Model<String>());
units.setOutputMarkupId(true);
metersPerUnit.setOutputMarkupId(true);
updateUnits((CoordinateReferenceSystem) getModelObject());
add(units);
add(metersPerUnit);
}
protected void onSRSUpdated(String srs, AjaxRequestTarget target) {
target.add(units);
target.add(metersPerUnit);
}
@Override
protected SRSListPanel srsListPanel() {
SRSListPanel srsList = new SRSListPanel(popupWindow.getContentId()) {
private static final long serialVersionUID = 2869219395676091081L;
@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);
wktLabel.setDefaultModelObject(crs.getName().toString());
wktLink.setEnabled(true);
target.add(wktLink);
updateUnits(crs);
target.add(units);
target.add(metersPerUnit);
}
};
srsList.setCompactMode(true);
return srsList;
}
@Override
public void convertInput() {
try {
super.convertInput();
} finally {
updateUnits();
}
}
@Override
protected void onBeforeRender() {
// updateUnits();
super.onBeforeRender();
}
private void updateUnits() {
CoordinateReferenceSystem crs = (CoordinateReferenceSystem) getConvertedInput();
updateUnits(crs);
}
private void updateUnits(CoordinateReferenceSystem crs) {
if (crs == null) {
units.setDefaultModelObject("--");
metersPerUnit.setDefaultModelObject("--");
} else {
Double meters = infoModel.getObject().getMetersPerUnit(crs);
metersPerUnit.setDefaultModelObject(String.valueOf(meters));
CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(0);
final Unit<?> unit = axis.getUnit();
final String unitStr = unit.toString();
units.setDefaultModelObject(unitStr);
}
}
}
private TextParamPanel name(IModel<GridSetInfo> model) {
TextParamPanel namePanel = new TextParamPanel("name", new PropertyModel<String>(model,
"name"), new StringResourceModel("AbstractGridSetPage.name"), true,
new UniqueNameValidator(model.getObject().getName()));
return namePanel;
}
private GridSetInfo getInfo(final String gridSetName) {
GridSetInfo gridsetInfo;
if (gridSetName == null) {
gridsetInfo = new GridSetInfo();
} else {
GridSetBroker gridSetBroker = GWC.get().getGridSetBroker();
GridSet gridSet = gridSetBroker.get(gridSetName);
if (gridSet == null) {
throw new IllegalArgumentException("Requested GridSet does not exist: '"
+ gridSetName + "'");
}
final String name = gridSet.getName();
final boolean internal = GWC.get().isInternalGridSet(name);
gridsetInfo = new GridSetInfo(gridSet, internal);
}
return gridsetInfo;
}
private Component saveLink(Form<GridSetInfo> form) {
return new AjaxSubmitLink("save", form) {
private static final long serialVersionUID = 1L;
@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
super.onError(target, form);
target.add(form);
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
onSave(target, form);
}
};
}
protected abstract void onSave(AjaxRequestTarget target, Form<?> form);
private static class UniqueNameValidator implements IValidator<String> {
private static final long serialVersionUID = 1L;
private final String previousName;
/**
* @param previousName
* the initial name of the gridset when the page loaded, may be {@code null} only
* in case we're creating a new gridset
*/
public UniqueNameValidator(final String previousName) {
this.previousName = previousName;
}
@Override
public void validate(IValidatable<String> iv) {
final String name = iv.getValue();
if (name.equals(previousName)) {
return;
}
final GridSetBroker gridSetBroker = GWC.get().getGridSetBroker();
if (previousName != null) {
gridSetBroker.get(previousName);
}
GridSet gridSet = gridSetBroker.get(name);
if (gridSet != null) {
ValidationError error = new ValidationError("gridSetAlreadyExists");
error.setVariable("name", name);
iv.error(error);
}
}
}
}