/*
* Copyright (c) 2015 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.io.wfs.ui.getfeature;
import java.awt.BorderLayout;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.xml.namespace.QName;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import de.fhg.igd.mapviewer.BasicMapKit;
import de.fhg.igd.mapviewer.server.MapServer;
import de.fhg.igd.mapviewer.server.tiles.CustomTileMapServer;
import de.fhg.igd.mapviewer.view.MapToolAction;
import eu.esdihumboldt.hale.io.wfs.capabilities.BBox;
import eu.esdihumboldt.hale.io.wfs.capabilities.FeatureTypeInfo;
import eu.esdihumboldt.hale.io.wfs.capabilities.WFSCapabilities;
import eu.esdihumboldt.hale.io.wfs.ui.capabilities.AbstractWFSCapabilitiesPage;
import eu.esdihumboldt.hale.io.wfs.ui.getfeature.internal.BBoxTool;
import eu.esdihumboldt.hale.io.wfs.ui.getfeature.internal.ClippingMapServer;
import eu.esdihumboldt.hale.ui.util.swing.SwingComposite;
import eu.esdihumboldt.hale.ui.util.wizard.ConfigurationWizard;
import eu.esdihumboldt.hale.ui.util.wizard.ConfigurationWizardPage;
/**
* Page for specifying a bounding box.
*
* @author Simon Templer
*/
public class BBOXPage extends ConfigurationWizardPage<WFSGetFeatureConfig> {
private final AbstractWFSCapabilitiesPage<? extends WFSGetFeatureConfig> capabilitiesPage;
private BasicMapKit mapKit;
private String lastCapUrl;
private BBoxTool bboxTool;
/**
* Create a new wizard page for specifying the request bounding box.
*
* @param wizard the parent wizard
* @param capabilitiesPage the capabilities page
*/
public BBOXPage(ConfigurationWizard<? extends WFSGetFeatureConfig> wizard,
AbstractWFSCapabilitiesPage<? extends WFSGetFeatureConfig> capabilitiesPage) {
super(wizard, "wfsBBOX");
setTitle("Query bounding box");
setDescription("Please set bounding box corners with left click, remove with right click.");
this.capabilitiesPage = capabilitiesPage;
// no BBOX is allowed
setPageComplete(true);
}
@Override
public boolean updateConfiguration(WFSGetFeatureConfig configuration) {
if (bboxTool != null) {
if (bboxTool.getPositions().size() == 0) {
// no bounding box
configuration.setBbox(null);
}
else if (bboxTool.getPositions().size() == 2) {
// derive BB
GeoPosition pos1 = bboxTool.getPositions().get(0);
GeoPosition pos2 = bboxTool.getPositions().get(1);
int epsg = pos1.getEpsgCode();
double x1 = Math.min(pos1.getX(), pos2.getX());
double y1 = Math.min(pos1.getY(), pos2.getY());
double x2 = Math.max(pos1.getX(), pos2.getX());
double y2 = Math.max(pos1.getY(), pos2.getY());
configuration.setBbox(new BBox(x1, y1, x2, y2));
// FIXME what kind of representation is best
// i.e. most likely to work with implementations?
// urn:ogc:def:crs:EPSG:: <- not working with tested GeoServer
configuration.setBboxCrsUri("EPSG:" + epsg);
return true;
}
else {
return false;
}
}
return true;
}
@Override
protected void onShowPage() {
String capUrl = capabilitiesPage.getCapabilitiesURL();
if (!Objects.equals(capUrl, lastCapUrl)) {
// capabilities changed
// update map
WFSCapabilities caps = capabilitiesPage.getCapabilities();
bboxTool.reset();
updateMap(caps);
}
else {
// capabilities stayed the same
}
lastCapUrl = capUrl;
updateState();
}
/**
* Update the map (i.e. set the map server).
*
* @param caps the WFS capabilities
*/
private void updateMap(WFSCapabilities caps) {
CustomTileMapServer tileServer = new CustomTileMapServer();
// use Stamen Terrain as default map here
tileServer.setUrlPattern("http://tile.stamen.com/terrain/{z}/{x}/{y}.jpg");
tileServer.setAttributionText(
"Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA.");
tileServer.setZoomLevel(16);
MapServer server = tileServer;
Set<GeoPosition> positions = null;
if (caps != null) {
/*
* TODO optimal solution would be using the WMS that serves the
* layers corresponding to the feature types
*/
// collect BBs from feature types
Set<QName> types = new HashSet<>(getWizard().getConfiguration().getTypeNames());
if (types.isEmpty()) {
// no features will be selected
}
else {
List<BBox> bbs = new ArrayList<>();
for (QName type : types) {
FeatureTypeInfo info = caps.getFeatureTypes().get(type);
if (info != null && info.getWgs84BBox() != null) {
bbs.add(info.getWgs84BBox());
}
}
if (!bbs.isEmpty()) {
double minX, maxX, minY, maxY;
Iterator<BBox> it = bbs.iterator();
BBox bb = it.next();
minX = bb.getX1();
minY = bb.getY1();
maxX = bb.getX2();
maxY = bb.getY2();
while (it.hasNext()) {
bb = it.next();
minX = Math.min(minX, bb.getX1());
minY = Math.min(minY, bb.getY1());
maxX = Math.max(maxX, bb.getX2());
maxY = Math.max(maxY, bb.getY2());
}
GeoPosition topLeft = new GeoPosition(minX, maxY, 4326);
GeoPosition bottomRight = new GeoPosition(maxX, minY, 4326);
Color back = mapKit.getBackground();
server = new ClippingMapServer(server, topLeft, bottomRight,
new Color(back.getRed(), back.getGreen(), back.getBlue(), 170));
positions = new HashSet<>();
positions.add(topLeft);
positions.add(bottomRight);
}
else {
// ignore BBs, provide full map
}
}
}
mapKit.setServer(server, true);
if (positions != null) {
try {
mapKit.zoomToPositions(positions);
} catch (Exception e) {
// ignore error
}
}
mapKit.refresh();
}
@Override
protected void createContent(Composite parent) {
Composite page = new Composite(parent, SWT.NONE);
GridLayoutFactory.swtDefaults().numColumns(1).applyTo(page);
SwingComposite wrapper = new SwingComposite(page);
GridDataFactory.fillDefaults().grab(true, true).hint(600, 400).applyTo(wrapper);
wrapper.getContentPane().setLayout(new BorderLayout());
// create map kit
mapKit = new BasicMapKit();
// configure map
updateMap(null);
// mapKit.addCustomPainter(mypainter);
// add map kit
wrapper.getContentPane().add(mapKit, BorderLayout.CENTER);
// create tool
final Display display = Display.getCurrent();
final Runnable updateRunner = new Runnable() {
@Override
public void run() {
updateState();
}
};
bboxTool = new BBoxTool() {
@Override
protected void addPosition(GeoPosition pos) {
super.addPosition(pos);
display.asyncExec(updateRunner);
}
@Override
public void reset() {
super.reset();
display.asyncExec(updateRunner);
}
};
// activate tool
new MapToolAction(bboxTool, mapKit, true);
setControl(page);
}
/**
* Update the page state.
*/
protected void updateState() {
// either an empty or complete bb is allowed
boolean valid = bboxTool.getPositions().size() == 0 || bboxTool.getPositions().size() == 2;
setPageComplete(valid);
}
}