Java tutorial
/* * * ============================================================================== * 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 org.apache.wicket.Application; import org.apache.wicket.Component; import org.apache.wicket.RuntimeConfigurationType; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.markup.ComponentTag; 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.markup.html.panel.Panel; import org.apache.wicket.request.Request; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.resource.JavaScriptResourceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wicketstuff.openlayers.api.Bounds; import org.wicketstuff.openlayers.api.IJavascriptComponent; import org.wicketstuff.openlayers.api.InfoWindow; import org.wicketstuff.openlayers.api.LonLat; import org.wicketstuff.openlayers.api.SphericalMercatorLonLat; import org.wicketstuff.openlayers.api.Marker; import org.wicketstuff.openlayers.api.Overlay; import org.wicketstuff.openlayers.api.layer.Layer; import org.wicketstuff.openlayers.api.layer.OSM; import org.wicketstuff.openlayers.api.layer.GMap; import org.wicketstuff.openlayers.api.layer.WFS; import org.wicketstuff.openlayers.api.layer.WMS; import org.wicketstuff.openlayers.api.layer.Vector; import org.wicketstuff.openlayers.event.EventType; import org.wicketstuff.openlayers.event.OverlayListenerBehavior; import org.wicketstuff.openlayers.event.PopupListener; import org.wicketstuff.openlayers.js.JSUtils; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; /** * Wicket component to embed <a href="http://www.openlayers.org/">Openlayers Maps</a> into your * pages. */ public class OpenLayersMap extends Panel implements IOpenLayersMap { private static final String OPEN_LAYERS_VERSION = "2.13.1"; private static Logger log = LoggerFactory.getLogger(OpenLayersMap.class); private String businessLogicProjection = null; private abstract class JSMethodBehavior extends Behavior { private static final long serialVersionUID = 1L; private final String attribute; public JSMethodBehavior(final String attribute) { this.attribute = attribute; } protected abstract String getJSinvoke(); /** * @see Behavior#onComponentTag(org.apache.wicket.Component, * org.apache.wicket.markup.ComponentTag) */ @Override public void onComponentTag(Component component, ComponentTag tag) { String invoke = getJSinvoke(); if (attribute.equalsIgnoreCase("href")) { invoke = "javascript:" + invoke; } tag.put(attribute, invoke); } } public class PanDirectionBehavior extends JSMethodBehavior { private static final long serialVersionUID = 1L; private final int dx; private final int dy; public PanDirectionBehavior(String event, final int dx, final int dy) { super(event); this.dx = dx; this.dy = dy; } @Override protected String getJSinvoke() { return getJSpanDirection(dx, dy); } } public class SetCenterBehavior extends JSMethodBehavior { private static final long serialVersionUID = 1L; private final LonLat gLatLng; private final Integer zoom; public SetCenterBehavior(String event, LonLat gLatLng, Integer zoom) { super(event); this.gLatLng = gLatLng; this.zoom = zoom; } @Override protected String getJSinvoke() { return getJSsetCenter(gLatLng, zoom); } } public class SetZoomBehavior extends JSMethodBehavior { private static final long serialVersionUID = 1L; private final Integer zoom; public SetZoomBehavior(final String event, final Integer zoom) { super(event); this.zoom = zoom; } @Override protected String getJSinvoke() { return getJSsetZoom(zoom); } } public class ZoomInBehavior extends JSMethodBehavior { private static final long serialVersionUID = 1L; public ZoomInBehavior(String event) { super(event); } @Override protected String getJSinvoke() { return getJSzoomIn(); } } public class ZoomOutBehavior extends JSMethodBehavior { private static final long serialVersionUID = 1L; public ZoomOutBehavior(String event) { super(event); } @Override protected String getJSinvoke() { return getJSzoomOut(); } } private static final long serialVersionUID = 1L; private Bounds bounds; private PopupListener callbackListener = null; private static final LonLat DEFAULT_CENTER = new SphericalMercatorLonLat(37.4419, -122.1419); private LonLat center = DEFAULT_CENTER; private final List<IJavascriptComponent> controls = new ArrayList<IJavascriptComponent>(); private boolean externalControls = false; private InfoWindow infoWindow; private List<Layer> layers = new ArrayList<Layer>(); private final WebMarkupContainer map; private HashMap<String, String> options = new HashMap<String, String>(); private List<Overlay> overlays = new ArrayList<Overlay>(); private static final int DEFAULT_ZOOM = 13; private int zoom = DEFAULT_ZOOM; // determines if the marker layer will be visible in the // OpenLayers.Control.LayerSwitcher private boolean showMarkersInLayerSwitcher = true; /** * * Constructs a map with a default layer : "OpenLayers WMS", * "http://labs.metacarta.com/wms/vmap0" * * @param id */ public OpenLayersMap(final String id, boolean developmentMode) { this(id, new OpenLayersMapHeaderContributor(developmentMode, OPEN_LAYERS_VERSION), new ArrayList<Overlay>(), new ArrayList<Layer>(), new HashMap<String, String>()); HashMap<String, String> layerOptions = new HashMap<String, String>(); layerOptions.put("layers", JSUtils.getQuotedString("basic")); layers.add(new WMS("OpenLayers WMS", "http://labs.metacarta.com/wms/vmap0", layerOptions)); } public OpenLayersMap(String id) { this(id, false); } /** * Construct. * * @param id */ public OpenLayersMap(final String id, boolean developmentMode, List<Layer> defaultLayers, HashMap<String, String> options) { this(id, new OpenLayersMapHeaderContributor(developmentMode, OPEN_LAYERS_VERSION), new ArrayList<Overlay>(), defaultLayers, options); } public OpenLayersMap(final String id, List<Layer> defaultLayers, HashMap<String, String> options) { this(id, false, defaultLayers, options); } public OpenLayersMap(final String id, boolean developmentMode, List<Layer> defaultLayers, HashMap<String, String> options, List<Overlay> overlays) { this(id, new OpenLayersMapHeaderContributor(developmentMode, OPEN_LAYERS_VERSION), overlays, defaultLayers, options); } public OpenLayersMap(final String id, List<Layer> defaultLayers, HashMap<String, String> options, List<Overlay> overlays) { this(id, false, defaultLayers, options, overlays); } public OpenLayersMap(final String id, List<Layer> defaultLayers, HashMap<String, String> options, List<Overlay> overlays, PopupListener popupListener) { this(id, false, defaultLayers, options, overlays, popupListener); } public OpenLayersMap(final String id, boolean developmentMode, List<Layer> defaultLayers, HashMap<String, String> options, List<Overlay> overlays, PopupListener popupListener) { this(id, new OpenLayersMapHeaderContributor(developmentMode, OPEN_LAYERS_VERSION), overlays, popupListener, defaultLayers, options); } /** * * Popups up the window as default! * * is protected to allow subclasses to override the HeaderContributor that is used. @see * OpenLayersMapHeaderContributor * * @param id * @param headerContrib * @param overlays * */ protected OpenLayersMap(final String id, final OpenLayersMapHeaderContributor headerContrib, List<Overlay> overlays, List<Layer> defaultLayers, HashMap<String, String> options) { this(id, headerContrib, overlays, new PopupListener(false) { private static final long serialVersionUID = 1L; @Override protected void onClick(AjaxRequestTarget target, Overlay overlay) { // make sure that info window is closed if (Marker.class.isInstance(overlay)) { clickAndOpenPopup((Marker) overlay, target); } } }, defaultLayers, options); } /** * Construct. * * @param id * @param headerContrib * @param overlays */ private OpenLayersMap(final String id, final OpenLayersMapHeaderContributor headerContrib, List<Overlay> overlays, PopupListener popupListener, List<Layer> defaultLayers, HashMap<String, String> options) { super(id); popupListener.setOpenLayersMap(this); this.overlays = overlays; layers = defaultLayers; this.options = options; // always add callbacklistener dont know if its gonna be used later on! callbackListener = popupListener; add(callbackListener); add(headerContrib); addHeaderContributorsForLayers(layers); add(new Behavior() { private static final long serialVersionUID = 1L; @Override public void renderHead(Component component, IHeaderResponse response) { response.render(OnDomReadyHeaderItem.forScript(getJSinit())); } }); setInfoWindow(new InfoWindow()); add(getInfoWindow()); map = new WebMarkupContainer("map"); map.setOutputMarkupId(true); add(map); } private void addHeaderContributorsForLayers(List<Layer> layers) { for (Layer layer : layers) { layer.bindHeaderContributors(this); } } /** * Add a control. * * @param control * control to add * @return This */ public OpenLayersMap addControl(IJavascriptComponent control) { controls.add(control); final JavaScriptResourceReference[] jsReferences = control.getJSResourceReferences(); if (jsReferences != null && jsReferences.length > 0) { add(new Behavior() { /** * */ private static final long serialVersionUID = 1L; @Override public void renderHead(Component c, IHeaderResponse response) { for (JavaScriptResourceReference javascriptResourceReference : jsReferences) { response.render(JavaScriptHeaderItem.forReference(javascriptResourceReference)); } } }); } AjaxRequestTarget target = getRequestCycle().find(AjaxRequestTarget.class); if (target != null && findPage() != null) { target.appendJavaScript(control.getJSadd(OpenLayersMap.this)); if (jsReferences != null && jsReferences.length > 0) { for (JavaScriptResourceReference javascriptResourceReference : jsReferences) { target.getHeaderResponse() .render(JavaScriptHeaderItem.forReference(javascriptResourceReference)); } } } return this; } /** * Add an overlay. * * @param overlay * overlay to add * @return This */ public OpenLayersMap addOverlay(Overlay overlay) { overlays.add(overlay); for (OverlayListenerBehavior behavior : overlay.getBehaviors()) { add(behavior); } if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { String jsToRun = getJsOverlay(overlay); ajaxRequestTarget.appendJavaScript(jsToRun); } } return this; } /** * Clear all overlays. * * @return This */ public OpenLayersMap clearOverlays() { for (Overlay overlay : overlays) { for (OverlayListenerBehavior behavior : overlay.getBehaviors()) { remove(behavior); } } overlays.clear(); if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { ajaxRequestTarget.appendJavaScript(getJSinvoke("clearOverlays()")); } } return this; } public Bounds getBounds() { return bounds; } public PopupListener getCallbackListener() { return callbackListener; } public LonLat getCenter() { return center; } public List<IJavascriptComponent> getControls() { return controls; } /** * Generates the JavaScript used to instantiate this OpenlayersMap as an JavaScript class on the * client side. * * @return The generated JavaScript */ protected String getJSinit() { StringBuffer js = new StringBuffer(); 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('" + map.getMarkupId() + "', options, null, " + String.valueOf(showMarkersInLayerSwitcher) + ");\n"); } else { js.append("new WicketOMap('" + map.getMarkupId() + "', null, null, " + String.valueOf(showMarkersInLayerSwitcher) + ");\n"); } for (Layer layer : layers) { if (layer instanceof WMS) { WMS wms = (WMS) layer; js.append("var wms" + wms.getId() + " =" + wms.getJSconstructor() + ";\n"); js.append(getJSinvoke("addLayer(wms" + wms.getId() + "," + wms.getId() + ")")); } if (layer instanceof GMap) { GMap gmap = (GMap) layer; js.append("var gmap" + gmap.getId() + " =" + gmap.getJSconstructor() + ";\n"); js.append(getJSinvoke("addLayer(gmap" + gmap.getId() + "," + gmap.getId() + ")")); } if (layer instanceof OSM) { OSM osm = (OSM) layer; js.append("var osm" + osm.getId() + " =" + osm.getJSconstructor() + ";\n"); js.append(getJSinvoke("addLayer(osm" + osm.getId() + "," + osm.getId() + ")")); } if (layer instanceof WFS) { WFS wfs = (WFS) layer; js.append("var wfs" + wfs.getId() + " =" + wfs.getJSconstructor() + ";\n"); js.append(getJSinvoke("addLayer(wfs" + wfs.getId() + "," + wfs.getId() + ")")); } if (layer instanceof Vector) { Vector vec = (Vector) layer; js.append("var vec" + vec.getId() + " =" + vec.getJSconstructor() + ";\n"); js.append(getJSinvoke("addLayer(vec" + vec.getId() + "," + vec.getId() + ")")); } } /* * If zoom and center are available then use them on the initial map rendering. */ if (zoom != DEFAULT_ZOOM) { if (!center.equals(DEFAULT_CENTER)) js.append(getJSsetCenter(center, zoom)); else js.append(getJSsetZoom(zoom)); } else { js.append(getJSinvoke("zoomToMaxExtent()")); } for (IJavascriptComponent control : controls) { js.append(control.getJSadd(this)); } // Add the overlays. for (Overlay overlay : overlays) { js.append(getJsOverlay(overlay)); } js.append(getJSinvoke("setPopupId('" + getInfoWindow().getContent().getMarkupId() + "')")); if (businessLogicProjection != null) { js.append(getJSSetBusinessLogicProjection()); } return js.toString(); } public String getJSInstance() { return "Wicket.omaps['" + map.getMarkupId() + "']"; } /** * Convenience method for generating a JavaScript call on this Openlayermap with the given * invocation. * * @param invocation * The JavaScript call to invoke on this Openlayermap. * @return The generated JavaScript. */ // TODO Could this become default or protected? public String getJSinvoke(String invocation) { return "Wicket.omaps['" + map.getMarkupId() + "']." + invocation + ";\n"; } public String getJSinvokeNoLineEnd(String invocation) { return "Wicket.omaps['" + map.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); } // add listeners for (EventType evt : marker.getEvents()) { jsToRun += getJSinvoke("addMarkerListener('" + evt.name() + "','" + callbackListener.getCallBackForMarker(marker) + "'," + marker.getOverlayJSVar() + ")"); } if (marker.getIcon() != null) { // prepend icon stuff jsToRun = marker.getIcon().getSize().getJSadd() + marker.getIcon().getOffset().getJSadd() + marker.getIcon().getJSadd() + jsToRun; } } return jsToRun; } private String getJSpanDirection(int dx, int dy) { return getJSinvoke("panDirection(" + dx + "," + dy + ")"); } private String getJSsetCenter(LonLat center, Integer zoom) { if (center != null && zoom != null) return getJSinvoke("setCenter(" + center.getJSconstructor() + ", " + zoom + ")"); else return ""; } private String getJSsetDoubleClickZoomEnabled(boolean enabled) { return getJSinvoke("setDoubleClickZoomEnabled(" + enabled + ")"); } private String getJSsetDraggingEnabled(boolean enabled) { return getJSinvoke("setDraggingEnabled(" + enabled + ")"); } private String getJSsetScrollWheelZoomEnabled(boolean enabled) { return getJSinvoke("setScrollWheelZoomEnabled(" + enabled + ")"); } private String getJSsetZoom(Integer zoom) { return zoom != null ? getJSinvoke("setZoom(" + zoom + ")") : ""; } private String getJSzoomIn() { return getJSinvoke("zoomIn()"); } private String getJSzoomOut() { return getJSinvoke("zoomOut()"); } public List<Layer> getLayers() { return layers; } public List<Overlay> getOverlays() { return Collections.unmodifiableList(overlays); } public Integer getZoom() { return zoom; } public boolean isExternalControls() { return externalControls; } /** * Remove a control. * * @param control * control to remove * @return This */ public OpenLayersMap removeControl(IJavascriptComponent control) { controls.remove(control); if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { ajaxRequestTarget.appendJavaScript(control.getJSremove(OpenLayersMap.this)); } } return this; } /** * Remove an overlay. * * @param overlay * overlay to remove * @return This */ public OpenLayersMap removeOverlay(Overlay overlay) { while (overlays.contains(overlay)) { overlays.remove(overlay); } for (OverlayListenerBehavior behavior : overlay.getBehaviors()) { remove(behavior); } if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { ajaxRequestTarget.appendJavaScript(overlay.getJSremove(OpenLayersMap.this)); } } return this; } /** * Set the center. * * @param center * center to set */ public void setCenter(LonLat center, Integer zoom) { if (!this.center.equals(center)) { this.center = center; this.zoom = zoom; if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { ajaxRequestTarget.appendJavaScript(getJSsetCenter(center, zoom)); } } } } public void setExternalControls(boolean externalControls) { this.externalControls = externalControls; } public void setLayers(List<Layer> layers) { this.layers = layers; } public void setOverlays(List<Overlay> overlays) { clearOverlays(); for (Overlay overlay : overlays) { addOverlay(overlay); } } public void setZoom(Integer level) { if (zoom != level) { zoom = level; if (findPage() != null) { AjaxRequestTarget ajaxRequestTarget = getRequestCycle().find(AjaxRequestTarget.class); if (ajaxRequestTarget != null) { ajaxRequestTarget.appendJavaScript(getJSsetZoom(zoom)); } } } } /** * 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()); getInfoWindow().update(target); } public void setInfoWindow(InfoWindow infoWindow) { this.infoWindow = infoWindow; } public InfoWindow getInfoWindow() { return infoWindow; } /** * @see org.apache.wicket.Component#onRender() */ @Override protected void onRender() { super.onRender(); RuntimeConfigurationType configurationType = Application.get().getConfigurationType(); if (configurationType.equals(RuntimeConfigurationType.DEVELOPMENT) && !Application.get().getMarkupSettings().getStripWicketTags()) { log.warn("Application is in DEVELOPMENT mode && Wicket tags are not stripped," + " Firefox 3.0 will not render the OMap." + " Change to DEPLOYMENT mode || turn on Wicket tags stripping." + " See:" + " http://www.nabble.com/Gmap2-problem-with-Firefox-3.0-to18137475.html."); } } public void setBounds(Bounds bounds) { this.bounds = bounds; } public void setCenter(LonLat center) { setCenter(center, zoom); } /** * @param showMarkersInLayerSwitcher * if true the internal markers layer will be visible in the * OpenLayers.Control.LayerSwitcher * * Default is true. * * Set to false to hide the markers layer from the LayerSwitcher. */ public void setShowMarkersInLayerSwitcher(boolean showMarkersInLayerSwitcher) { this.showMarkersInLayerSwitcher = showMarkersInLayerSwitcher; } 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; } private String getJSSetBusinessLogicProjection() { if (businessLogicProjection == null) { return getJSinvoke("setBusinessLogicProjection(null)"); } return getJSinvoke("setBusinessLogicProjection('" + businessLogicProjection + "')"); } }