package org.diretto.web.richwebclient.view.widgets.googlemap.client.base;
import com.google.gwt.maps.client.HasMap;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapTypeControlOptions;
import com.google.gwt.maps.client.MapTypeControlStyle;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.NavigationControlOptions;
import com.google.gwt.maps.client.NavigationControlStyle;
import com.google.gwt.maps.client.base.HasLatLng;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.event.Event;
import com.google.gwt.maps.client.event.HasMouseEvent;
import com.google.gwt.maps.client.event.MouseEventCallback;
import com.google.gwt.maps.client.impl.ControlPositionImpl;
import com.google.gwt.maps.client.impl.MapTypeControlStyleImpl;
import com.google.gwt.maps.client.impl.NavigationControlStyleImpl;
import org.diretto.web.richwebclient.view.widgets.googlemap.client.markers.IconFactory;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.client.Paintable;
import com.vaadin.terminal.gwt.client.UIDL;
/**
* This {@code abstract} class provides a default implementation and class
* constraints for {@code GoogleMap} client side components.
*
* @author Tobias Schlecht
*/
public abstract class AbstractVGoogleMap extends Composite implements Paintable
{
protected SimplePanel wrapperPanel = null;
protected MapWidget mapWidget = null;
protected HasMap map = null;
protected MapOptions mapOptions = null;
protected static String paintableID;
protected static ApplicationConnection applicationConnection;
private boolean zoomChanged = false;
private boolean dragging = false;
private HasLatLng center = null;
private int zoomLevel;
private double lowerLeftLongitude;
private double upperRightLongitude;
private int absoluteLeft = -1;
private int absoluteRight;
private int absoluteTop;
private int absoluteBottom;
/**
* Provides base implementation to construct a {@code VGoogleMap}.
*/
public AbstractVGoogleMap()
{
wrapperPanel = new SimplePanel();
initWidget(wrapperPanel);
}
/**
* Initializes the {@link MapWidget}.
*
* @param zoomLevel The initial zoom level
* @param centerLatitude The initial center latitude in degrees
* @param centerLongitude The initial center longitude in degrees
* @param mapType The initial {@code MapType}
* @param scrollWheelZoomingEnabled {@code true} if scroll wheel zooming is
* enabled; otherwise {@code false}
* @param draggingEnabled {@code true} if dragging is enabled; otherwise
* {@code false}
*/
private void initMap(int zoomLevel, double centerLatitude, double centerLongitude, MapType mapType, boolean scrollWheelZoomingEnabled, boolean draggingEnabled)
{
mapOptions = new MapOptions();
mapOptions.setZoom(zoomLevel);
mapOptions.setCenter(new LatLng(centerLatitude, centerLongitude));
mapOptions.setMapTypeId(mapType.getAPIValue());
mapOptions.setScrollwheel(scrollWheelZoomingEnabled);
mapOptions.setDraggable(draggingEnabled);
mapOptions.setDisableDoubleClickZoom(true);
mapOptions.setMapTypeControl(true);
mapOptions.setNavigationControl(true);
NavigationControlOptions navigationControlOptions = new NavigationControlOptions(new ControlPositionImpl(), new NavigationControlStyleImpl());
navigationControlOptions.setStyle(NavigationControlStyle.SMALL);
mapOptions.setNavigationControlOptions(navigationControlOptions);
MapTypeControlOptions mapTypeControlOptions = new MapTypeControlOptions(new ControlPositionImpl(), new MapTypeControlStyleImpl());
mapTypeControlOptions.setStyle(MapTypeControlStyle.HORIZONTAL_BAR);
mapOptions.setMapTypeControlOptions(mapTypeControlOptions);
mapWidget = new MapWidget(mapOptions);
map = mapWidget.getMap();
mapWidget.setWidth("100%");
mapWidget.setHeight("100%");
wrapperPanel.add(mapWidget);
triggerResizeMapEvent();
}
@Override
public void setWidth(String width)
{
super.setWidth(width);
wrapperPanel.setWidth(width);
if(map != null && isMapDivResized())
{
triggerResizeMapEvent();
}
}
@Override
public void setHeight(String height)
{
super.setHeight(height);
wrapperPanel.setHeight(height);
if(map != null && isMapDivResized())
{
triggerResizeMapEvent();
}
}
@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection applicationConnection)
{
if(applicationConnection.updateComponent(this, uidl, true))
{
return;
}
paintableID = uidl.getId();
AbstractVGoogleMap.applicationConnection = applicationConnection;
if(mapWidget == null)
{
if(IconFactory.INSTANCE.getApplicationURL() == null)
{
IconFactory.INSTANCE.setApplicationURL(uidl.getStringAttribute("applicationURL"));
}
initMap(uidl.getIntVariable("zoomLevel"), uidl.getDoubleVariable("centerLatitude"), uidl.getDoubleVariable("centerLongitude"), MapType.valueOf(uidl.getStringVariable("mapType")), uidl.getBooleanAttribute("scrollWheelZoomingEnabled"), uidl.getBooleanAttribute("draggingEnabled"));
addEventListeners();
}
else
{
setMapValues(uidl.getIntVariable("zoomLevel"), uidl.getDoubleVariable("centerLatitude"), uidl.getDoubleVariable("centerLongitude"), MapType.valueOf(uidl.getStringVariable("mapType")));
}
}
/**
* Sets the given values to the map.
*
* @param zoomLevel The zoom level
* @param centerLatitude The center latitude in degrees
* @param centerLongitude The center longitude in degrees
* @param mapType The {@code MapType}
*/
private void setMapValues(int zoomLevel, double centerLatitude, double centerLongitude, MapType mapType)
{
if(map.getZoom() != zoomLevel)
{
map.setZoom(zoomLevel);
}
if(map.getCenter().getLatitude() != centerLatitude || map.getCenter().getLongitude() != centerLongitude)
{
map.setCenter(new LatLng(centerLatitude, centerLongitude));
}
if(!map.getMapTypeId().equals(mapType.getAPIValue()))
{
map.setMapTypeId(mapType.getAPIValue());
}
}
/**
* Sends the variables to the server side component.
*/
private void updateVariables()
{
double lowerLeftLongitude = map.getBounds().getSouthWest().getLongitude();
double upperRightLongitude = map.getBounds().getNorthEast().getLongitude();
if(lowerLeftLongitude > upperRightLongitude)
{
lowerLeftLongitude = this.lowerLeftLongitude;
upperRightLongitude = this.upperRightLongitude;
}
applicationConnection.updateVariable(paintableID, "zoomLevel", map.getZoom(), false);
applicationConnection.updateVariable(paintableID, "centerLatitude", map.getCenter().getLatitude(), false);
applicationConnection.updateVariable(paintableID, "centerLongitude", map.getCenter().getLongitude(), false);
applicationConnection.updateVariable(paintableID, "mapType", MapType.getValue(map.getMapTypeId()).toString(), false);
applicationConnection.updateVariable(paintableID, "lowerLeftLatitude", map.getBounds().getSouthWest().getLatitude(), false);
applicationConnection.updateVariable(paintableID, "lowerLeftLongitude", lowerLeftLongitude, false);
applicationConnection.updateVariable(paintableID, "upperRightLatitude", map.getBounds().getNorthEast().getLatitude(), false);
applicationConnection.updateVariable(paintableID, "upperRightLongitude", upperRightLongitude, true);
}
/**
* Adds {@code EventListener}s to the map.
*/
private void addEventListeners()
{
Event.addListener(map, "idle", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
updateVariables();
}
});
Event.addListener(map, "maptypeid_changed", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
updateVariables();
}
});
Event.addListener(map, "zoom_changed", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
zoomChanged = true;
}
});
Event.addListener(map, "dragstart", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
dragging = true;
}
});
Event.addListener(map, "dragend", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
dragging = false;
}
});
Event.addListener(map, "bounds_changed", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
if(center == null)
{
updateGlobalMapVariables();
}
int newZoomLevel = map.getZoom();
double newLowerLeftLongitude = map.getBounds().getSouthWest().getLongitude();
double newUpperRightLongitude = map.getBounds().getNorthEast().getLongitude();
if(zoomChanged)
{
zoomChanged = false;
if(newZoomLevel < zoomLevel)
{
if(newLowerLeftLongitude > lowerLeftLongitude || newUpperRightLongitude < upperRightLongitude)
{
map.setZoom(zoomLevel);
updateVariables();
}
else
{
updateGlobalMapVariables();
}
}
else
{
updateGlobalMapVariables();
}
}
else if(dragging)
{
if(newLowerLeftLongitude > newUpperRightLongitude || (newLowerLeftLongitude > lowerLeftLongitude && newUpperRightLongitude < upperRightLongitude) || (newLowerLeftLongitude < lowerLeftLongitude && newUpperRightLongitude > upperRightLongitude))
{
map.setCenter(center);
updateVariables();
}
else
{
updateGlobalMapVariables();
}
}
}
});
Event.addListener(map, "resize", new MouseEventCallback()
{
@Override
public void callback(HasMouseEvent event)
{
if(center == null)
{
updateGlobalMapVariables();
}
double newLowerLeftLongitude = map.getBounds().getSouthWest().getLongitude();
double newUpperRightLongitude = map.getBounds().getNorthEast().getLongitude();
if(Math.abs(newLowerLeftLongitude - lowerLeftLongitude) > 100 || Math.abs(newUpperRightLongitude - upperRightLongitude) > 100)
{
map.setZoom(zoomLevel + 1);
updateVariables();
}
else
{
updateGlobalMapVariables();
}
}
});
}
/**
* Determines whether the DIV block of the map has been resized.
*
* @return {@code true} if the DIV block has been resized; otherwise
* {@code false}
*/
private boolean isMapDivResized()
{
Element div = map.getDiv();
if(absoluteLeft == -1)
{
updateGlobalDivVariables();
return false;
}
if(absoluteLeft != div.getAbsoluteLeft() || absoluteRight != div.getAbsoluteRight() || absoluteTop != div.getAbsoluteTop() || absoluteBottom != div.getAbsoluteBottom())
{
updateGlobalDivVariables();
return true;
}
return false;
}
/**
* Triggers a {@code resize} map event.
*/
private void triggerResizeMapEvent()
{
HasLatLng center = map.getCenter();
Event.trigger(map, "resize");
map.setCenter(center);
}
/**
* Updates the global variables of the map.
*/
private void updateGlobalMapVariables()
{
center = map.getCenter();
zoomLevel = map.getZoom();
double lowerLeftLongitude = map.getBounds().getSouthWest().getLongitude();
double upperRightLongitude = map.getBounds().getNorthEast().getLongitude();
if(lowerLeftLongitude < upperRightLongitude)
{
this.lowerLeftLongitude = lowerLeftLongitude;
this.upperRightLongitude = upperRightLongitude;
}
}
/**
* Updates the global variables of the DIV block of the map.
*/
private void updateGlobalDivVariables()
{
Element div = map.getDiv();
absoluteLeft = div.getAbsoluteLeft();
absoluteRight = div.getAbsoluteRight();
absoluteTop = div.getAbsoluteTop();
absoluteBottom = div.getAbsoluteBottom();
}
}