org.wicketstuff.openlayers.AjaxOpenLayersMap.java Source code

Java tutorial

Introduction

Here is the source code for org.wicketstuff.openlayers.AjaxOpenLayersMap.java

Source

/*
 * 
 * ==============================================================================
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.wicketstuff.openlayers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.resource.CoreLibrariesContributor;
import org.wicketstuff.openlayers.api.Bounds;
import org.wicketstuff.openlayers.api.IJavascriptComponent;
import org.wicketstuff.openlayers.api.LonLat;
import org.wicketstuff.openlayers.api.Marker;
import org.wicketstuff.openlayers.api.Overlay;
import org.wicketstuff.openlayers.api.feature.Feature;
import org.wicketstuff.openlayers.api.feature.FeatureStyle;
import org.wicketstuff.openlayers.api.layer.Layer;
import org.wicketstuff.openlayers.api.layer.Vector;
import org.wicketstuff.openlayers.event.EventType;
import org.wicketstuff.openlayers.event.OverlayListenerBehavior;

/**
 * 
 * @author Marin Mandradjiev (marinsm@hotmail.com)
 * 
 */
public class AjaxOpenLayersMap extends WebMarkupContainer implements IOpenLayersMap {
    private static final long serialVersionUID = 159201381315392564L;

    private List<Layer> layers;
    private final HashMap<String, String> options;
    private final List<Feature> features;
    private final List<IJavascriptComponent> controls = new ArrayList<IJavascriptComponent>();
    private final List<Overlay> overlays = new ArrayList<Overlay>();
    private final List<FeatureStyle> featureStyles;
    private final HashMap<String, Vector> featureVectors = new HashMap<String, Vector>();
    private boolean externalControls = false;
    private Bounds bounds = null;
    private LonLat center = null;
    private Integer zoom = null;
    private String businessLogicProjection = null;
    private String markersLayerName = null;

    public AjaxOpenLayersMap(final String id, final List<Layer> layers) {
        this(id, layers, null);
    }

    public AjaxOpenLayersMap(final String id, final List<Layer> layers, final HashMap<String, String> options) {
        this(id, layers, options, null);
    }

    public AjaxOpenLayersMap(final String id, final List<Layer> layers, final HashMap<String, String> options,
            final List<Feature> features) {
        this(id, layers, options, features, null);
    }

    public AjaxOpenLayersMap(final String id, final List<Layer> layers, final HashMap<String, String> options,
            final List<Feature> features, final List<FeatureStyle> featureStyles) {
        super(id);
        setOutputMarkupId(true);
        this.layers = (layers == null) ? new ArrayList<Layer>() : layers;
        this.options = (options == null) ? new HashMap<String, String>() : options;
        this.features = (features == null) ? new ArrayList<Feature>() : features;
        this.featureStyles = (featureStyles == null) ? new ArrayList<FeatureStyle>() : featureStyles;
        add(new Behavior() {

            private static final long serialVersionUID = 1L;

            @Override
            public void renderHead(Component c, IHeaderResponse response) {
                response.render(OnDomReadyHeaderItem.forScript(getJSinit()));
            }
        });
        for (Feature feature : this.features) {
            getFeatureVector(feature.getDisplayInLayer());
        }
    }

    public static void onPageRenderHead(IHeaderResponse response, String pathToOpenLayersJS) {
        if (pathToOpenLayersJS == null || pathToOpenLayersJS.trim().length() == 0) {
            pathToOpenLayersJS = "http://openlayers.org/api/";
        } else {
            pathToOpenLayersJS = pathToOpenLayersJS.trim();
            if (!pathToOpenLayersJS.endsWith("/")) {
                pathToOpenLayersJS = pathToOpenLayersJS + "/";
            }
        }
        pathToOpenLayersJS = pathToOpenLayersJS + "OpenLayers.js";
        response.render(JavaScriptHeaderItem.forUrl(pathToOpenLayersJS));
        // TODO Import all other JS files which will be used later on
        CoreLibrariesContributor.contributeAjax(Application.get(), response);
        response.render(JavaScriptHeaderItem
                .forReference(new JavaScriptResourceReference(AjaxOpenLayersMap.class, "wicket-openlayersmap.js")));
    }

    public void setExternalControls(boolean externalControls) {
        this.externalControls = externalControls;
    }

    public boolean isExternalControls() {
        return externalControls;
    }

    public void setBounds(Bounds bounds) {
        this.bounds = bounds;
    }

    public Bounds getBounds() {
        return bounds;
    }

    public void setCenter(LonLat center) {
        this.center = center;

        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJSSetCenter());
        }
    }

    public LonLat getCenter() {
        return center;
    }

    public void setZoom(Integer zoom) {
        this.zoom = zoom;
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJSSetCenter());
        }
    }

    public Integer getZoom() {
        return zoom;
    }

    public void setCenter(LonLat center, Integer zoom) {
        this.center = center;
        this.zoom = zoom;
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJSSetCenter());
        }
    }

    private String getJSSetCenter() {
        if (center == null) {
            return "";
        }
        String transformation = "";
        if (getBusinessLogicProjection() != null) {
            transformation = ".transform(new OpenLayers.Projection(\"" + getBusinessLogicProjection() + "\"), "
                    + getJSinvokeNoLineEnd("map") + ".getProjectionObject())";
        }

        if (zoom == null) {
            return getJSinvoke("setCenter(" + center.getJSconstructor() + transformation + ")");
        }
        return getJSinvoke(
                "setCenter(" + center.getJSconstructor() + transformation + ", " + zoom.toString() + ")");
    }

    private String getJSSetBusinessLogicProjection() {
        if (businessLogicProjection == null) {
            return getJSinvoke("setBusinessLogicProjection(null)");
        }
        return getJSinvoke("setBusinessLogicProjection('" + businessLogicProjection + "')");
    }

    /**
     * Add a control.
     * 
     * @param control
     *            control to add
     * @return This
     */
    public IOpenLayersMap addControl(IJavascriptComponent control) {
        controls.add(control);
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(control.getJSadd(this));
        }
        return this;
    }

    /**
     * Add an overlay.
     * 
     * @param overlay
     *            overlay to add
     * @return This
     */
    public IOpenLayersMap addOverlay(Overlay overlay) {
        overlays.add(overlay);
        if (overlay instanceof Marker) {
            ((Marker) overlay).setMap(this);
        }
        for (OverlayListenerBehavior behavior : overlay.getBehaviors()) {
            add(behavior);
        }
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJsOverlay(overlay));
        }
        return this;
    }

    /**
     * Add a layer.
     * 
     * @param layer
     *            layer to add
     * @return This
     */
    public IOpenLayersMap addLayer(Layer layer) {
        layers.add(layer);
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(layer.getJSAddLayer(this));
        }
        return this;
    }

    /**
     * Add a feature.
     * 
     * @param feature
     *            feature to add
     * @return This
     */
    public IOpenLayersMap addFeature(Feature feature) {
        features.add(feature);
        feature.setMap(this);
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget
                    .appendJavaScript(feature.getJSAddFeature(this, getFeatureVector(feature.getDisplayInLayer())));
        }
        return this;
    }

    /**
     * Add a feature style.
     * 
     * @param featureStyle
     *            featureStyle to add
     * @return This
     */
    public IOpenLayersMap addFeatureStyle(FeatureStyle featureStyle) {
        featureStyles.add(featureStyle);
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(featureStyle.getJSAddStyle(this));
        }
        return this;
    }

    public IOpenLayersMap removeFeatureStyle(FeatureStyle featureStyle) {
        while (featureStyles.contains(featureStyle)) {
            featureStyles.remove(featureStyle);
        }
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(featureStyle.getJSRemoveStyle(this));
        }
        return this;
    }

    /**
     * Clear all overlays.
     * 
     * @return This
     */
    public IOpenLayersMap clearOverlays() {
        for (Overlay overlay : overlays) {
            for (OverlayListenerBehavior behavior : overlay.getBehaviors()) {
                remove(behavior);
            }
        }
        overlays.clear();
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJSinvoke("clearOverlays()"));
        }
        return this;
    }

    /**
     * Generates the JavaScript used to instantiate this AjaxOpenLayersMap as an JavaScript class on
     * the client side.
     * 
     * @return The generated JavaScript
     */
    protected String getJSinit() {
        StringBuffer js = new StringBuffer();
        String jsMarkersLayerName = getMarkersLayerName() == null ? ", null" : ", '" + getMarkersLayerName() + "'";
        if (options.size() > 0) {
            js.append("\nvar options = {");
            boolean first = true;
            for (String key : options.keySet()) {
                if (first) {
                    first = false;
                } else {
                    js.append(",\n");
                }
                js.append(key + ":" + options.get(key));
            }
            js.append("};\n");
            js.append("new WicketOMap('" + this.getMarkupId() + "', options" + jsMarkersLayerName + ", true);\n");
        } else {
            js.append("new WicketOMap('" + this.getMarkupId() + "', null" + jsMarkersLayerName + ", true);\n");
        }
        for (FeatureStyle featureStyle : featureStyles) {
            js.append(featureStyle.getJSAddStyle(this));
        }
        // This will add all layers which are used in features
        for (Feature feature : features) {
            getFeatureVector(feature.getDisplayInLayer());
        }
        for (Layer layer : getLayers()) {
            js.append(layer.getJSAddLayer(this));
        }
        for (Feature feature : features) {
            js.append(feature.getJSAddFeature(this, getFeatureVector(feature.getDisplayInLayer())));
        }
        js.append(getJSSetCenter());
        if (center == null || zoom == null) {
            js.append(getJSinvoke("zoomToMaxExtent()"));
        }
        for (IJavascriptComponent control : controls) {
            js.append(control.getJSadd(this));
        }
        // Add the overlays.
        for (Overlay overlay : overlays) {
            js.append(getJsOverlay(overlay));
        }
        if (businessLogicProjection != null) {
            js.append(getJSSetBusinessLogicProjection());
        }
        return js.toString();
    }

    /**
     * Convenience method for generating a JavaScript call on this AjaxOpenLayersMap with the given
     * invocation.
     * 
     * @param invocation
     *            The JavaScript call to invoke on this AjaxOpenLayersMap.
     * @return The generated JavaScript.
     */
    public String getJSinvoke(String invocation) {
        return "Wicket.omaps['" + this.getMarkupId() + "']." + invocation + ";\n";
    }

    public String getJSinvokeNoLineEnd(String invocation) {
        return "Wicket.omaps['" + this.getMarkupId() + "']." + invocation;
    }

    private String getJsOverlay(Overlay overlay) {
        String jsToRun = overlay.getJSadd(this) + "\n";
        if (overlay instanceof Marker) {
            Marker marker = (Marker) overlay;
            // if marker has popup and there are no events attached then attach
            // default listener
            if (marker.getPopup() != null && (marker.getEvents() == null || marker.getEvents().length == 0)) {
                // add mousedown listener!
                marker.addEvent(EventType.mousedown);
            }
            // TODO add listeners
            if (marker.getIcon() != null) {
                // prepend icon stuff
                jsToRun = marker.getIcon().getSize().getJSadd() + marker.getIcon().getOffset().getJSadd()
                        + marker.getIcon().getJSadd() + jsToRun;
            }
        }
        return jsToRun;
    }

    /**
     * Remove a control.
     * 
     * @param control
     *            control to remove
     * @return This
     */
    public IOpenLayersMap removeControl(IJavascriptComponent control) {
        controls.remove(control);
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(control.getJSremove(this));
        }
        return this;
    }

    /**
     * Remove an overlay.
     * 
     * @param overlay
     *            overlay to remove
     * @return This
     */
    public IOpenLayersMap removeOverlay(Overlay overlay) {
        while (overlays.contains(overlay)) {
            overlays.remove(overlay);
        }
        for (OverlayListenerBehavior behavior : overlay.getBehaviors()) {
            remove(behavior);
        }
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(overlay.getJSremove(this));
        }
        return this;
    }

    /**
     * Remove an feature.
     * 
     * @param feature
     *            feature to remove
     * @return This
     */
    public IOpenLayersMap removeFeature(Feature feature) {
        while (features.contains(feature)) {
            features.remove(feature);
        }
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(
                    feature.getJSRemoveFeature(this, getFeatureVector(feature.getDisplayInLayer())));
        }
        return this;
    }

    /**
     * Update state from a request to an AJAX target.
     */
    public void update(AjaxRequestTarget target) {
        Request request = RequestCycle.get().getRequest();
        // Attention: don't use setters as this will result in an endless
        // AJAX request loop
        center = LonLat
                .parseWithNames(request.getRequestParameters().getParameterValue("centerConverted").toString());
        zoom = Integer.parseInt(request.getRequestParameters().getParameterValue("zoomConverted").toString());
        bounds = Bounds
                .parseWithNames(request.getRequestParameters().getParameterValue("boundsConverted").toString());
    }

    public List<Layer> getLayers() {
        return layers;
    }

    public List<IJavascriptComponent> getControls() {
        return controls;
    }

    public String getJSInstance() {
        return "Wicket.omaps['" + this.getMarkupId() + "']";
    }

    public List<Overlay> getOverlays() {
        return Collections.unmodifiableList(overlays);
    }

    public void setLayers(List<Layer> layers) {
        this.layers = layers;
    }

    public void setOverlays(List<Overlay> overlays) {
        clearOverlays();
        for (Overlay overlay : overlays) {
            addOverlay(overlay);
        }
    }

    public void setBusinessLogicProjection(String businessLogicProjection) {
        this.businessLogicProjection = businessLogicProjection;
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            ajaxRequestTarget.appendJavaScript(getJSSetBusinessLogicProjection());
        }
    }

    public String getBusinessLogicProjection() {
        return businessLogicProjection;
    }

    public Vector getFeatureVector(String name) {
        Vector vector = featureVectors.get(name);
        if (vector == null) {
            vector = new Vector(name == null ? "Default" : name);
            addLayer(vector);
            featureVectors.put(name, vector);
        }
        return vector;
    }

    public void setMarkersLayerName(String markersLayerName) {
        this.markersLayerName = markersLayerName;
    }

    public String getMarkersLayerName() {
        return markersLayerName;
    }

    public void setVisibleOnlyLayers(String... names) {
        AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
        if (ajaxRequestTarget != null) {
            List<String> invisibleNames = new ArrayList<String>();
            invisibleNames.add(markersLayerName);
            invisibleNames.addAll(featureVectors.keySet());
            StringBuffer visibleLayers = new StringBuffer();
            String layerId = null;
            for (String name : names) {
                layerId = findLayerId(name);
                if (layerId != null) {
                    if (visibleLayers.length() > 0)
                        visibleLayers.append(",");
                    visibleLayers.append(layerId);
                }
                invisibleNames.remove(name);
            }
            StringBuffer invisibleLayers = new StringBuffer();
            for (String name : invisibleNames) {
                layerId = findLayerId(name);
                if (layerId != null) {
                    if (invisibleLayers.length() > 0)
                        invisibleLayers.append(",");
                    invisibleLayers.append(layerId);
                }
            }
            ajaxRequestTarget.appendJavaScript(getJSinvoke("setLayersVisibility([" + visibleLayers.toString()
                    + "], [" + invisibleLayers.toString() + "])"));
        }
    }

    public void toggleLayer(String name) {
        String layerId = findLayerId(name);
        if (layerId != null) {
            AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class);
            if (ajaxRequestTarget != null) {
                ajaxRequestTarget.appendJavaScript(getJSinvoke("toggleLayer(" + layerId + ")"));
            }
        }
    }

    private String findLayerId(String name) {
        Vector vector = featureVectors.get(name);
        if (vector != null)
            return vector.getId();
        return markersLayerName != null && markersLayerName.equals(name) ? "1" : null;
    }
}