/**
* Copyright (c) 2011 Alexander De Leon Battista
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package es.upm.fi.dia.oeg.map4rdf.client.view.v2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.gwtopenmaps.openlayers.client.Map;
import org.gwtopenmaps.openlayers.client.Projection;
import org.gwtopenmaps.openlayers.client.Size;
import org.gwtopenmaps.openlayers.client.Style;
import org.gwtopenmaps.openlayers.client.control.MousePosition;
import org.gwtopenmaps.openlayers.client.control.MousePositionOptions;
import org.gwtopenmaps.openlayers.client.control.MousePositionOutput;
import org.gwtopenmaps.openlayers.client.control.SelectFeature;
import org.gwtopenmaps.openlayers.client.event.MapMoveListener;
import org.gwtopenmaps.openlayers.client.event.MapZoomListener;
import org.gwtopenmaps.openlayers.client.event.VectorFeatureSelectedListener;
import org.gwtopenmaps.openlayers.client.event.VectorFeatureUnselectedListener;
import org.gwtopenmaps.openlayers.client.feature.VectorFeature;
import org.gwtopenmaps.openlayers.client.geometry.Geometry;
import org.gwtopenmaps.openlayers.client.geometry.LineString;
import org.gwtopenmaps.openlayers.client.geometry.LinearRing;
import org.gwtopenmaps.openlayers.client.LonLat;
import org.gwtopenmaps.openlayers.client.layer.Vector;
import org.gwtopenmaps.openlayers.client.layer.VectorOptions;
import org.gwtopenmaps.openlayers.client.popup.Popup;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import es.upm.fi.dia.oeg.map4rdf.client.resource.BrowserResources;
import es.upm.fi.dia.oeg.map4rdf.client.style.StyleMapShape;
import es.upm.fi.dia.oeg.map4rdf.client.util.DrawPointStyle;
import es.upm.fi.dia.oeg.map4rdf.client.util.FeatureClickEvent;
import es.upm.fi.dia.oeg.map4rdf.share.Circle;
import es.upm.fi.dia.oeg.map4rdf.share.OpenLayersAdapter;
import es.upm.fi.dia.oeg.map4rdf.share.Point;
import es.upm.fi.dia.oeg.map4rdf.share.PointBean;
import es.upm.fi.dia.oeg.map4rdf.share.PolyLine;
import es.upm.fi.dia.oeg.map4rdf.share.Polygon;
import es.upm.fi.dia.oeg.map4rdf.share.WKTGeometry;
/**
* @author Alexander De Leon
*/
public class OpenLayersMapLayer implements MapLayer,
VectorFeatureSelectedListener, VectorFeatureUnselectedListener,
MapMoveListener {
private static final int CIRCLE_NUMBER_OF_POINTS = 20;
private final Vector vectorLayer;
private FlowPanel popupPanel;
private final Set<VectorFeature> features = new HashSet<VectorFeature>();
private final OpenLayersMapView owner;
private final Map map;
private final java.util.Map<String, List<ClickHandler>> handlers = new HashMap<String, List<ClickHandler>>();
private final BrowserResources browserResources;
private List<VectorFeature> polylines;
private java.util.Map<DrawPointStyle.Style, List<VectorFeature>> points;
private LonLat lastLonLatClicked;
private SelectFeature selectFeatureControl;
public OpenLayersMapLayer(OpenLayersMapView owner, Map map, String name,
BrowserResources browserResources) {
this.owner = owner;
this.map = map;
this.browserResources = browserResources;
polylines = new ArrayList<VectorFeature>();
VectorOptions vectorOptions = new VectorOptions();
vectorLayer = new Vector(name + "_vectors", vectorOptions);
vectorLayer.setDisplayInLayerSwitcher(false);
map.addLayer(vectorLayer);
map.addMapMoveListener(this);
points = new HashMap<DrawPointStyle.Style, List<VectorFeature>>();
for (DrawPointStyle.Style i : DrawPointStyle.Style.values()) {
points.put(i, new ArrayList<VectorFeature>());
}
//Adds the custom mouse position to the map
MousePositionOutput mpOut = new MousePositionOutput() {
@Override
public String format(LonLat lonLat, Map map) {
lastLonLatClicked = lonLat;
return "";
}
};
MousePositionOptions mpOptions = new MousePositionOptions();
mpOptions.setFormatOutput(mpOut); // rename to setFormatOutput
map.addControl(new MousePosition(mpOptions));
}
@Override
public HasClickHandlers draw(Point point, DrawPointStyle pointStyle) {
LonLat ll = new LonLat(point.getX(), point.getY());
ll.transform(point.getProjection(), map.getProjection());
org.gwtopenmaps.openlayers.client.geometry.Point olPoint = new org.gwtopenmaps.openlayers.client.geometry.Point(
ll.lon(), ll.lat());
return addFeature(olPoint, getStyle(olPoint, pointStyle), pointStyle);
}
@Override
public HasClickHandlers drawPolygon(StyleMapShape<Polygon> polygon, DrawPointStyle pointStyle) {
List<Point> points = polygon.getMapShape().getPoints();
List<Point> tranformedPoints = new ArrayList<Point>();
for (Point p : points) {
LonLat ll = new LonLat(p.getX(), p.getY());
ll.transform(p.getProjection(), map.getProjection());
tranformedPoints.add(new PointBean(p.getUri(), ll.lon(), ll.lat(),map.getProjection()));
}
LinearRing ring = new LinearRing(getPoints(tranformedPoints));
return addFeature(
new org.gwtopenmaps.openlayers.client.geometry.Polygon(
new LinearRing[] { ring }), getStyle(polygon), pointStyle);
}
@Override
public HasClickHandlers drawPolyline(StyleMapShape<PolyLine> polyline, DrawPointStyle pointStyle) {
List<Point> points = polyline.getMapShape().getPoints();
List<Point> tranformedPoints = new ArrayList<Point>();
for (Point p : points) {
LonLat ll = new LonLat(p.getX(), p.getY());
ll.transform(p.getProjection(), map.getProjection());
tranformedPoints.add(new PointBean(p.getUri(), ll.lon(), ll.lat(),map.getProjection()));
}
LineString lineString = new LineString(getPoints(tranformedPoints));
return addFeature(lineString, getStyle(polyline), pointStyle);
}
@Override
public HasClickHandlers drawCircle(StyleMapShape<Circle> circle) {
org.gwtopenmaps.openlayers.client.geometry.Point[] circlePoints = new org.gwtopenmaps.openlayers.client.geometry.Point[CIRCLE_NUMBER_OF_POINTS];
double EARTH_RADIUS = 6371000;
double d = circle.getMapShape().getRadius() / EARTH_RADIUS;
double lat1 = Math.toRadians(circle.getMapShape().getCenter().getY());
double lng1 = Math.toRadians(circle.getMapShape().getCenter().getX());
double a = 0;
double step = 360.0 / CIRCLE_NUMBER_OF_POINTS;
for (int i = 0; i < CIRCLE_NUMBER_OF_POINTS; i++) {
double tc = Math.toRadians(a);
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(d)
+ Math.cos(lat1) * Math.sin(d) * Math.cos(tc));
double lng2 = lng1
+ Math.atan2(Math.sin(tc) * Math.sin(d) * Math.cos(lat1),
Math.cos(d) - Math.sin(lat1) * Math.sin(lat2));
circlePoints[i] = new org.gwtopenmaps.openlayers.client.geometry.Point(
Math.toDegrees(lng2), Math.toDegrees(lat2));
LonLat ll = new LonLat(circlePoints[i].getX(),
circlePoints[i].getY());
ll.transform(circle.getMapShape().getCenter().getProjection(), map.getProjection());
circlePoints[i] = new org.gwtopenmaps.openlayers.client.geometry.Point(
ll.lon(), ll.lat());
a += step;
}
LinearRing ring = new LinearRing(circlePoints);
return addFeature(
(new org.gwtopenmaps.openlayers.client.geometry.Polygon(
new LinearRing[] { ring })), getStyle(circle), null);
}
@Override
public HasClickHandlers drawCircle(StyleMapShape<Circle> circle, String text) {
org.gwtopenmaps.openlayers.client.geometry.Point[] circlePoints = new org.gwtopenmaps.openlayers.client.geometry.Point[CIRCLE_NUMBER_OF_POINTS];
double EARTH_RADIUS = 6371000;
double d = circle.getMapShape().getRadius() / EARTH_RADIUS;
double lat1 = Math.toRadians(circle.getMapShape().getCenter().getY());
double lng1 = Math.toRadians(circle.getMapShape().getCenter().getX());
double a = 0;
double step = 360.0 / CIRCLE_NUMBER_OF_POINTS;
for (int i = 0; i < CIRCLE_NUMBER_OF_POINTS; i++) {
double tc = Math.toRadians(a);
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(d)
+ Math.cos(lat1) * Math.sin(d) * Math.cos(tc));
double lng2 = lng1
+ Math.atan2(Math.sin(tc) * Math.sin(d) * Math.cos(lat1),
Math.cos(d) - Math.sin(lat1) * Math.sin(lat2));
circlePoints[i] = new org.gwtopenmaps.openlayers.client.geometry.Point(
Math.toDegrees(lng2), Math.toDegrees(lat2));
LonLat ll = new LonLat(circlePoints[i].getX(),
circlePoints[i].getY());
ll.transform(circle.getMapShape().getCenter().getProjection(), map.getProjection());
circlePoints[i] = new org.gwtopenmaps.openlayers.client.geometry.Point(
ll.lon(), ll.lat());
a += step;
}
LinearRing ring = new LinearRing(circlePoints);
Style style = getStyle(circle);
style.setLabel(text);
return addFeature(
(new org.gwtopenmaps.openlayers.client.geometry.Polygon(
new LinearRing[] { ring })), style, null);
}
@Override
public HasClickHandlers drawWKTGeometry(
StyleMapShape<WKTGeometry> wktGeometryStyle, DrawPointStyle drawStyle) {
Geometry geometry = org.gwtopenmaps.openlayers.client.geometry.Geometry.fromWKT(wktGeometryStyle.getMapShape().getWKT());
geometry.transform(new Projection(wktGeometryStyle.getMapShape().getProjection()), new Projection(map.getProjection()));
return addFeature(geometry,getStyle(wktGeometryStyle),drawStyle);
}
@Override
public PopupWindow createPopupWindow() {
return new PopupWindow() {
private final FlowPanel panel = new FlowPanel();
private Popup popup;
private int width=106;
private int height=106;
MapZoomListener zoomListener;
@Override
public boolean remove(Widget w) {
return panel.remove(w);
}
@Override
public Iterator<Widget> iterator() {
return panel.iterator();
}
@Override
public void clear() {
panel.clear();
}
@Override
public void add(Widget w) {
panel.add(w);
}
@Override
public void open(Point location) {
MapZoomListener zoomListener = new MapZoomListener() {
@Override
public void onMapZoom(MapZoomEvent eventObject) {
if(getPopupLeft()!=null && getPopupLeft()!="" && !getPopupLeft().isEmpty() && getPopupTop()!=null && getPopupTop()!="" && !getPopupTop().isEmpty()){
int popupLeft=Integer.parseInt(getPopupLeft().replace("px", ""));
int popupTop=Integer.parseInt(getPopupTop().replace("px", ""));
popupPanel.getElement().getStyle().setLeft(popupLeft-(width/2), Unit.PX);
popupPanel.getElement().getStyle().setTop(popupTop-(height/2), Unit.PX);
}
}
};
map.addMapZoomListener(zoomListener);
LonLat popupPosition = OpenLayersAdapter.getLatLng(location);
popupPosition.transform(location.getProjection(), map.getProjection());
popup = new Popup("exclusive-mapresources-popup",
popupPosition, new Size(width, height),panel.getElement().getInnerHTML()
, false);
map.addPopupExclusive(popup);
popupPanel = new FlowPanel();
popupPanel.setSize(width+"px", height+"px");
popupPanel.add(panel);
owner.getContainer().add(popupPanel);
if(getPopupLeft()!=null && getPopupLeft()!="" && getPopupTop()!=null && getPopupTop()!=""){
int popupLeft=Integer.parseInt(getPopupLeft().replace("px", ""));
int popupTop=Integer.parseInt(getPopupTop().replace("px", ""));
popupPanel.getElement().getStyle().setPosition(Position.ABSOLUTE);
popupPanel.getElement().getStyle().setLeft(popupLeft-(width/2), Unit.PX);
popupPanel.getElement().getStyle().setTop(popupTop-(height/2), Unit.PX);
}
popupPanel.getElement().getStyle().setZIndex(2024);
popupPanel.getElement().setAttribute("id", "map4rdf-popup-new");
replace();
}
@Override
public void close() {
if (popup != null) {
map.removePopup(popup);
owner.getContainer().remove(popupPanel);
map.removeListener(zoomListener);
}
}
};
}
// set
native String getPopupLeft() /*-{
if($wnd.document.getElementById("exclusive-mapresources-popup")!=null){
var e = $wnd.document.getElementById("exclusive-mapresources-popup").style.left;
return e;
} else {
return "";
}
}-*/;
native String getPopupTop() /*-{
if($wnd.document.getElementById("exclusive-mapresources-popup")!=null){
var e = $wnd.document.getElementById("exclusive-mapresources-popup").style.top;
return e;
} else {
return "";
}
}-*/;
native void replace() /*-{
if($wnd.document.getElementById("exclusive-mapresources-popup")!=null && $wnd.document.getElementById("map4rdf-popup-new")!=null){
var element = $wnd.document
.getElementById("exclusive-mapresources-popup");
var parent = element.parentNode;
var newElement = $wnd.document.getElementById("map4rdf-popup-new");
parent.appendChild(newElement);
element.style.height = "0px";
element.style.width = "0px";
}
return;
}-*/;
private native void consoleLog(Object toLog)/*-{
$wnd.console.log(toLog);
console.log(toLog);
}-*/;
@Override
public void clear() {
for (VectorFeature feature : features) {
vectorLayer.removeFeature(feature);
}
features.clear();
removePolylines();
for (List<VectorFeature> i : points.values()) {
i.clear();
}
}
@Override
public MapView getMapView() {
return owner;
}
public class FeatureHasClickHandlerWrapper implements HasClickHandlers {
private final String featureId;
private FeatureHasClickHandlerWrapper(String featureId) {
this.featureId = featureId;
}
@Override
public void fireEvent(GwtEvent<?> event) {
// ignore
}
@Override
public HandlerRegistration addClickHandler(final ClickHandler handler) {
List<ClickHandler> clickHandlers = handlers.get(featureId);
if (clickHandlers == null) {
clickHandlers = new ArrayList<ClickHandler>();
handlers.put(featureId, clickHandlers);
}
final List<ClickHandler> fClickHandlers = clickHandlers;
clickHandlers.add(handler);
return new HandlerRegistration() {
@Override
public void removeHandler() {
fClickHandlers.remove(handler);
}
};
}
}
void bind() {
vectorLayer.addVectorFeatureSelectedListener(this);
vectorLayer.addVectorFeatureUnselectedListener(this);
SelectFeature selectFeature = new SelectFeature(vectorLayer);
selectFeatureControl = selectFeature;
selectFeature.setClickOut(false);
selectFeature.setToggle(true);
selectFeature.setMultiple(false);
map.addControl(selectFeature);
selectFeature.activate();
}
/* ------------------------- helper methods -- */
private org.gwtopenmaps.openlayers.client.geometry.Point[] getPoints(
List<Point> points) {
org.gwtopenmaps.openlayers.client.geometry.Point[] pointsArray = new org.gwtopenmaps.openlayers.client.geometry.Point[points
.size()];
int index = 0;
for (Point p : points) {
pointsArray[index++] = new org.gwtopenmaps.openlayers.client.geometry.Point(
p.getX(), p.getY());
}
return pointsArray;
}
private HasClickHandlers addFeature(Geometry geometry, Style style,
DrawPointStyle pointStyle) {
VectorFeature feature = new VectorFeature(geometry);
if (style != null) {
feature.setStyle(style);
feature.getStyle().setGraphicZIndex(0);
}
String featureId = DOM.createUniqueId();
feature.getAttributes().setAttribute("map4rdf_id", featureId);
if (geometry instanceof LineString) {
if(pointStyle!=null){
points.get(pointStyle.getStyle()).add(feature);
}else{
polylines.add(feature);
}
}else{
if(pointStyle!=null){
points.get(pointStyle.getStyle()).add(feature);
}else{
points.get(DrawPointStyle.getDefaultStyle()).add(feature);
}
}
/*TODO: REVIEW THIS OLD CODE AND TEST IF THE NEW CODE THROW ERRORS
if(geometry instanceof org.gwtopenmaps.openlayers.client.geometry.Polygon ){
if(pointStyle!=null){
points.get(pointStyle.getStyle()).add(feature);
}else{
points.get(DrawPointStyle.getDefaultStyle()).add(feature);
}
}
if (geometry instanceof org.gwtopenmaps.openlayers.client.geometry.Point) {
if (pointStyle != null) {
points.get(pointStyle.getStyle()).add(feature);
} else {
points.get(DrawPointStyle.getDefaultStyle()).add(feature);
}
}*/
vectorLayer.addFeature(feature);
features.add(feature);
return new FeatureHasClickHandlerWrapper(featureId);
}
private void removeFeature(VectorFeature feature) {
vectorLayer.removeFeature(feature);
features.remove(feature);
}
private Style getStyle(StyleMapShape<?> styleMapShape) {
Style style = new Style();
style.setFillColor(styleMapShape.getFillColor());
style.setFillOpacity(styleMapShape.getFillOpacity());
style.setStrokeColor(styleMapShape.getStrokeColor());
style.setStrokeOpacity(styleMapShape.getStrokeOpacity());
style.setStrokeWidth(styleMapShape.getStrokeWidth());
style.setCursor("pointer");
return style;
}
private Style getStyle(
org.gwtopenmaps.openlayers.client.geometry.Point olPoint,
DrawPointStyle pointStyle) {
Style style = new Style();
style.setExternalGraphic(GWT.getModuleBaseURL()
+ pointStyle.getImageURL());
style.setGraphicOffset(pointStyle.getDesplaceOffsetX(), pointStyle.getDesplaceOffsetY());
style.setGraphicSize(pointStyle.getWidth(), pointStyle.getHeight());
style.setFillOpacity(1);
style.setCursor("pointer");
return style;
}
private Style getTextStyle(String text) {
Style style = new Style();
style.setLabel(text);
style.setCursor("pointer");
style.setPointRadius(20);
return style;
}
@Override
public void onFeatureSelected(FeatureSelectedEvent eventObject) {
List<ClickHandler> clickHandlers = handlers.get(eventObject
.getVectorFeature().getAttributes()
.getAttributeAsString("map4rdf_id"));
if (clickHandlers != null) {
for (ClickHandler handler : clickHandlers) {
handler.onClick(new FeatureClickEvent(lastLonLatClicked));
}
}
}
@Override
public void onFeatureUnselected(FeatureUnselectedEvent eventObject) {
//Disabled because the new GUI do auto close without click on the image of last resource.
/*List<ClickHandler> clickHandlers = handlers.get(eventObject
.getVectorFeature().getAttributes()
.getAttributeAsString("map4rdf_id"));
if (clickHandlers != null) {
for (ClickHandler handler : clickHandlers) {
handler.onClick(null);
}
}*/
}
@Override
public void onMapMove(MapMoveEvent eventObject) {
return;
}
@Override
public void removePointsStyle(DrawPointStyle pointStyle) {
if (pointStyle != null) {
for (VectorFeature i : points.get(pointStyle.getStyle())) {
removeFeature(i);
}
points.get(pointStyle.getStyle()).clear();
} else {
for (VectorFeature i : points.get(DrawPointStyle.getDefaultStyle())) {
removeFeature(i);
}
points.get(DrawPointStyle.getDefaultStyle()).clear();
}
}
@Override
public void removePolylines() {
for (VectorFeature i : polylines) {
removeFeature(i);
}
polylines.clear();
}
@Override
public Map getOLMap() {
return map;
}
@Override
public void unselectFeatures() {
for(VectorFeature feature: vectorLayer.getSelectedFeatures()){
selectFeatureControl.unSelect(feature);
}
}
}