001/** 002 * Copyright 2018 Tampere University of Technology, Pori Department 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package service.tut.pori.apilta.shock; 017 018import java.util.ArrayList; 019import java.util.Date; 020import java.util.HashSet; 021import java.util.Iterator; 022import java.util.List; 023 024import org.apache.log4j.Logger; 025 026import core.tut.pori.context.ServiceInitializer; 027import core.tut.pori.http.parameters.DataGroups; 028import core.tut.pori.http.parameters.DateIntervalParameter; 029import core.tut.pori.http.parameters.Limits; 030import core.tut.pori.users.UserIdentity; 031import service.tut.pori.apilta.ApiltaProperties; 032import service.tut.pori.apilta.shock.GroupCalculator.GroupMethod; 033import service.tut.pori.apilta.shock.datatypes.LocationData; 034import service.tut.pori.apilta.shock.datatypes.LocationLimits; 035import service.tut.pori.apilta.shock.datatypes.ShockHighlight; 036import service.tut.pori.apilta.shock.datatypes.ShockHighlightList; 037import service.tut.pori.apilta.shock.datatypes.ShockMeasurement; 038import service.tut.pori.apilta.shock.datatypes.ShockMeasurementList; 039import service.tut.pori.apilta.utils.MathUtils; 040 041/** 042 * 043 * 044 */ 045public final class ShockCore { 046 private static final DataGroups DATAGROUP_LOCATION_DATA = new DataGroups(Definitions.DATA_GROUP_LOCATION_DATA); 047 private static final Logger LOGGER = Logger.getLogger(ShockCore.class); 048 049 /** 050 * 051 */ 052 private ShockCore() { 053 // nothing needed 054 } 055 056 /** 057 * 058 * @param userIdentity 059 * @param list 060 * @throws IllegalArgumentException on invalid data 061 */ 062 public static void createMeasurement(UserIdentity userIdentity, ShockMeasurementList list) throws IllegalArgumentException { 063 if(!ShockMeasurementList.isValid(list)){ 064 throw new IllegalArgumentException("Empty or invalid measurement list provided."); 065 } 066 067 ShockDAO dao = ServiceInitializer.getDAOHandler().getDAO(ShockDAO.class); 068 for(ShockMeasurement measurement : list.getShockMeasurements()){ 069 measurement.setUserId(userIdentity); // set the authenticated user as the owner for all measurements 070 071 if(measurement.getVisibility() == null){ 072 LOGGER.debug("Empty visibility for measurement provided by user, id: "+userIdentity.getUserId()+", defaulting to: "+Definitions.DEFAULT_VISIBILITY); 073 measurement.setVisibility(Definitions.DEFAULT_VISIBILITY); 074 } 075 076 dao.createMeasurement(measurement); 077 } 078 } 079 080 /** 081 * 082 * @param userIdentity 083 * @param locationLimits 084 * @param dataGroups 085 * @param dateInterval timestamp interval 086 * @param levelFilter 087 * @param limits 088 * @param groupMethod 089 * @param groupRange in meters 090 * @param userIdFilter 091 * @return measurement list or null if nothing was found 092 */ 093 public static ShockMeasurementList getMeasurements(UserIdentity userIdentity, LocationLimits locationLimits, DataGroups dataGroups, DateIntervalParameter dateInterval, int[] levelFilter, Limits limits, GroupMethod groupMethod, Integer groupRange, long[] userIdFilter) { 094 ShockMeasurementList measurements = ServiceInitializer.getDAOHandler().getDAO(ShockDAO.class).getMeasurements(userIdentity, locationLimits, dataGroups, dateInterval, levelFilter, limits, userIdFilter); 095 if(!ShockMeasurementList.isEmpty(measurements) && groupMethod != null) { 096 GroupCalculator gc = new GroupCalculator(groupMethod, groupRange); 097 gc.setLevelFilter(levelFilter); 098 List<ShockMeasurement> list = gc.group(measurements.getShockMeasurements()); 099 if(list == null) { 100 return null; 101 } 102 measurements.setShockMeasurements(list); 103 } 104 return measurements; 105 } 106 107 /** 108 * 109 * @param userIdentity 110 * @param minMeasurements the highlight group's minimum number of measurements 111 * @param range maximum range (in km) around a central point when calculating groups 112 * @param locationLimits 113 * @param dateInterval timestamp interval 114 * @param levelFilter 115 * @param limits 116 * @param userIdFilter 117 * @return highlight list of null if nothing was found 118 */ 119 public static ShockHighlightList getHighlights(UserIdentity userIdentity, int minMeasurements, double range, LocationLimits locationLimits, DateIntervalParameter dateInterval, int[] levelFilter, Limits limits, long[] userIdFilter) { 120 ShockMeasurementList list = getMeasurements(userIdentity, locationLimits, DATAGROUP_LOCATION_DATA, dateInterval, levelFilter, limits, null, null, userIdFilter); 121 if(ShockMeasurementList.isEmpty(list)) { 122 LOGGER.debug("No measurements found with the given values."); 123 return null; 124 } 125 126 ArrayList<ShockHighlight> highlights = new ArrayList<>(); 127 HashSet<Long> userIds = new HashSet<>(); 128 long minTimeDifference = ServiceInitializer.getPropertyHandler().getSystemProperties(ApiltaProperties.class).getShockGroupTimeDifference(); 129 List<ShockMeasurement> measurements = list.getShockMeasurements(); 130 int size = 0; 131 while((size = measurements.size()) > minMeasurements) { 132 ShockMeasurement center = measurements.remove(size-1); 133 Date from = center.getTimestamp(); 134 Date to = from; 135 long centerTimestamp = center.getTimestamp().getTime(); 136 int minLevel = center.getLevel(); 137 int maxLevel = minLevel; 138 double maxRange = 0; 139 int measurementCount = 0; 140 userIds.clear(); 141 Long userId = center.getUserId().getUserId(); 142 userIds.add(userId); 143 LocationData location = center.getLocationData(); 144 double lat = location.getLatitude(); 145 double lon = location.getLongitude(); 146 147 for(Iterator<ShockMeasurement> iter = measurements.iterator(); iter.hasNext();){ 148 ShockMeasurement m = iter.next(); 149 Date mt = m.getTimestamp(); 150 UserIdentity mUserId = m.getUserId(); 151 if(UserIdentity.equals(mUserId, userId) && Math.abs(mt.getTime()-centerTimestamp) < minTimeDifference){ // if the measurements are from the same user, check that the measurements are from different sessions 152 continue; 153 } 154 location = m.getLocationData(); 155 double tRange = MathUtils.haversine(lat, lon, location.getLatitude(), location.getLongitude()); 156 if(tRange < range){ 157 userIds.add(mUserId.getUserId()); 158 ++measurementCount; 159 if(tRange > maxRange){ 160 maxRange = tRange; 161 } 162 int level = m.getLevel(); 163 if(level < minLevel){ 164 minLevel = level; 165 }else if(level > maxLevel){ 166 maxLevel = level; 167 } 168 169 if(mt.before(from)) { 170 from = mt; 171 }else if(mt.after(to)) { 172 to = mt; 173 } 174 iter.remove(); 175 } // if 176 } // for 177 178 if(measurementCount > minMeasurements) { 179 ShockHighlight hl = new ShockHighlight(); 180 hl.setLatitude(lat); // naively use the center point even though it may not be the exact center 181 hl.setLongitude(lon); // naively use the center point even though it may not be the exact center 182 hl.setFrom(from); 183 hl.setTo(to); 184 hl.setMaxLevel(maxLevel); 185 hl.setMinLevel(minLevel); 186 hl.setUserCount(userIds.size()); 187 hl.setMaxRange(maxRange); 188 hl.setMeasurementCount(measurementCount); 189 highlights.add(hl); 190 } 191 } 192 193 return ShockHighlightList.getShockMeasurementList(highlights); 194 } 195}