/* * Copyright (c) 2010, 2011 Mashery, Inc. All Rights Reserved. * * 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 com.mashery.examples.api.client.weatherbug; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style.BorderStyle; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.Style.Visibility; import com.google.gwt.maps.client.HasMapCanvasProjection; import com.google.gwt.maps.client.base.HasLatLngBounds; import com.google.gwt.maps.client.base.HasPoint; import com.google.gwt.maps.client.base.LatLng; import com.google.gwt.maps.client.base.Point; import com.google.gwt.maps.client.event.Event; import com.google.gwt.maps.client.event.EventCallback; import com.google.gwt.maps.client.event.HasMapsEventListener; import com.google.gwt.maps.client.overlay.HasProjection; import com.google.gwt.maps.client.overlay.OverlayView; import com.google.gwt.user.client.ui.UIObject; public class WeatherBugOverlayView extends OverlayView { private static final int TILE_CACHE_SIZE = 64; private static final String WEATHERBUG_ENDPOINT = "http://i.wxbug.net/GEO/Google/${layerType}/GetTile_v2.aspx?as=0&c=0&fq=0&mw=0&ds=0&stl=1&api_key=cn63prcp8pmrzmu7exhg9pgd"; private static final LayerType DEFAULT_LAYER_TYPE = LayerType.TEMPERATURE; private final LinkedHashMap<TileKey, ImageElement> tiles = new LinkedHashMap<TileKey, ImageElement>(); private LayerType layerType = DEFAULT_LAYER_TYPE; private DivElement div; private HasMapsEventListener boundsChangedListener, zoomChangedListener; public LayerType getLayerType() { return layerType; } public void setLayerType(LayerType layerType) { if (layerType == null) layerType = DEFAULT_LAYER_TYPE; this.layerType = layerType; for (ImageElement img : tiles.values()) img.removeFromParent(); tiles.clear(); if (div != null) draw(); } @Override public void onAdd() { Document doc = Document.get(); DivElement div = doc.createDivElement(); div.getStyle().setBorderStyle(BorderStyle.NONE); div.getStyle().setBorderWidth(0d, Unit.PX); div.getStyle().setPosition(Position.ABSOLUTE); div.getStyle().setOpacity(0.4d); this.div = div; getPanes().getOverlayLayer().appendChild(div); boundsChangedListener = Event.addListener(getMap(), "bounds_changed", new EventCallback() { @Override public void callback() { WeatherBugOverlayView.this.draw(); } }); zoomChangedListener = Event.addListener(getMap(), "zoom_changed", new EventCallback() { @Override public void callback() { for (Node child = WeatherBugOverlayView.this.div.getFirstChild(); child != null;) { Node node = child; child = child.getNextSibling(); node.removeFromParent(); } tiles.clear(); } }); } @Override public void draw() { HasLatLngBounds bounds = getMap().getBounds(); HasMapCanvasProjection ovrPrj = getProjection(); HasPoint ovrNE = ovrPrj.fromLatLngToDivPixel(bounds.getNorthEast()); HasPoint ovrSW = ovrPrj.fromLatLngToDivPixel(bounds.getSouthWest()); div.getStyle().setLeft(ovrSW.getX(), Unit.PX); div.getStyle().setTop(ovrNE.getY(), Unit.PX); div.getStyle().setWidth(ovrNE.getX() - ovrSW.getX(), Unit.PX); div.getStyle().setHeight(ovrSW.getY() - ovrNE.getY(), Unit.PX); String endpoint = WEATHERBUG_ENDPOINT.replace("${layerType}", layerType.getPath()); HashSet<TileKey> usedTiles = new HashSet<TileKey>(); int zoom = getMap().getZoom(); double scale = Math.pow(2d, zoom); HasProjection prj = getMap().getProjection(); LatLng nwLatLng = new LatLng(bounds.getNorthEast().getLatitude(), bounds.getSouthWest().getLongitude()); HasPoint nwRaw = prj.fromLatLngToPoint(nwLatLng); LatLng seLatLng = new LatLng(bounds.getSouthWest().getLatitude(), bounds.getNorthEast().getLongitude()); HasPoint seRaw = prj.fromLatLngToPoint(seLatLng); Point nw = new Point(Math.floor(nwRaw.getX() * scale), Math.floor(nwRaw.getY() * scale)); Point se = new Point(Math.floor(seRaw.getX() * scale), Math.floor(seRaw.getY() * scale)); double offsetX = nw.getX() % 256d; double offsetY = nw.getY() % 256d; Point startPoint = new Point(nw.getX() - offsetX, nw.getY() - offsetY); while (startPoint.getY() < se.getY()) { Point p = new Point(startPoint.getX(), startPoint.getY()); startPoint = new Point(startPoint.getX(), startPoint.getY() + 256); while (p.getX() < se.getX()) { int tx = (int) p.getX() / 256; int ty = (int) p.getY() / 256; TileKey tileKey = new TileKey(tx, ty, zoom); usedTiles.add(tileKey); ImageElement img = tiles.remove(tileKey); if (img == null) { img = Document.get().createImageElement(); img.getStyle().setBorderStyle(BorderStyle.NONE); img.getStyle().setBorderWidth(0d, Unit.PX); img.getStyle().setPosition(Position.ABSOLUTE); StringBuilder buf = new StringBuilder(endpoint); buf.append("&tx=").append(tx); buf.append("&ty=").append(ty); buf.append("&zm=").append(zoom); img.setSrc(buf.toString()); div.appendChild(img); } else { img.getStyle().setVisibility(Visibility.VISIBLE); } img.getStyle().setLeft(p.getX() - nw.getX(), Unit.PX); img.getStyle().setTop(p.getY() - nw.getY(), Unit.PX); img.getStyle().setWidth(256d, Unit.PX); img.getStyle().setHeight(256d, Unit.PX); tiles.put(tileKey, img); p = new Point(p.getX() + 256, p.getY()); } } for (Iterator<Map.Entry<TileKey, ImageElement>> i = tiles.entrySet().iterator(); i.hasNext();) { Map.Entry<TileKey, ImageElement> entry = i.next(); if (!usedTiles.remove(entry.getKey())) { if (tiles.size() > TILE_CACHE_SIZE) { i.remove(); entry.getValue().removeFromParent(); } else { entry.getValue().getStyle().setVisibility(Visibility.HIDDEN); } } } } @Override public void onRemove() { Event.removeListener(boundsChangedListener); Event.removeListener(zoomChangedListener); div.removeFromParent(); div = null; tiles.clear(); } public void show() { UIObject.setVisible(div, true); } public void hide() { UIObject.setVisible(div, false); } private static class TileKey { private final int tx; private final int ty; private final int zoom; private final int hashCode; public TileKey(int tx, int ty, int zoom) { this.tx = tx; this.ty = ty; this.zoom = zoom; int hashCode = 17; hashCode = 37 * hashCode + tx; hashCode = 37 * hashCode + ty; hashCode = 37 * hashCode + zoom; this.hashCode = hashCode; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || obj.getClass() != getClass()) return false; TileKey o = (TileKey) obj; return tx == o.tx && ty == o.ty && zoom == o.zoom; } @Override public int hashCode() { return hashCode; } } }