eu.limetri.client.mapviewer.layer.weather.data.WeatherDataFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mapviewer-weather Show documentation
Show all versions of mapviewer-weather Show documentation
MapViewer Weather layer project
/**
* Copyright (C) 2008-2012 AgroSense Foundation.
*
* AgroSense is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* There are special exceptions to the terms and conditions of the GPLv3 as it
* is applied to this software, see the FLOSS License Exception
* .
*
* AgroSense is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* AgroSense. If not, see .
*/
package eu.limetri.client.mapviewer.layer.weather.data;
import eu.limetri.client.mapviewer.layer.weather.tilefactory.CurrentWeatherData;
import eu.limetri.client.mapviewer.layer.weather.tilefactory.WeatherFactory;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
/**
*
* @author Frantisek Post
*/
public class WeatherDataFactory {
private static final int POOL_SIZE = 5;
private static final int MISSING_TRESHOLD = 10;
private static final int LOADING_THREAD_SLEEP_INTERVAL = 100;
private ExecutorService executorService;
private WeatherFactory weatherfactory;
private Map> weatherDataObjectForZoomCache;
private Map> dataCoordinateForZoomCache;
private Map currentWeatherDataObjectCache;
private Map currentDataCoordinationCache;
private BlockingQueue loadingQueue;
private final Thread loadingThread;
private int currentZoom = -1;
private static final WeatherDataFactory instance = new WeatherDataFactory();
private WeatherDataFactory() {
weatherDataObjectForZoomCache = new HashMap<>();
dataCoordinateForZoomCache = new HashMap<>();
currentWeatherDataObjectCache = new HashMap<>();
currentDataCoordinationCache = new HashMap<>();
loadingQueue = new LinkedBlockingQueue<>();
weatherfactory = new WeatherFactory();
executorService = Executors.newFixedThreadPool(POOL_SIZE, new ThreadFactory() {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "tile-pool-" + count++);
t.setPriority(Thread.MIN_PRIORITY);
t.setDaemon(true);
return t;
}
});
loadingThread = new Thread(new Runnable() {
int emptyQueueCounter = 0;
@Override
public void run() {
while (true) {
KeyObject head = loadingQueue.poll();
if (head == null) {
try {
emptyQueueCounter++;
if (emptyQueueCounter > 4) {
emptyQueueCounter = 0;
synchronized (loadingThread) {
loadingThread.wait();
}
} else {
Thread.sleep(LOADING_THREAD_SLEEP_INTERVAL);
}
} catch (InterruptedException ex) {
//noop
}
} else {
CurrentWeatherDataObject weatherObjectData = currentWeatherDataObjectCache.get(head);
load(head, weatherObjectData);
}
}
}
});
}
public CurrentWeatherDataObject getCurrentWeatherDataForArea(double startLongitude, double startLatitude, double endLongitude, double endLatitude, int zoom) {
if (this.currentZoom != zoom) {
this.currentZoom = zoom;
currentWeatherDataObjectCache = weatherDataObjectForZoomCache.get(zoom);
if (currentWeatherDataObjectCache == null) {
currentWeatherDataObjectCache = new HashMap<>();
weatherDataObjectForZoomCache.put(zoom, currentWeatherDataObjectCache);
}
currentDataCoordinationCache = dataCoordinateForZoomCache.get(zoom);
if (currentDataCoordinationCache == null) {
currentDataCoordinationCache = new HashMap<>();
dataCoordinateForZoomCache.put(zoom, currentDataCoordinationCache);
}
loadingQueue.clear();
}
if (!loadingThread.isAlive()) {
loadingThread.start();
} else {
synchronized (loadingThread) {
loadingThread.notify();
}
}
List dataFromCache = findInCoordCache(startLongitude, startLatitude, endLongitude, endLatitude, zoom);
if (!dataFromCache.isEmpty()) {
CurrentWeatherDataObject weatherDataObject = new CurrentWeatherDataObject();
weatherDataObject.setData(dataFromCache);
weatherDataObject.setLoaded();
return weatherDataObject;
}
KeyObject keyObject = new KeyObject(zoom, startLongitude, startLatitude, endLongitude, endLatitude);
CurrentWeatherDataObject weatherDataObject = currentWeatherDataObjectCache.get(keyObject);
if (weatherDataObject != null && weatherDataObject.isOutdated()) {
currentWeatherDataObjectCache.remove(keyObject);
weatherDataObject = null;
}
if (weatherDataObject == null) {
weatherDataObject = new CurrentWeatherDataObject();
currentWeatherDataObjectCache.put(keyObject, weatherDataObject);
startLoading(keyObject);
} else {
weatherDataObject.setLoaded();
}
return weatherDataObject;
}
private void startLoading(KeyObject keyObject) {
if (!loadingQueue.contains(keyObject)) {
loadingQueue.add(keyObject);
if (loadingQueue.size() > 2) {
loadingQueue.poll();
}
}
}
private void load(final KeyObject head, final CurrentWeatherDataObject weatherDataObject) {
executorService.execute(new Runnable() {
@Override
public void run() {
List data = weatherfactory.getCurrentWeatherDataForArea(head.getStartLongitude(), head.getStartLatitude(), head.getEndLongitude(), head.getEndLatitude(), head.getZoom());
if (data != null && !data.isEmpty()) {
for (CurrentWeatherData weatherData : data) {
double latitude = weatherData.getLatitude();
double longitude = weatherData.getLongitude();
Point2D coordPoint = new Point.Double(latitude, longitude);
currentDataCoordinationCache.put(coordPoint, weatherData);
}
} else {
double centerLongitude = head.getStartLongitude() + (head.getEndLongitude() - head.getStartLongitude()) / 2;
double centerLatitude = head.getStartLatitude() + (head.getEndLatitude() - head.getStartLatitude()) / 2;
data = weatherfactory.readCitiesData(centerLatitude, centerLongitude);
}
weatherDataObject.setData(data);
}
});
}
private List findInCoordCache(double startLongitude, double startLatitude, double endLongitude, double endLatitude, int zoom) {
Rectangle2D rectangle = new Rectangle.Double(Math.min(startLongitude, endLongitude), Math.min(startLatitude, endLatitude), Math.abs(startLongitude - endLongitude), Math.abs(startLatitude - endLatitude));
List foundInCache = new ArrayList<>();
double minLongitude = Double.MAX_VALUE;
double maxLongitude = Double.MIN_VALUE;
double minLatitude = Double.MAX_VALUE;
double maxLatitude = Double.MIN_VALUE;
List outDatedCoords = new ArrayList<>();
for (Point2D coord : currentDataCoordinationCache.keySet()) {
CurrentWeatherData dataObject = currentDataCoordinationCache.get(coord);
if (!dataObject.isOutdated()) {
if (rectangle.contains(coord)) {
foundInCache.add(dataObject);
if (coord.getX() > maxLatitude) {
maxLatitude = coord.getX();
}
if (coord.getX() < minLatitude) {
minLatitude = coord.getX();
}
if (coord.getY() > maxLongitude) {
maxLongitude = coord.getY();
}
if (coord.getY() < minLongitude) {
minLongitude = coord.getY();
}
}
} else {
outDatedCoords.add(coord);
}
}
for (Point2D coord : outDatedCoords) {
currentDataCoordinationCache.remove(coord);
}
if (!foundInCache.isEmpty()) {
Rectangle2D coveredRectangle = new Rectangle.Double(minLatitude, minLongitude, maxLatitude - minLatitude, maxLongitude - minLongitude);
double bottom = 100 * (coveredRectangle.getX() - rectangle.getX()) / rectangle.getWidth();
double left = 100 * (coveredRectangle.getY() - rectangle.getY()) / rectangle.getHeight();
double top = 100 * ((rectangle.getWidth() - ((coveredRectangle.getX() - rectangle.getX()) + coveredRectangle.getWidth()))) / rectangle.getWidth();
double right = 100 * ((rectangle.getHeight() - ((coveredRectangle.getY() - rectangle.getY()) + coveredRectangle.getHeight()))) / rectangle.getHeight();
if (right > MISSING_TRESHOLD) {
Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX(), coveredRectangle.getY() + coveredRectangle.getHeight(), rectangle.getWidth(), (rectangle.getY() + rectangle.getHeight()) - (coveredRectangle.getY() + coveredRectangle.getHeight()));
addToCache(loadRectangle, zoom);
}
if (left > MISSING_TRESHOLD) {
Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX(), rectangle.getY(), rectangle.getWidth(), coveredRectangle.getY() - rectangle.getY());
addToCache(loadRectangle, zoom);
}
if (bottom > MISSING_TRESHOLD) {
// Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX(), rectangle.getY(), coveredRectangle.getX() - rectangle.getX(), rectangle.getHeight());
Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX(), coveredRectangle.getY(), coveredRectangle.getX() - rectangle.getX(), coveredRectangle.getHeight());
addToCache(loadRectangle, zoom);
}
if (top > MISSING_TRESHOLD) {
// Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX() + coveredRectangle.getWidth(), rectangle.getY(), rectangle.getWidth() - (coveredRectangle.getWidth() + (coveredRectangle.getY() - rectangle.getY())), rectangle.getHeight());
Rectangle2D loadRectangle = new Rectangle.Double(rectangle.getX() + coveredRectangle.getWidth(), coveredRectangle.getY(), rectangle.getWidth() - (coveredRectangle.getWidth() + (coveredRectangle.getY() - rectangle.getY())), coveredRectangle.getHeight());
addToCache(loadRectangle, zoom);
}
}
return foundInCache;
}
private void addToCache(Rectangle2D loadRectangle, int zoom) {
KeyObject keyObject = new KeyObject(zoom, loadRectangle.getX(), loadRectangle.getY(), loadRectangle.getX() + loadRectangle.getWidth(), loadRectangle.getY() + loadRectangle.getHeight());
CurrentWeatherDataObject weatherDataObject = new CurrentWeatherDataObject();
currentWeatherDataObjectCache.put(keyObject, weatherDataObject);
startLoading(keyObject);
}
public static WeatherDataFactory getInstance() {
return instance;
}
}