package org.activityinfo.ui.client.component.report.editor.map; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Status; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.toolbar.ToolBar; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.rpc.AsyncCallback; import org.activityinfo.model.type.geo.AiLatLng; import org.activityinfo.i18n.shared.I18N; import org.activityinfo.legacy.client.Dispatcher; import org.activityinfo.legacy.client.monitor.MaskingAsyncMonitor; import org.activityinfo.legacy.shared.Log; import org.activityinfo.legacy.shared.command.GenerateElement; import org.activityinfo.legacy.shared.model.BaseMap; import org.activityinfo.legacy.shared.reports.content.AdminOverlay; import org.activityinfo.legacy.shared.reports.content.MapContent; import org.activityinfo.legacy.shared.reports.model.MapReportElement; import org.activityinfo.ui.client.EventBus; import org.activityinfo.ui.client.component.report.editor.map.symbols.LeafletMarkerDrilldownEventHandler; import org.activityinfo.ui.client.component.report.editor.map.symbols.LeafletReportOverlays; import org.activityinfo.ui.client.page.report.HasReportElement; import org.activityinfo.ui.client.page.report.ReportChangeHandler; import org.activityinfo.ui.client.page.report.ReportEventBus; import org.discotools.gwt.leaflet.client.LeafletResourceInjector; import org.discotools.gwt.leaflet.client.controls.zoom.Zoom; import org.discotools.gwt.leaflet.client.crs.epsg.EPSG3857; import org.discotools.gwt.leaflet.client.events.Event; import org.discotools.gwt.leaflet.client.events.handler.EventHandler; import org.discotools.gwt.leaflet.client.events.handler.EventHandler.Events; import org.discotools.gwt.leaflet.client.events.handler.EventHandlerManager; import org.discotools.gwt.leaflet.client.map.MapOptions; import org.discotools.gwt.leaflet.client.types.LatLng; /** * Displays the content of a MapElement using Google Maps. */ public class MapEditorMapView extends ContentPanel implements HasReportElement<MapReportElement> { private static final int DEFAULT_ZOOM_CONTROL_OFFSET_X = 5; private static final int ZOOM_CONTROL_OFFSET_Y = 5; private static final int RDC_CENTER_LONG = 25; private static final int RDC_CENTER_LAT = -1; private final Dispatcher dispatcher; private final ReportEventBus reportEventBus; private BaseMap currentBaseMap = null; private final Status statusWidget; private final LeafletMarkerDrilldownEventHandler markerDrilldownEventHandler; private MapReportElement model = new MapReportElement(); private LeafletReportOverlays overlays; // Model of a the map private MapContent content; private boolean zoomSet = false; // True when the first layer is just put on the map private boolean isFirstLayerUpdate = true; //private LargeMapControl zoomControl; private int zoomControlOffsetX = DEFAULT_ZOOM_CONTROL_OFFSET_X; private LeafletMap map; public MapEditorMapView(Dispatcher dispatcher, EventBus eventBus) { this.dispatcher = dispatcher; this.reportEventBus = new ReportEventBus(eventBus, this); this.reportEventBus.listen(new ReportChangeHandler() { @Override public void onChanged() { loadContent(); } }); this.markerDrilldownEventHandler = new LeafletMarkerDrilldownEventHandler(dispatcher); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { int bottomX = getAbsoluteLeft() + getOffsetWidth(); int bottomY = getAbsoluteTop() + getOffsetHeight(); markerDrilldownEventHandler.setPosition(bottomX, bottomY); } }); setLayout(new FitLayout()); setHeaderVisible(false); statusWidget = new Status(); ToolBar toolBar = new ToolBar(); toolBar.add(statusWidget); setBottomComponent(toolBar); LeafletResourceInjector.ensureInjected(); } public void setZoomControlOffsetX(int offset) { zoomControlOffsetX = offset; if (map != null) { try { Zoom zoomControl = map.getMap().getZoomControl(); Element container = zoomControl.getContainer(); container.getStyle().setMarginLeft(zoomControlOffsetX, Unit.PX); } catch (Exception e) { Log.error("Exception thrown while setting zoom control", e); } } } @Override public void bind(MapReportElement model) { this.model = model; loadContent(); } @Override public void disconnect() { reportEventBus.disconnect(); } @Override public MapReportElement getModel() { return model; } // // private ControlPosition zoomControlPosition() { // return new ControlPosition(ControlAnchor.TOP_LEFT, zoomControlOffsetX, // ZOOM_CONTROL_OFFSET_Y); // } /** * Updates the size of the map and adds Overlays to reflect the content of * the current selected indicators */ private void loadContent() { // Don't update when no layers are present if (model.getLayers().isEmpty()) { isFirstLayerUpdate = true; return; } // Prevent setting the extents for the MapWidget when more then 1 layer // is added if (isFirstLayerUpdate && model.getLayers().size() > 0) { isFirstLayerUpdate = false; } dispatcher.execute(new GenerateElement<MapContent>(model), new MaskingAsyncMonitor(this, I18N.CONSTANTS.loadingMap()), new AsyncCallback<MapContent>() { @Override public void onFailure(Throwable caught) { MessageBox.alert("Load failed", caught.getMessage(), null); } @Override public void onSuccess(MapContent result) { onContentLoaded(result); } }); } private void onContentLoaded(MapContent result) { Log.debug("MapPreview: Received content"); content = result; statusWidget.setStatus(result.getUnmappedSites().size() + " " + I18N.CONSTANTS.siteLackCoordiantes(), null); if (!isRendered()) { return; } if (map == null) { createMap(); } overlays.clear(); overlays.setBaseMap(result.getBaseMap()); overlays.addMarkers(result.getMarkers(), markerDrilldownEventHandler); for (AdminOverlay overlay : result.getAdminOverlays()) { overlays.addAdminLayer(overlay); } if (!zoomSet) { if (model.getZoomLevel() != -1 && model.getCenter() != null) { map.getMap() .setView(new LatLng(model.getCenter().getLat(), model.getCenter().getLng()), model.getZoomLevel(), true); } else { map.fitBounds(result.getExtents()); } zoomSet = true; } } private void createMap() { MapOptions mapOptions = new MapOptions(); mapOptions.setCenter(new LatLng(content.getExtents().getCenterY(), content.getExtents().getCenterX())); mapOptions.setZoom(6); mapOptions.setProperty("crs", new EPSG3857()); map = new LeafletMap(mapOptions); add(map); layout(); EventHandlerManager.addEventHandler(map.getMap(), Events.moveend, new EventHandler() { @Override public void handle(Event event) { updateModelFromMap(); } }); EventHandlerManager.addEventHandler(map.getMap(), Events.zoomend, new EventHandler() { @Override public void handle(Event event) { updateModelFromMap(); } }); overlays = new LeafletReportOverlays(map.getMap()); } private void updateModelFromMap() { model.setZoomLevel(map.getMap().getZoom()); LatLng center = map.getMap().getBounds().getCenter(); model.setCenter(new AiLatLng(center.lat(), center.lng())); } }