package com.nutiteq.kml; import java.util.Vector; import javax.microedition.lcdui.Image; import com.nutiteq.BasicMapComponent; import com.nutiteq.cache.Cache; import com.nutiteq.cache.ImageWaiter; import com.nutiteq.components.KmlPlace; import com.nutiteq.components.MapPos; import com.nutiteq.components.Place; import com.nutiteq.components.PlaceInfo; import com.nutiteq.components.WgsBoundingBox; import com.nutiteq.io.ResourceRequestor; import com.nutiteq.net.DownloadRequestor; import com.nutiteq.task.TasksRunner; import com.nutiteq.utils.Utils; /** * @author Jaak Laineste * */ public class KmlServicesHandler implements DownloadRequestor, ImageWaiter, KmlElementsWaiter { private WgsBoundingBox lastUpdateBBox; private int lastUpdateZoom; private KmlService[] services = new KmlService[0]; private Vector[] placesForService = new Vector[0]; private boolean[] needsUpdate = new boolean[0]; private final KmlStylesCache stylesCache = new KmlStylesCache(); private final BasicMapComponent mapComponent; private final TasksRunner tasksRunner; private boolean zoomedOut; public KmlServicesHandler(final BasicMapComponent mapComponent, final TasksRunner taskRunner) { this.mapComponent = mapComponent; tasksRunner = taskRunner; } public KmlService[] getServices() { return services; } public void removeService(final KmlService service) { // TODO jaanus : optimize boolean found = false; for (int i = 0; i < services.length; i++) { found = services[i] == service; if (found) { break; } } if (!found) { return; } final KmlService[] result = new KmlService[services.length - 1]; final Vector[] kmlPlaces = new Vector[placesForService.length - 1]; final boolean[] update = new boolean[needsUpdate.length - 1]; int count = 0; Place[] removedPlaces = null; for (int i = 0; i < services.length; i++) { if (services[i] == service) { final Vector removedKmlPlaces = placesForService[i]; removedPlaces = new Place[removedKmlPlaces.size()]; for (int j = 0; j < removedKmlPlaces.size(); j++) { removedPlaces[j] = ((KmlPlace) removedKmlPlaces.elementAt(j)).getPlace(); } continue; } result[count] = services[i]; kmlPlaces[count] = placesForService[i]; update[count] = needsUpdate[i]; count++; } // TODO jaanus : null check to map component if (removedPlaces != null) { mapComponent.removePlaces(removedPlaces); } services = result; placesForService = kmlPlaces; needsUpdate = update; } public void addService(final KmlService service) { // TODO jaanus : handle duplicates services = appendObject(services, service); placesForService = resizePlacesArrayByOne(placesForService); needsUpdate = addOneBooleanToArray(needsUpdate, false); } private boolean[] addOneBooleanToArray(final boolean[] source, final boolean valueForAdded) { final boolean[] result = new boolean[source.length + 1]; System.arraycopy(source, 0, result, 0, source.length); result[result.length - 1] = valueForAdded; return result; } private Vector[] resizePlacesArrayByOne(final Vector[] original) { final Vector[] result = new Vector[original.length + 1]; System.arraycopy(original, 0, result, 0, original.length); result[result.length - 1] = new Vector(); return result; } private KmlService[] appendObject(final KmlService[] array, final KmlService added) { if (array.length == 0) { return new KmlService[] { added }; } final KmlService[] result = new KmlService[array.length + 1]; System.arraycopy(array, 0, result, 0, array.length); result[result.length - 1] = added; return result; } // TODO jaanus: !!!!! public void mapMoved(final WgsBoundingBox boundingBox, final int zoom) { for (int i = 0; i < services.length; i++) { if (needsUpdate[i]) { continue; } if (services[i].needsUpdate(boundingBox, zoom)) { needsUpdate[i] = true; } } zoomedOut = lastUpdateZoom - zoom < 0; if (someServiceNeedsUpdate(needsUpdate)) { lastUpdateBBox = boundingBox; lastUpdateZoom = zoom; tasksRunner.enqueueDownloadRequestor(this, Cache.CACHE_LEVEL_NONE); } } private boolean someServiceNeedsUpdate(final boolean[] serviceNeedsUpdate) { for (int i = 0; i < serviceNeedsUpdate.length; i++) { if (serviceNeedsUpdate[i]) { return true; } } return false; } public ResourceRequestor getDownloadable() { if (!someServiceNeedsUpdate(needsUpdate)) { return null; } for (int i = 0; i < needsUpdate.length; i++) { if (!needsUpdate[i]) { continue; } final KmlService service = services[i]; needsUpdate[i] = false; return new KmlReader(this, service, service.getServiceUrl(lastUpdateBBox, lastUpdateZoom), stylesCache, tasksRunner, service.getDefaultIcon()); } return null; } public void addKmlPlaces(final KmlService service, final KmlPlace[] addedKmlPlaces) { for (int j = 0; j < services.length; j++) { if (service != services[j]) { continue; } final Vector kmlPlaces = placesForService[j]; final Vector addedPlaces = new Vector(); final Vector removedPlaces = new Vector(); if (zoomedOut) { // remove old places for service appendPlacesElements(removedPlaces, kmlPlaces); kmlPlaces.setSize(0); } for (int i = 0; i < addedKmlPlaces.length; i++) { if (kmlPlaces.contains(addedKmlPlaces[i])) { continue; } final Place tmpPlace = addedKmlPlaces[i].getPlace(); removedPlaces.removeElement(tmpPlace); kmlPlaces.addElement(addedKmlPlaces[i]); addedPlaces.addElement(tmpPlace); } final int maxElements = service.maxResults() * 2; if (kmlPlaces.size() > maxElements) { final MapPos middlePoint = mapComponent.getInternalMiddlePoint(); final int[] distances = new int[kmlPlaces.size()]; final int[] indexes = new int[distances.length]; for (int index = 0; index < indexes.length; index++) { indexes[index] = index; final MapPos placePos = ((KmlPlace) kmlPlaces.elementAt(index)).getPlace() .getMapPosition(); // if new place from server, place location on map has not been // calculated distances[index] = placePos == null ? 0 : middlePoint.distanceInPixels(placePos); } Utils.doubleBubbleSort(distances, indexes); final Vector removedElements = new Vector(); for (int k = distances.length - 1; k > maxElements; k--) { final KmlPlace p = (KmlPlace) kmlPlaces.elementAt(indexes[k]); removedElements.addElement(p); removedPlaces.addElement(p.getPlace()); } for (int k = 0; k < removedElements.size(); k++) { kmlPlaces.removeElement(removedElements.elementAt(k)); } } if (addedPlaces.size() > 0) { final Place[] added = new Place[addedPlaces.size()]; addedPlaces.copyInto(added); mapComponent.addPlaces(added, removedPlaces.size() == 0); } if (removedPlaces.size() > 0) { final Place[] removed = new Place[removedPlaces.size()]; removedPlaces.copyInto(removed); mapComponent.removePlaces(removed); } } } private void appendPlacesElements(final Vector places, final Vector kmlPlaces) { for (int k = 0; k < kmlPlaces.size(); k++) { places.addElement(((KmlPlace) kmlPlaces.elementAt(k)).getPlace()); } } public void imageDownloaded(final String url, final Image image) { boolean updated = false; for (int j = 0; j < services.length; j++) { final Vector kmlPlaces = placesForService[j]; for (int i = 0; i < kmlPlaces.size(); i++) { final KmlPlace kPlace = (KmlPlace) kmlPlaces.elementAt(i); if (kPlace.usesIcon(url, stylesCache)) { updated = true; final Place place = kPlace.getPlace(); //TODO jaanus : fix this place icon hack place.setIcon(image); } } } if (updated) { // TODO jaanus : fix this repaint hack mapComponent.addPlaces(new Place[0]); } } public PlaceInfo getAdditionalInfo(final Place place) { for (int i = 0; i < services.length; i++) { final int placeIndex = placesForService[i].indexOf(place); if (placeIndex >= 0) { return ((KmlPlace) placesForService[i].elementAt(placeIndex)).getInfoObject(); } } return null; } /** * get all places from a KML Service * @param service * @return Array of KmlPlace[] */ public KmlPlace[] getKmlPlaces(final KmlService service) { if(service == null){ return null; } Vector out = new Vector(); for (int i = 0; i < services.length; i++) { if (service.equals(services[i])) { for (int p = 0; p < placesForService[i].size(); p++) { out.addElement((KmlPlace) placesForService[i].elementAt(p)); } } } KmlPlace[] retval = new KmlPlace[out.size()]; out.copyInto(retval); return retval; } public KmlPlace[] getKmlPlaces() { Vector out = new Vector(); for (int i = 0; i < services.length; i++) { for (int p = 0; p < placesForService[i].size(); p++) { out.addElement((KmlPlace) placesForService[i].elementAt(p)); } } KmlPlace[] retval = new KmlPlace[out.size()]; out.copyInto(retval); return retval; } }