package org.sigmah.client.ui.widget.map; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * 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.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Style; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.Widget; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.sigmah.client.i18n.I18N; import org.sigmah.shared.dto.BoundingBoxDTO; /** * Simplify the creation of a WorldMap implementation by handling the * most commons user cases. * * @author Raphaƫl Calabro (rcalabro@ideia.fr) * @param <P> Native implementation of the pins */ public abstract class AbstractWorldMap<P> implements WorldMap { private static final String LOADING_MASK_STYLENAME = "ext-el-mask"; private static final String LOADING_MESSAGE_STYLENAME = "ext-el-mask-msg"; private final FlowPanel root; private final FlowPanel loadingMask; private boolean initialized; private boolean displayed; private boolean loading; private final Map<Pin, P> pins; private boolean useCenterAndZoom; private BoundingBoxDTO bounds; private double centerLongitude; private double centerLatitude; private int zoom; public AbstractWorldMap() { root = new FlowPanel(); root.getElement().getStyle().setPosition(Style.Position.RELATIVE); loadingMask = new FlowPanel(); loadingMask.setStyleName(LOADING_MASK_STYLENAME); final Label loadingMessage = new Label(I18N.CONSTANTS.loading()); loadingMessage.setStyleName(LOADING_MESSAGE_STYLENAME); loadingMask.add(loadingMessage); pins = new HashMap<Pin, P>(); bounds = new BoundingBoxDTO(); root.add(loadingMask); } protected abstract void displayBounds(BoundingBoxDTO bounds); protected abstract void displayCenterAndZoom(double latitude, double longitude, int zoom); protected abstract void displayPins(List<P> pins); protected abstract P createNativePin(Pin pin); protected abstract void movePin(P pin, double latitude, double longitude); protected abstract void addPinDragEndHandler(P pin, PinDragEndHandler dragEndHandler); protected abstract void init(); @Override public void setSize(String width, String height) { root.setWidth(width); root.setHeight(height); } @Override public void setBounds(BoundingBoxDTO bounds) { this.bounds = bounds; this.useCenterAndZoom = false; updateBounds(); } @Override public BoundingBoxDTO getBounds() { return new BoundingBoxDTO(this.bounds); } @Override public void setCenterAndZoom(double latitude, double longitude, int zoom) { this.centerLongitude = longitude; this.centerLatitude = latitude; this.zoom = zoom; this.useCenterAndZoom = true; updateBounds(); } @Override public void addPin(Pin pin) { addPinPrivate(pin); updatePins(); } private void addPinPrivate(Pin pin) { pin.setParent(this); final P nativePin; if(initialized) { nativePin = createNativePin(pin); for(final PinDragEndHandler dragEndHandler : pin.getPinDragEndHandlers()) { addPinDragEndHandler(nativePin, dragEndHandler); } } else { nativePin = null; } pins.put(pin, nativePin); } @Override public void setPins(List<Pin> pins) { this.pins.clear(); final BoundingBoxDTO pinBounds = new BoundingBoxDTO(0, 0, 0, 0); if(!pins.isEmpty()) { final Pin pin = pins.get(0); pinBounds.setX1(pin.getLongitude()); pinBounds.setX2(pin.getLongitude()); pinBounds.setY1(pin.getLatitude()); pinBounds.setY2(pin.getLatitude()); } for(final Pin pin : pins) { addPinPrivate(pin); // Extends the bounding box to cover all the pins. pinBounds.grow(pin.getLongitude(), pin.getLatitude()); } updatePins(); // Center the view on the given pins. Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { setBounds(pinBounds); } }); } @Override public void updatePinPosition(Pin pin) { final P nativePin = pins.get(pin); if(nativePin != null) { movePin(nativePin, pin.getLatitude(), pin.getLongitude()); } } @Override public void removeAllPins() { pins.clear(); updatePins(); } @Override public void addPinDragEndHandler(Pin pin, PinDragEndHandler dragEndHandler) { final P nativePin = pins.get(pin); if(nativePin != null) { addPinDragEndHandler(nativePin, dragEndHandler); } } @Override public Widget asWidget() { return root; } @Override public void setLoading(boolean loading) { this.loading = loading; updateLoadingMask(); } @Override public boolean isLoading() { return loading; } @Override public void setDisplayed(boolean displayed) { this.displayed = displayed; if(displayed && !initialized) { init(); } } protected void setInitialized(boolean initialized) { this.initialized = initialized; createNativePins(); updatePins(); updateBounds(); updateLoadingMask(); } protected Panel getRoot() { return root; } private void updateBounds() { if(initialized) { if(useCenterAndZoom) { displayCenterAndZoom(centerLatitude, centerLongitude, zoom); } else { displayBounds(bounds); } } } private void updatePins() { if(initialized) { displayPins(new ArrayList<P>(pins.values())); } } private void createNativePins() { for(final Map.Entry<Pin, P> entry : pins.entrySet()) { if(entry.getValue() == null) { final P nativePin = createNativePin(entry.getKey()); for(final PinDragEndHandler dragEndHandler : entry.getKey().getPinDragEndHandlers()) { addPinDragEndHandler(nativePin, dragEndHandler); } entry.setValue(nativePin); } } } private void updateLoadingMask() { if(!initialized || loading) { loadingMask.getElement().getStyle().clearDisplay(); } else { loadingMask.getElement().getStyle().setDisplay(Style.Display.NONE); } } }