package com.project.website.canvas.client.canvastools.map; import java.util.HashMap; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.IsWidget; import com.google.gwt.user.client.ui.Widget; import com.project.gwtmapstraction.client.mxn.LatLonPoint; import com.project.gwtmapstraction.client.mxn.MapProvider; import com.project.gwtmapstraction.client.mxn.Mapstraction; import com.project.gwtmapstraction.client.mxn.Marker; import com.project.shared.client.events.SimpleEvent.Handler; import com.project.shared.client.events.SingleEvent; import com.project.shared.client.handlers.RegistrationsManager; import com.project.shared.client.utils.ElementUtils; import com.project.shared.client.utils.HandlerUtils; import com.project.shared.client.utils.widgets.DialogWithZIndex; import com.project.shared.client.utils.widgets.WidgetUtils; import com.project.shared.data.Location; import com.project.shared.data.Point2D; import com.project.shared.data.funcs.AsyncFunc; import com.project.shared.data.funcs.Func; import com.project.shared.utils.StringUtils; import com.project.website.canvas.client.canvastools.base.CanvasToolCommon; import com.project.website.canvas.client.canvastools.base.CanvasToolEvents; import com.project.website.canvas.client.canvastools.base.ResizeMode; import com.project.website.canvas.client.canvastools.base.interfaces.CanvasTool; import com.project.website.canvas.client.canvastools.base.interfaces.ICanvasToolEvents; import com.project.website.canvas.client.resources.CanvasResources; import com.project.website.canvas.shared.data.ElementData; import com.project.website.canvas.shared.data.MapData; public class MapTool extends Composite implements CanvasTool<MapData> { private static final double DEFAULT_MAP_LONGITUDE = 0; private static final double DEFAULT_MAP_LATITUDE = 0; private static final int DEFAULT_MAP_ZOOM = 1; private static final MapProvider DEFAULT_MAP_PROVIDER = MapProvider.GOOGLE_V3; // Don't expose the Microsoft maps provider, the way we work with it is buggy... private static final Iterable<MapProvider> userAvailableProviders = Iterables.filter(MapToolStaticUtils.AVAILABLE_PROVIDERS, Predicates.not(Predicates.equalTo(MapProvider.MICROSOFT))); interface MapToolUiBinder extends UiBinder<Widget, MapTool> { } private static MapToolUiBinder uiBinder = GWT.create(MapToolUiBinder.class); @UiField FlowPanel mainPanel; @UiField FlowPanel mapPanel; @UiField FlowPanel mapLoadingPanel; private CanvasToolEvents _toolEvents = new CanvasToolEvents(this); private final MapToolBar _toolbar = new MapToolBar(); private final RegistrationsManager registrationsManager = new RegistrationsManager(); private DialogBox optionsDialog; private MapToolOptions mapToolOptionsWidget; private HashMap<MapProvider, Widget> mapWidgets = new HashMap<MapProvider, Widget>(); private Mapstraction mapstraction = null; private MapData mapData = null; private final SingleEvent<Void> asyncInitCompleted = new SingleEvent<Void>(); public MapTool() { initWidget(uiBinder.createAndBindUi(this)); CanvasToolCommon.initCanvasToolWidget(this); this.addStyleName(CanvasResources.INSTANCE.main().mapToolEmpty()); this.getApiLoadedAndAttachedAsyncFunc() .then(getEnsureSetupProviderAsyncFunc(DEFAULT_MAP_PROVIDER)) .then(new Func.VoidAction() { @Override public void exec() { asyncInitCompleted.dispatch(null); }}) .run(null); } private AsyncFunc<Void, Void> getApiLoadedAndAttachedAsyncFunc() { return MapToolStaticUtils.getLoadMapScriptsAsyncFunc() .then(WidgetUtils.getOnAttachAsyncFunc(this)); } @Override public ICanvasToolEvents getToolEvents() { return this._toolEvents; } @Override public void bind() { this.registrationsManager.add(this._toolbar.getOptionsLink().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { showOptions(); } })); this.registrationsManager.add(this._toolbar.getOptionsBar().addDomHandler(new MouseDownHandler() { @Override public void onMouseDown(MouseDownEvent event) { if (event.isControlKeyDown()) { _toolEvents.dispatchMoveStartRequestEvent(event); } } }, MouseDownEvent.getType())); this.registrationsManager.add(this._toolbar.getMapSearchButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { mapSearch(); } })); this.registrationsManager.add(this._toolbar.getRemoveMarkersLink().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (null != mapstraction) { mapstraction.removeAllMarkers(); } _toolbar.getRemoveMarkersLink().setEnabled(false); _toolbar.getRemoveMarkersLink().addStyleName(CanvasResources.INSTANCE.main().disabledLink()); } })); this.registrationsManager.add(this._toolbar.getMapSearchTextBox().addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { mapSearch(); } } })); } @Override public boolean canRotate() { return true; } @Override public ResizeMode getResizeMode() { return ResizeMode.BOTH; } @Override public MapData getValue() { if (this.isReady()) { LatLonPoint center = this.mapstraction.getCenter(); this.mapData.center = new Location(); this.mapData.center.latitude = center.getLat(); this.mapData.center.longitude = center.getLon(); this.mapData.zoom = this.mapstraction.getZoom(); this.mapData.mapType = MapToolStaticUtils.fromMapstractionMapType(this.mapstraction.getMapType()); } return this.mapData; } @Override public void setActive(boolean isActive) { // TODO Auto-generated method stub } @Override public void setElementData(ElementData data) { this.setValue((MapData) data); } @Override public void setValue(MapData value) { this.mapData = value; this.applyMapDataToWidget(); } @Override public void setViewMode(boolean isViewMode) { this._toolbar.getOptionsLink().setVisible(false == isViewMode); } @Override public void onResize() { // widget may be null if api not loaded yet if (this.isReady()) { this.updateMapSize(); } } private void applyMapDataToWidget() { final MapProvider provider = this.getCurrentSelectedProvider(); // apply the data to the widget only after the async init has finished. this.asyncInitCompleted.addHandler(HandlerUtils.fromAsyncFunc( this.getEnsureSetupProviderAsyncFunc(provider) .then(new Func.VoidAction() { @Override public void exec() { actualApplyMapDataToWidget(provider, getWidgetForProvider(provider)); }}) )); } private void actualApplyMapDataToWidget(MapProvider provider, Widget mapWidget) { if (null == this.mapData) { return; } if (null == this.mapData.center) { this.mapData.center = new Location(); this.mapData.center.latitude = DEFAULT_MAP_LATITUDE; this.mapData.center.longitude = DEFAULT_MAP_LONGITUDE; this.mapData.zoom = DEFAULT_MAP_ZOOM; } this.removeStyleName(CanvasResources.INSTANCE.main().mapToolEmpty()); // we MUST first swap the api, because some functions are not // implemented by all APIS this.mapstraction.swap(provider, mapWidget.getElement()); this.mapstraction.setCenter(LatLonPoint.create(this.mapData.center.latitude, this.mapData.center.longitude)); this.mapstraction.setZoom(this.mapData.zoom); this.mapstraction.setMapType(MapToolStaticUtils.fromMapType(this.mapData.mapType)); mapWidget.setVisible(true); this.updateMapSize(); } private AsyncFunc<Void,Void> getEnsureSetupProviderAsyncFunc(final MapProvider provider) { Widget mapWidget = getWidgetForProvider(provider); if (null == mapWidget) { mapWidget = this.createWidgetForProvider(provider); } return WidgetUtils.getOnAttachAsyncFunc(mapWidget) .then(new Func.VoidAction() { @Override public void exec() { ensureCreatedMapstractionInstance(getWidgetForProvider(provider), provider); }}); } private void ensureCreatedMapstractionInstance(Widget mapWidget, MapProvider provider) { if (null != this.mapstraction) { return; } this.mapstraction = Mapstraction.createInstance(mapWidget.getElement(), provider, true); this.mapstraction.setDebug(true); this.mapstraction.addSmallControls(); this.mapstraction.enableScrollWheelZoom(); this.mapstraction.setCenter(LatLonPoint.create(DEFAULT_MAP_LATITUDE, DEFAULT_MAP_LONGITUDE)); this.mapstraction.setZoom(DEFAULT_MAP_ZOOM); this._toolbar.getOptionsLink().removeStyleName(CanvasResources.INSTANCE.main().disabledLink()); } private Widget getWidgetForProvider(MapProvider provider) { return this.mapWidgets.get(provider); } private boolean isReady() { return (null != this.mapData) && this.isAttached() && MapToolStaticUtils.isApiLoaded() && (null != this.mapstraction); } private void updateMapSize() { Point2D widgetSize = ElementUtils.getElementClientSize(this.getWidgetForProvider(getCurrentSelectedProvider()).getElement()); this.mapstraction.resizeTo(widgetSize.getX(), widgetSize.getY()); } private MapProvider getCurrentSelectedProvider() { if (StringUtils.isWhitespaceOrNull(this.mapData.provider)) { return DEFAULT_MAP_PROVIDER; } try { return MapProvider.valueOf(this.mapData.provider); } catch (IllegalArgumentException e) { return DEFAULT_MAP_PROVIDER; } } private Widget createWidgetForProvider(final MapProvider provider) { Widget mapWidget = getWidgetForProvider(provider); if (null != mapWidget) { return mapWidget; } mapWidget = new FlowPanel(); // For mapstraction to work properly, must set the element id ElementUtils.generateId("mw_" + provider.ordinal(), mapWidget.getElement()); mapWidget.addStyleName(CanvasResources.INSTANCE.main().mapToolMapWidget()); this.mapWidgets.put(provider, mapWidget); this.mapPanel.add(mapWidget); return mapWidget; } protected void showOptions() { if (false == this.isReady()) { return; } if (null == this.optionsDialog) { this.optionsDialog = new DialogWithZIndex(false, true); this.optionsDialog.setText("Map options"); } if (null == this.mapToolOptionsWidget) { this.mapToolOptionsWidget = new MapToolOptions(userAvailableProviders); this.mapToolOptionsWidget.addDoneHandler(new Handler<Void>() { @Override public void onFire(Void arg) { optionsDialog.hide(); setValue(mapToolOptionsWidget.getValue()); } }); this.optionsDialog.add(this.mapToolOptionsWidget); this.mapToolOptionsWidget.addValueChangeHandler(new ValueChangeHandler<MapData>() { @Override public void onValueChange(ValueChangeEvent<MapData> event) { setValue(event.getValue()); } }); } this.mapToolOptionsWidget.setValue(this.getValue()); this.optionsDialog.center(); } protected void mapSearch() { final String query = this._toolbar.getMapSearchTextBox().getText(); if (StringUtils.isWhitespaceOrNull(query)) { return; } final MapTool that = this; this.asyncInitCompleted.addHandler(HandlerUtils.fromAsyncFunc( WidgetUtils.setEnabledFunc(this._toolbar.getMapSearchButton(), false) .then(this.getEnsureSetupProviderAsyncFunc(MapProvider.MICROSOFT)) .then(new Func.VoidAction() { @Override public void exec() { that.performMapSearch(query); }}) .then(WidgetUtils.setEnabledFunc(this._toolbar.getMapSearchButton(), true)) )); } private void performMapSearch(String query) { // Assumes the map provider is ready. Element microsoftMapElem = this.getWidgetForProvider(MapProvider.MICROSOFT).getElement(); this.mapstraction.swap(MapProvider.MICROSOFT, microsoftMapElem); final MapTool that = this; this.mapLoadingPanel.setVisible(true); MicrosoftMapFind finder = new MicrosoftMapFind() { @Override public void callback(boolean found, double lat, double lon, int zoomLevel) { that.mapLoadingPanel.setVisible(false); if (found) { that.mapData.center.latitude = lat; that.mapData.center.longitude = lon; that.mapData.zoom = zoomLevel; } that.applyMapDataToWidget(); if (found) { that.mapstraction.addMarker(Marker.create(LatLonPoint.create(lat, lon))); that._toolbar.getRemoveMarkersLink().setEnabled(true); that._toolbar.getRemoveMarkersLink().removeStyleName(CanvasResources.INSTANCE.main().disabledLink()); } } }; finder.find(this.mapstraction.getMap(), query); //bingLocationsQuery(query); } @Override public IsWidget getToolbar() { return this._toolbar; } // private void bingLocationsQuery(String query) // { // BindLocationRequestFactory factory = GWT.create(BindLocationRequestFactory.class); // BindLocationRequest request = factory.create(); // request.locations(ApiKeys.BIND_MAPS, query) // . // .request(new JsonpRequestCallback<LocationResponse>() { // @Override // public void onSuccess(LocationResponse result) // { // Window.alert("Result: " + result); // } // }); // } }