/* * Copyright (c) 2011, H.Westphal * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the jo-widgets.org nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.jowidgets.addons.map.common.impl; import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; import org.jowidgets.addons.map.common.IAvailableCallback; import org.jowidgets.addons.map.common.IDesignationListener; import org.jowidgets.addons.map.common.IMap; import org.jowidgets.addons.map.common.IMapContext; import org.jowidgets.addons.map.common.IViewChangeListener; import org.jowidgets.util.Assert; import de.micromata.opengis.kml.v_2_2_0.Feature; import de.micromata.opengis.kml.v_2_2_0.Kml; import de.micromata.opengis.kml.v_2_2_0.KmlFactory; import de.micromata.opengis.kml.v_2_2_0.Point; public final class GoogleEarth extends Composite implements IMap, IMapContext { private final Map<Class<?>, IDesignationCallback<?>> designationCallbacks = new HashMap<Class<?>, IDesignationCallback<?>>() { private static final long serialVersionUID = 1L; { put(Point.class, new PointDesignationCallback()); } }; private final List<IViewChangeListener> viewChangeListeners = new CopyOnWriteArrayList<IViewChangeListener>(); private final String apiKey; private final Browser browser; private volatile String pluginName; private volatile boolean initialized; private volatile boolean initializing; private String language = Locale.getDefault().getLanguage(); private IAvailableCallback availableCallback; private IDesignationCallback<?> designationCallback; public GoogleEarth(final Composite parent, final String apiKey) { this(parent, apiKey, SWT.NONE); } public GoogleEarth(final Composite parent, final String apiKey, final int style) { super(parent, style); Assert.paramNotEmpty(apiKey, "apiKey"); this.apiKey = apiKey; super.setLayout(new FillLayout()); browser = new Browser(this, style & SWT.MOZILLA); createCallbacks(); } @Override public void setLayout(final Layout layout) { // ignore } @Override public void setLanguage(final String language) { Assert.paramNotEmpty(language, "language"); if (initialized || initializing) { throw new IllegalStateException("language cannot be set during or after initialization"); } this.language = language; } @Override public void initialize(final IAvailableCallback availableCallback) { checkWidget(); if (initialized) { throw new IllegalStateException("widget is already initialized"); } if (initializing) { throw new IllegalStateException("widget is currently initializing"); } initializing = true; this.availableCallback = availableCallback; try { final String html = IOUtils.toString(GoogleEarth.class.getResourceAsStream("googleearth.html"), "UTF-8") .replace("@API_KEY@", apiKey) .replace("@LANG@", language); if (!browser.setText(html)) { throw new RuntimeException("initialization failed"); } } catch (final IOException e) { throw new Error(e); } } private void createCallbacks() { new BrowserFunction(browser, "onMapInitialized") { @Override public Object function(final Object[] arguments) { super.function(arguments); pluginName = (String) arguments[0]; initialized = true; initializing = false; if (availableCallback != null) { getBrowser().getDisplay().asyncExec(new Runnable() { @Override public void run() { availableCallback.onAvailable(GoogleEarth.this); } }); } return null; } }; new BrowserFunction(browser, "onMapError") { @Override public Object function(final Object[] arguments) { super.function(arguments); final String error = (String) arguments[0]; final Label label = new Label(GoogleEarth.this, SWT.NONE); label.setText("Google Earth could not be initialized: " + error); // TODO i18n browser.dispose(); GoogleEarth.this.layout(); initialized = true; initializing = false; return null; } }; new BrowserFunction(browser, "onViewChange") { @Override public Object function(final Object[] arguments) { super.function(arguments); getBrowser().getDisplay().asyncExec(new Runnable() { @Override public void run() { for (final IViewChangeListener listener : viewChangeListeners) { listener.onViewChange( (Double) arguments[0], (Double) arguments[1], (Double) arguments[2], (Double) arguments[3]); } } }); return null; } }; } @Override public double[] getBoundingBox() { checkWidget(); checkAvailable(); final Object[] res = (Object[]) browser.evaluate("return new Array(ge.getView().getViewportGlobeBounds().getNorth(), ge.getView().getViewportGlobeBounds().getSouth(), ge.getView().getViewportGlobeBounds().getEast(), ge.getView().getViewportGlobeBounds().getWest())"); return new double[] {(Double) res[0], (Double) res[1], (Double) res[2], (Double) res[3]}; } @Override public boolean flyTo(final String placemarkId, final double range) { checkWidget(); checkAvailable(); final Object result = browser.evaluate("return flyToPlacemark('" + StringEscapeUtils.escapeJavaScript(placemarkId) + "', " + range + ");"); return result instanceof Boolean ? (Boolean) result : false; } @Override public boolean flyTo(final double latitude, final double longitude, final double range) { checkWidget(); checkAvailable(); return browser.execute("flyTo(" + latitude + "," + longitude + "," + range + ");"); } @Override public boolean addFeature(final Feature feature) { checkWidget(); checkAvailable(); final Kml kml = KmlFactory.createKml(); kml.setFeature(feature); final StringWriter sw = new StringWriter(); kml.marshal(sw); return browser.execute(pluginName + ".getFeatures().appendChild(" + pluginName + ".parseKml('" + StringEscapeUtils.escapeJavaScript(sw.toString()) + "'));"); } @Override public boolean removeFeature(final String featureId) { checkWidget(); checkAvailable(); final Object result = browser.evaluate("return removeFeature('" + StringEscapeUtils.escapeJavaScript(featureId) + "');"); return result instanceof Boolean ? (Boolean) result : false; } @Override public boolean removeAllFeatures() { checkWidget(); checkAvailable(); return browser.execute("removeAllFeatures();"); } @Override public boolean isInitialized() { return initialized; } @Override public boolean isAvailable() { return pluginName != null; } private void checkAvailable() { if (!isAvailable()) { throw new IllegalStateException("widget is not available"); } } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public boolean startDesignation(final Class type, final IDesignationListener listener) { checkWidget(); checkAvailable(); Assert.paramNotNull(listener, "listener"); if (designationCallback != null) { throw new IllegalStateException("widget is already in designation mode"); } designationCallback = designationCallbacks.get(type); if (designationCallback == null) { throw new UnsupportedOperationException(type.getName() + " is not supported as designation type"); } return designationCallback.register(browser, listener); } @Override public boolean endDesignation() { checkWidget(); checkAvailable(); if (designationCallback == null) { throw new IllegalStateException("widget is not in designation mode"); } try { return designationCallback.unregister(); } finally { designationCallback = null; } } @Override public boolean isDesignationRunning() { checkWidget(); checkAvailable(); return designationCallback != null; } @Override public void addViewChangeListener(final IViewChangeListener listener) { viewChangeListeners.add(listener); } @Override public boolean removeViewChangeListener(final IViewChangeListener listener) { return viewChangeListeners.remove(listener); } @Override public Set<Class<?>> getSupportedDesignationClasses() { return designationCallbacks.keySet(); } }