/* * Copyright (C) 2014 - 2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo FLOW. * * Akvo FLOW is free software: you can redistribute it and modify it under the terms of * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, * either version 3 of the License or any later version. * * Akvo FLOW 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 Affero General Public License included below for more details. * * The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>. */ package com.gallatinsystems.gis.map; import static com.gallatinsystems.common.util.MemCacheUtils.containsKey; import static com.gallatinsystems.common.util.MemCacheUtils.putObject; import java.util.HashMap; import java.util.Map; import net.sf.jsr107cache.Cache; import org.waterforpeople.mapping.dao.SurveyInstanceDAO; import org.waterforpeople.mapping.domain.SurveyInstance; import com.gallatinsystems.survey.dao.SurveyDAO; import com.gallatinsystems.survey.dao.SurveyUtils; import com.gallatinsystems.survey.domain.Survey; import com.gallatinsystems.survey.domain.SurveyGroup; import com.gallatinsystems.survey.domain.SurveyGroup.PrivacyLevel; import com.gallatinsystems.surveyal.dao.SurveyedLocaleClusterDao; import com.gallatinsystems.surveyal.domain.SurveyedLocale; import com.gallatinsystems.surveyal.domain.SurveyedLocaleCluster; public class MapUtils { // used to multiply latitude and longitude values, to fit them in a long private static final int MULT = 1000000; // used to divide long values by MULT, to go back to double values for // latitude / longitude values private static final double REVMULT = 0.000001; public static final String LATITUDE = "latitude"; public static final String LONGITUDE = "longitude"; public static final String ALTITUDE = "height"; // delta can be +1 or -1, depending on if we want to add a point or delete a point. public static void recomputeCluster(Cache cache, SurveyedLocale locale, int delta) { final SurveyedLocaleClusterDao slcDao = new SurveyedLocaleClusterDao(); final SurveyInstanceDAO siDao = new SurveyInstanceDAO(); final SurveyDAO sDao = new SurveyDAO(); Long latTotal; Long lonTotal; Double latCenter; Double lonCenter; Boolean showOnPublicMap = false; Long surveyId = null; if (locale.getGeocells() == null || locale.getGeocells().size() == 0) { // nothing to do return; } if (locale.getLastSurveyalInstanceId() != null) { SurveyInstance si = siDao.getByKey(locale.getLastSurveyalInstanceId()); if (si != null) { surveyId = si.getSurveyId(); // get public status, first try from cache String pubKey = surveyId.toString() + "-publicStatus"; if (containsKey(cache, pubKey)) { showOnPublicMap = (Boolean) cache.get(pubKey); } else { Survey s = sDao.getByKey(surveyId); if (s != null) { SurveyGroup surveyGroup = SurveyUtils.retrieveSurveyGroup(s .getSurveyGroupId()); if (surveyGroup != null) { showOnPublicMap = surveyGroup.getPrivacyLevel() == PrivacyLevel.PUBLIC; putObject(cache, pubKey, showOnPublicMap); } } } } } for (int i = 1; i <= 4; i++) { String cell = locale.getGeocells().get(i) + "-" + showOnPublicMap.toString(); if (containsKey(cache, cell)) { @SuppressWarnings("unchecked") final Map<String, Long> cellMap = (Map<String, Long>) cache.get(cell); final Long count = cellMap.get("count"); if (count == 1 && delta == -1) { // the cluster needs to be deleted, because the count will // become zero cache.remove(cell); SurveyedLocaleCluster clusterInStore = slcDao.getByKey(cellMap.get("id")); if (clusterInStore != null) { slcDao.delete(clusterInStore); } } else { latTotal = cellMap.get("lat") + Math.round(locale.getLatitude() * MULT * delta); lonTotal = cellMap.get("lon") + Math.round(locale.getLongitude() * MULT * delta); addToCache(cache, cell, cellMap.get("id"), count + delta, latTotal, lonTotal); SurveyedLocaleCluster clusterInStore = slcDao.getByKey(cellMap.get("id")); if (clusterInStore != null) { clusterInStore.setCount(cellMap.get("count").intValue() + delta); clusterInStore.setLatCenter(REVMULT * latTotal / (count + delta)); clusterInStore.setLonCenter(REVMULT * lonTotal / (count + delta)); slcDao.save(clusterInStore); } } } else { // try to get it in the datastore. This can happen when the // cache has expired final SurveyedLocaleCluster clusterInStore = slcDao.getExistingCluster(locale .getGeocells().get(i), showOnPublicMap); if (clusterInStore != null) { if (clusterInStore.getCount() == 1 && delta == -1) { // the cluster needs to be deleted // as we didn't find it in the cache, we only need to delete it in the // datastore slcDao.delete(clusterInStore); } else { final Long count = clusterInStore.getCount().longValue(); latCenter = (clusterInStore.getLatCenter() * count + locale.getLatitude() * delta) / (count + delta); lonCenter = (clusterInStore.getLonCenter() * count + locale.getLongitude() * delta) / (count + delta); addToCache(cache, cell, clusterInStore.getKey().getId(), clusterInStore.getCount() + delta, Math.round(MULT * latCenter * (count + delta)), Math.round(MULT * lonCenter * (count + delta))); clusterInStore.setCount(clusterInStore.getCount() + delta); clusterInStore.setLatCenter(latCenter); clusterInStore.setLonCenter(lonCenter); slcDao.save(clusterInStore); } } else { // create a new one SurveyedLocaleCluster slcNew = new SurveyedLocaleCluster(locale.getLatitude(), locale.getLongitude(), locale.getGeocells().subList(0, i), locale .getGeocells().get(i), i + 1, locale.getKey().getId(), showOnPublicMap, locale.getLastSurveyedDate()); slcDao.save(slcNew); if (cache != null) { addToCache(cache, cell, slcNew.getKey().getId(), 1, Math.round(MULT * locale.getLatitude()), Math.round(MULT * locale.getLongitude())); } } } } } private static void addToCache(Cache cache, String cell, Long id, long count, Long latTotal, Long lonTotal) { final Map<String, Long> v = new HashMap<String, Long>(); v.put("count", count); v.put("id", id); // the cache stores lat/lon values as longs. We store the sums over the // whole cluster. v.put("lat", latTotal); v.put("lon", lonTotal); putObject(cache, cell, v); } }