/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.cismap.commons.gui.piccolo.eventlistener; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.PrecisionModel; import edu.umd.cs.piccolo.event.PInputEvent; import edu.umd.cs.piccolox.event.PNotificationCenter; import org.deegree.model.feature.FeatureCollection; import org.deegree.model.feature.FeatureProperty; import org.deegree.model.feature.GMLFeatureCollectionDocument; import org.deegree.model.spatialschema.JTSAdapter; import org.openide.util.Lookup; import org.openide.util.NbBundle; import java.awt.Color; import java.awt.geom.Point2D; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.net.URL; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import javax.swing.ImageIcon; import de.cismet.cismap.commons.CrsTransformer; import de.cismet.cismap.commons.WorldToScreenTransform; import de.cismet.cismap.commons.XBoundingBox; import de.cismet.cismap.commons.features.AbstractNewFeature; import de.cismet.cismap.commons.features.CommonFeatureAction; import de.cismet.cismap.commons.features.DefaultStyledFeature; import de.cismet.cismap.commons.features.Feature; import de.cismet.cismap.commons.features.FeatureServiceFeature; import de.cismet.cismap.commons.features.PureNewFeature; import de.cismet.cismap.commons.features.WMSFeature; import de.cismet.cismap.commons.featureservice.AbstractFeatureService; import de.cismet.cismap.commons.featureservice.DefaultLayerProperties; import de.cismet.cismap.commons.featureservice.GeometryHeuristics; import de.cismet.cismap.commons.featureservice.LayerProperties; import de.cismet.cismap.commons.gui.MappingComponent; import de.cismet.cismap.commons.gui.featureinfopanel.WMSGetFeatureInfoDescription; import de.cismet.cismap.commons.gui.layerwidget.ActiveLayerModel; import de.cismet.cismap.commons.gui.piccolo.FeatureAnnotationSymbol; import de.cismet.cismap.commons.gui.piccolo.PFeature; import de.cismet.cismap.commons.interaction.CismapBroker; import de.cismet.cismap.commons.interaction.GetFeatureInfoListener; import de.cismet.cismap.commons.interaction.events.GetFeatureInfoEvent; import de.cismet.cismap.commons.raster.wms.WMSLayer; import de.cismet.cismap.commons.raster.wms.WMSServiceLayer; import de.cismet.cismap.commons.rasterservice.MapService; import de.cismet.cismap.commons.tools.PFeatureTools; import de.cismet.cismap.commons.wms.capabilities.Parameter; import de.cismet.commons.concurrency.CismetExecutors; import de.cismet.security.WebAccessManager; import de.cismet.tools.gui.StaticSwingTools; import de.cismet.tools.gui.WaitingDialogThread; /** * DOCUMENT ME! * * @author therter * @version $Revision$, $Date$ */ public class GetFeatureInfoMultiGeomListener extends CreateGeometryListener { //~ Static fields/initializers --------------------------------------------- public static final String GET_FEATURE_INFO_MULTI_GEOM_NOTIFICATION = "GET_FEATURE_INFO_MULTI_GEOM_NOTIFICATION"; // NOI18N private static final String WMS_GML_FORMAT = "application/vnd.ogc.gml"; private static final String ENCODING_ATTR = "encoding=\""; //~ Instance fields -------------------------------------------------------- Vector<PFeature> pfVector = new Vector<PFeature>(); ArrayList<? extends CommonFeatureAction> commonFeatureActions = null; private final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(this.getClass()); private int clickCount = 0; private boolean selectionInProgress = false; private List<GetFeatureInfoListener> listener = new ArrayList<GetFeatureInfoListener>(); private ImageIcon pointIcon = new javax.swing.ImageIcon(getClass().getResource( "/de/cismet/cismap/commons/gui/res/linRefPoint.png")); //~ Constructors ----------------------------------------------------------- /** * Creates a new SelectionListener object. */ public GetFeatureInfoMultiGeomListener() { final Lookup.Result<CommonFeatureAction> result = Lookup.getDefault().lookupResult(CommonFeatureAction.class); commonFeatureActions = new ArrayList<CommonFeatureAction>(result.allInstances()); Collections.sort(commonFeatureActions, new Comparator<CommonFeatureAction>() { @Override public int compare(final CommonFeatureAction o1, final CommonFeatureAction o2) { return Integer.valueOf(o1.getSorter()).compareTo(Integer.valueOf(o2.getSorter())); } }); setGeometryFeatureClass(PureNewFeature.class); setMode(RECTANGLE); } //~ Methods ---------------------------------------------------------------- @Override public void mouseMoved(final PInputEvent pInputEvent) { setMappingComponent(pInputEvent); super.mouseMoved(pInputEvent); } @Override public void mousePressed(final PInputEvent pInputEvent) { setMappingComponent(pInputEvent); super.mousePressed(pInputEvent); } /** * Selektiere einen PNode. * * @param pInputEvent DOCUMENT ME! */ @Override public void mouseClicked(final edu.umd.cs.piccolo.event.PInputEvent pInputEvent) { setMappingComponent(pInputEvent); super.mouseClicked(pInputEvent); if (mode.equals(RECTANGLE) || mode.equals(ELLIPSE)) { selectionInProgress = true; try { if (log.isDebugEnabled()) { log.debug("mouseClicked():" + pInputEvent.getPickedNode()); // NOI18N } clickCount = pInputEvent.getClickCount(); if (pInputEvent.getComponent() instanceof MappingComponent) { mappingComponent = (MappingComponent)pInputEvent.getComponent(); } final int currentSrid = CrsTransformer.extractSridFromCrs(CismapBroker.getInstance().getSrs() .getCode()); final PureNewFeature feature = new PureNewFeature(createPointFromInput(pInputEvent)); feature.setGeometryType(AbstractNewFeature.geomTypes.POINT); feature.getGeometry().setSRID(currentSrid); // show the point on the map final DefaultStyledFeature styledFeature = new DefaultStyledFeature(); styledFeature.setGeometry(createPointFromInput(pInputEvent)); styledFeature.getGeometry().setSRID(currentSrid); final FeatureAnnotationSymbol fas = new FeatureAnnotationSymbol(pointIcon.getImage()); fas.setSweetSpotX(0.5); fas.setSweetSpotY(0.5); styledFeature.setPointAnnotationSymbol(fas); mappingComponent.highlightFeature(styledFeature, 1500); finishingEvent = pInputEvent; finishGeometry(feature); } finally { selectionInProgress = false; } } } /** * DOCUMENT ME! * * @param l featuresFromServicesSelectable DOCUMENT ME! */ public void addGetFeatureInfoListener(final GetFeatureInfoListener l) { listener.add(l); } /** * DOCUMENT ME! * * @param l featuresFromServicesSelectable DOCUMENT ME! */ public void removeGetFeatureInfoListener(final GetFeatureInfoListener l) { listener.remove(l); } /** * DOCUMENT ME! * * @param event DOCUMENT ME! * * @return DOCUMENT ME! */ private Point createPointFromInput(final PInputEvent event) { final Point2D pos = event.getPosition(); final WorldToScreenTransform wtst = getMappingComponent().getWtst(); final Coordinate coord = new Coordinate(wtst.getSourceX(pos.getX()), wtst.getSourceY(pos.getY())); final GeometryFactory gf = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), CrsTransformer.getCurrentSrid()); return gf.createPoint(coord); } @Override public void mouseDragged(final PInputEvent e) { setMappingComponent(e); super.mouseDragged(e); // To change body of generated methods, choose Tools | Templates. clickCount = e.getClickCount(); } /** * Wird gefeuert, wenn die Maustaste nach dem Ziehen des Markiervierecks losgelassen wird. * * @param event das Mouseevent (als PInputEvent) */ @Override public void mouseReleased(final PInputEvent event) { setMappingComponent(event); super.mouseReleased(event); } @Override protected Color getFillingColor() { return new Color(20, 20, 20, 20); } @Override protected void finishGeometry(final AbstractNewFeature feature) { super.finishGeometry(feature); selectionInProgress = true; mappingComponent.getHandleLayer().removeAllChildren(); final Geometry geom; if (feature.getGeometryType().equals(AbstractNewFeature.geomTypes.POINT)) { // 0.000001 this is even in geographic crs less than 1m geom = feature.getGeometry().buffer(0.000001); } else { geom = feature.getGeometry(); } final WaitingDialogThread<List<Feature>> t = new WaitingDialogThread<List<Feature>>( StaticSwingTools.getParentFrame( mappingComponent), true, NbBundle.getMessage( GetFeatureInfoMultiGeomListener.class, "GetFeatureInfoMultiGeomListener.finishGeometry.WaitingDialogThread"), null, 200, true) { @Override protected List<Feature> doInBackground() throws Exception { final List<Feature> toBeSelected = Collections.synchronizedList(new ArrayList<Feature>()); if ((geom != null)) { if (log.isDebugEnabled()) { // Hole alle PFeatures die das Markierviereck schneiden // und Hinzuf\u00FCgen dieser PFeatures zur Selektion log.debug("Markiergeometrie = " + geom.toText()); // NOI18N } final TreeMap<Integer, MapService> serviceTree = ((ActiveLayerModel)mappingComponent.getMappingModel()).getMapServices(); final ExecutorService executor = CismetExecutors.newFixedThreadPool(8); Map<MapService, List<Feature>> featureMap = new HashMap<MapService, List<Feature>>( serviceTree.size()); featureMap = Collections.synchronizedMap(featureMap); for (final Integer key : serviceTree.keySet()) { final MapService service = serviceTree.get(key); final FeatureRetriever fr = new FeatureRetriever(featureMap, service, geom, feature); executor.submit(fr); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.HOURS); for (final MapService service : featureMap.keySet()) { toBeSelected.addAll(featureMap.get(service)); } } return toBeSelected; } @Override protected void done() { try { final List<Feature> toBeSelected = get(); final GetFeatureInfoEvent evt = new GetFeatureInfoEvent(mappingComponent, geom); evt.setFeatures(toBeSelected); fireGetFeatureInfoEvent(evt); postSelectionChanged(); } catch (Exception e) { log.error("Error while trying to receiving features.", e); } finally { selectionInProgress = false; mappingComponent.getFeatureCollection().removeFeature(feature); } } }; t.start(); } /** * DOCUMENT ME! * * @param url DOCUMENT ME! * @param wmsServiceLayer DOCUMENT ME! * @param clickPoint DOCUMENT ME! * * @return DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ private List<Feature> getWMSFeatures(final String url, final WMSServiceLayer wmsServiceLayer, final Geometry clickPoint) throws Exception { final InputStream respIs = WebAccessManager.getInstance().doRequest(new URL(url)); final GMLFeatureCollectionDocument featureCollectionDocument = new GMLFeatureCollectionDocument(); final FeatureCollection featureCollection; String res = readInputStream(respIs, null); String encodingString = null; if (res.contains(ENCODING_ATTR)) { encodingString = res.substring(res.indexOf(ENCODING_ATTR) + ENCODING_ATTR.length()); encodingString = encodingString.substring(0, encodingString.indexOf("\"")); } if (encodingString != null) { res = readInputStream(new ByteArrayInputStream(res.getBytes()), encodingString); } final StringReader re = new StringReader(res); featureCollectionDocument.load(re, "http://dummyID"); if (featureCollectionDocument.getFeatureCount() == 0) { return null; } featureCollection = featureCollectionDocument.parse(); if ((featureCollection.size() == 1) && (featureCollection.getFeature(0).getName() != null) && featureCollection.getFeature(0).getName().getLocalName().equals("ExceptionText")) { try { final String errorMessage = featureCollectionDocument.getRootElement() .getFirstChild() .getFirstChild() .getTextContent(); throw new Exception(errorMessage); } catch (NullPointerException e) { throw new Exception("The wfs replies with an Exception, but the error text cannot be extracted."); } } if (featureCollection.size() > 0) { return processFeatureCollection( featureCollection.toArray(), true, wmsServiceLayer, clickPoint); } else { return new ArrayList<Feature>(); } } /** * Reads the given input stream. * * @param respIs the inputstream to read * @param charset the charset to be use. The default charset will be used, if it is null * * @return The inputstream as string * * @throws IOException DOCUMENT ME! */ private String readInputStream(final InputStream respIs, final String charset) throws IOException { final InputStreamReader reader; if (charset != null) { reader = new InputStreamReader(new BufferedInputStream(respIs), charset); } else { reader = new InputStreamReader(new BufferedInputStream(respIs)); } final StringBuilder res = new StringBuilder(); String tmp; final BufferedReader br = new BufferedReader(reader); while ((tmp = br.readLine()) != null) { res.append(tmp); } return res.toString(); } /** * DOCUMENT ME! * * @param featureCollection DOCUMENT ME! * @param evaluateExpressions DOCUMENT ME! * @param layer DOCUMENT ME! * @param clickPoint DOCUMENT ME! * * @return DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ protected List<Feature> processFeatureCollection(final org.deegree.model.feature.Feature[] featureCollection, final boolean evaluateExpressions, final WMSServiceLayer layer, final Geometry clickPoint) throws Exception { int i = 0; final int geometryIndex = GeometryHeuristics.findBestGeometryIndex(featureCollection[0]); final Vector<Feature> featureVector = new Vector(featureCollection.length); for (final org.deegree.model.feature.Feature degreeFeature : featureCollection) { final WMSFeature featureServiceFeature = new WMSFeature(layer); final int srid = CrsTransformer.getCurrentSrid(); this.initialiseFeature( featureServiceFeature, degreeFeature, evaluateExpressions, i, geometryIndex, srid, clickPoint); featureVector.add(featureServiceFeature); i++; } return featureVector; } /** * DOCUMENT ME! * * @param featureServiceFeature DOCUMENT ME! * @param degreeFeature DOCUMENT ME! * @param evaluateExpressions DOCUMENT ME! * @param index DOCUMENT ME! * @param geometryIndex DOCUMENT ME! * @param featureSrid DOCUMENT ME! * @param clickPoint DOCUMENT ME! * * @throws Exception DOCUMENT ME! */ protected void initialiseFeature(final WMSFeature featureServiceFeature, final org.deegree.model.feature.Feature degreeFeature, final boolean evaluateExpressions, final int index, final int geometryIndex, final int featureSrid, final Geometry clickPoint) throws Exception { // perform standard initilaisation featureServiceFeature.setLayerProperties(new DefaultLayerProperties()); // creating geometry if (featureServiceFeature.getGeometry() == null) { try { featureServiceFeature.setGeometry(JTSAdapter.export( degreeFeature.getGeometryPropertyValues()[geometryIndex])); } catch (Exception e) { featureServiceFeature.setGeometry(clickPoint); } } if ((featureServiceFeature.getGeometry() != null)) { featureServiceFeature.getGeometry().setSRID(featureSrid); } // adding properties if ((featureServiceFeature.getProperties() == null) || featureServiceFeature.getProperties().isEmpty()) { // set the properties final FeatureProperty[] featureProperties = degreeFeature.getProperties(); for (final FeatureProperty fp : featureProperties) { featureServiceFeature.addProperty(fp.getName().getAsString(), fp.getValue()); } } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void fireGetFeatureInfoEvent(final GetFeatureInfoEvent evt) { for (final GetFeatureInfoListener l : listener) { l.getFeatureInfoRequest(evt); } } /** * DOCUMENT ME! * * @param pInputEvent DOCUMENT ME! */ private void setMappingComponent(final edu.umd.cs.piccolo.event.PInputEvent pInputEvent) { if (getMappingComponent() == null) { super.setMappingComponent((MappingComponent)pInputEvent.getComponent()); } } /** * DOCUMENT ME! */ private void postSelectionChanged() { if (log.isDebugEnabled()) { log.debug("postSelectionChanged"); // NOI18N } final PNotificationCenter pn = PNotificationCenter.defaultCenter(); pn.postNotification(GetFeatureInfoMultiGeomListener.GET_FEATURE_INFO_MULTI_GEOM_NOTIFICATION, this); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public int getClickCount() { return clickCount; } /** * DOCUMENT ME! * * @return the selectionInProgress */ public boolean isSelectionInProgress() { return selectionInProgress; } /** * DOCUMENT ME! * * @param selectionInProgress the selectionInProgress to set */ public void setSelectionInProgress(final boolean selectionInProgress) { this.selectionInProgress = selectionInProgress; } //~ Inner Classes ---------------------------------------------------------- /** * DOCUMENT ME! * * @version $Revision$, $Date$ */ private class FeatureRetriever implements Runnable { //~ Instance fields ---------------------------------------------------- private final Map<MapService, List<Feature>> featureMap; private final MapService service; private final Geometry geom; private final AbstractNewFeature feature; //~ Constructors ------------------------------------------------------- /** * Creates a new FeatureRetriever object. * * @param featureMap DOCUMENT ME! * @param service DOCUMENT ME! * @param geom DOCUMENT ME! * @param feature DOCUMENT ME! */ public FeatureRetriever(final Map<MapService, List<Feature>> featureMap, final MapService service, final Geometry geom, final AbstractNewFeature feature) { this.featureMap = featureMap; this.service = service; this.geom = geom; this.feature = feature; } //~ Methods ------------------------------------------------------------ @Override public void run() { List<Feature> featuresFromService = new ArrayList<Feature>(); if (service instanceof AbstractFeatureService) { try { final AbstractFeatureService featureService = (AbstractFeatureService)service; if (!featureService.isInitialized()) { featureService.initAndWait(); } featuresFromService = featureService.getFeatureFactory() .createFeatures(featureService.getQuery(), new XBoundingBox(geom), null, 0, Integer.MAX_VALUE, null); } catch (Exception e) { log.error("Error while receiving features", e); } } else if (feature.getGeometryType().equals(AbstractNewFeature.geomTypes.POINT) && (service instanceof WMSServiceLayer)) { final WMSServiceLayer wmsService = (WMSServiceLayer)service; if (wmsService.isQueryable()) { final Parameter p = wmsService.getWmsCapabilities() .getRequest() .getFeatureInfoOperation() .getParameter("Format"); final boolean gmlResponsePossible = (p != null) && (p.getAllowedValues() != null) && p.getAllowedValues().contains(WMS_GML_FORMAT); for (final WMSLayer layer : (List<WMSLayer>)wmsService.getWMSLayers()) { if (gmlResponsePossible) { try { featuresFromService.addAll(getWMSFeatures( wmsService.getGetFeatureInfoUrl( (int)finishingEvent.getCanvasPosition().getX(), (int)finishingEvent.getCanvasPosition().getY(), layer, WMS_GML_FORMAT), wmsService, feature.getGeometry())); } catch (Exception e) { log.error("Error while retrieving features from wms", e); } } else { featuresFromService.add(new WMSGetFeatureInfoDescription( feature.getGeometry(), finishingEvent, layer, wmsService)); } } } } featureMap.put(service, featuresFromService); } } }