getObjectFromSql('select * from station where id = ?', [$id]); else $cache[$key] = $this->getObjectFromSql('select * from station where id = ? AND source_id = ?', [$id, $source_id]); } return $cache[$key]; } /** * Get object by name * * @param string $name * @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query) * @return Station */ public function getObjectByName($name, $source_id = null) { if (!is_string($name) || (!is_null($source_id) && !isInt($source_id))) { return new Station(0); } static $cache = array(); $key = $name; if (!isset($cache[$key])) { if (is_null($source_id)) $cache[$key] = $this->getObjectFromSql('select * from station where name = ? order by latest_location_packet_timestamp desc nulls last limit 1', [$name]); else $cache[$key] = $this->getObjectFromSql('select * from station where name = ? AND source_id = ? order by latest_location_packet_timestamp desc nulls last limit 1', [$name, $source_id]); } return $cache[$key]; } /** * Get list of objects by name (call) * * @param string $name * @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query) * @return Station */ public function getObjectListByName($name, $station_type_id = null, $source_id = null, $min_timestamp = 0) { if (!is_string($name) || (!is_null($source_id) && !isInt($source_id)) || (!is_null($station_type_id) && !isInt($station_type_id)) || !isInt($station_type_id) || !isInt($min_timestamp)) { return new Station(0); } static $cache = array(); $key = $name; if (!isset($cache[$key])) { if (isInt($station_type_id) && isInt($source_id)) { $cache[$key] = $this->getObjectListFromSql('select * from station where station_type_id = ? and name = ? and (source_id = ? or source_id is null) and latest_confirmed_packet_timestamp > ?', [$station_type_id, $name, $source_id, $min_timestamp]); } else if (isInt($station_type_id)) { $cache[$key] = $this->getObjectListFromSql('select * from station where station_type_id = ? and name = ? and latest_confirmed_packet_timestamp > ?', [$station_type_id, $name, $min_timestamp]); } else { $cache[$key] = $this->getObjectListFromSql('select * from station where name = ? and latest_confirmed_packet_timestamp > ?', [$name, $min_timestamp]); } } return $cache[$key]; } /** * Get sender station object by sender id * * @param string $name * @param int $senderId * @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query) * @return Station */ public function getSenderStationObjectBySenderId($senderId, $source_id = null) { if (!isInt($senderId) || (!is_null($source_id) && !isInt($source_id))) { return new Station(0); } static $cache = array(); $key = $senderId; if (!isset($cache[$key])) { if (is_null($source_id)) $cache[$key] = $this->getObjectFromSql('select station.* from station, sender where station.latest_sender_id = sender.id and sender.name = station.name and sender.id = ? and station.station_type_id = 1', [$senderId]); else $cache[$key] = $this->getObjectFromSql('select station.* from station, sender where station.source_id = ? station.latest_sender_id = sender.id and sender.name = station.name and sender.id = ? and station.station_type_id = 1', [$source_id, $senderId]); } return $cache[$key]; } /** * Get object by name and sender id * * @param string $name * @param int $senderId * @param int $source_id (optional, 1 = APRS, 2 = CWOP, 3 = CBAPRS, 5 = OGN) used by query planner to optimize the query) * @return Station */ public function getObjectByNameAndSenderId($name, $senderId, $source_id = null) { if (!is_string($name) || !isInt($senderId) || (!is_null($source_id) && !isInt($source_id))) { return new Station(0); } static $cache = array(); $key = $name . ';' . $senderId; if (!isset($cache[$key])) { if (is_null($source_id)) $cache[$key] = $this->getObjectFromSql('select * from station where name = ? and latest_sender_id = ?', [$name, $senderId]); else $cache[$key] = $this->getObjectFromSql('select * from station where name = ? and source_id = ? and latest_sender_id = ?', [$name, $source_id, $senderId]); } return $cache[$key]; } /** * Get object list * * @param int $activeDuringLatestNumberOfSeconds * @param int $limit * @param int $offset * @param mixed $filter_source - ID of the data_source (APRS, CWOP) or an array of data source IDs. * @return array */ public function getObjectList($activeDuringLatestNumberOfSeconds = (24*60*60), $limit=50, $offset=0, $filter_source = 0) { if ($activeDuringLatestNumberOfSeconds == 0) { $activeDuringLatestNumberOfSeconds = time(); } // Filter sources $filter_query = null; $filter_data = null; if (is_array($filter_source)) { if (count($filter_source) > 0 && count($filter_source) < 4) { $filter_query = 'and source_id IN (' . implode(',', $filter_source) . ')'; } } else if (isInt($filter_source) && $filter_source > 0) { $filter_query = 'and source_id = :fs'; $filter_data = intval($filter_source); } $pdo = PDOConnection::getInstance(); $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > :ts1 and (source_id != 5 or latest_confirmed_packet_timestamp > :ts2) ' . $filter_query . ' order by latest_confirmed_packet_timestamp desc limit :limit offset :offset' ); $stmt->bindValue(':ts1', (time() - $activeDuringLatestNumberOfSeconds)); $stmt->bindValue(':ts2', (time() - (60*60*24))); // OGN data should be deleted after 24h, but just to be safe we avoid including older data when searching $stmt->bindValue(':limit', $limit); $stmt->bindValue(':offset', $offset); if (!is_null($filter_data)) { $stmt->bindValue(':fs', $filter_data); } $stmt->execute(); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (is_array($records) && !empty($records)) { return $this->_getObjectListFromRecords($records); } // No object found, return empty array return []; } /** * Get number of stations * * @param int $activeDuringLatestNumberOfSeconds * @return int */ public function getNumberOfStations($activeDuringLatestNumberOfSeconds = (24*60*60)) { if ($activeDuringLatestNumberOfSeconds == 0) { $activeDuringLatestNumberOfSeconds = time(); } $sql = 'select count(*) c from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and (source_id != 5 or latest_confirmed_packet_timestamp > ?)'; $parameters = [(time() - $activeDuringLatestNumberOfSeconds), (time() - (60*60*24))]; $pdo = PDOConnection::getInstance(); $stmt = $pdo->prepareAndExec($sql, $parameters); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $sum = 0; foreach($rows as $row) { $sum += $row['c']; } return $sum; } /** * Get object list by query string * * @param int $q * @param int $activeDuringLatestNumberOfSeconds * @param int $limit * @param int $offset * @return array */ public function getObjectListByQueryString($q, $activeDuringLatestNumberOfSeconds = (24*60*60), $limit=50, $offset=0) { if (!is_string($q) || !isInt($limit)) { return []; } if ($activeDuringLatestNumberOfSeconds == 0) { $activeDuringLatestNumberOfSeconds = time(); } $pdo = PDOConnection::getInstance(); $stmt = $pdo->prepare(' select s.* from station s left outer join ogn_device d on d.device_id = s.latest_ogn_sender_address where s.latest_confirmed_packet_timestamp is not null and s.latest_confirmed_packet_timestamp > ? and (s.source_id != 5 or s.latest_confirmed_packet_timestamp > ?) and (s.name ilike ? or d.registration ilike ? or d.cn ilike ?) order by name limit ? offset ?' ); $stmt->bindValue(1, (time() - $activeDuringLatestNumberOfSeconds)); $stmt->bindValue(2, (time() - (60*60*24))); // OGN data should be deleted after 24h, but just to be safe we avoid including older data when searching $stmt->bindValue(3, "$q%"); $stmt->bindValue(4, "$q%"); $stmt->bindValue(5, "$q%"); $stmt->bindValue(6, $limit); $stmt->bindValue(7, $offset); $stmt->execute(); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (is_array($records) && !empty($records)) { return $this->_getObjectListFromRecords($records); } // No object found, return empty array return []; } /** * Get number of stations by query string * * @param int $q * @param int $activeDuringLatestNumberOfSeconds * @return int */ public function getNumberOfStationsByQueryString($q, $activeDuringLatestNumberOfSeconds = (24*60*60)) { if (!is_string($q)) { return 0; } if ($activeDuringLatestNumberOfSeconds == 0) { $activeDuringLatestNumberOfSeconds = time(); } $sql = 'select count(*) c from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and (source_id != 5 or latest_confirmed_packet_timestamp > ?) and name ilike ?'; $parameters = [(time() - $activeDuringLatestNumberOfSeconds), (time() - (60*60*24)), "$q%"]; $pdo = PDOConnection::getInstance(); $stmt = $pdo->prepareAndExec($sql, $parameters); return $stmt->fetchColumn(); } /** * Get object list by sender id (close to specified lat/lng) * * @param int $senderId * @param int $limit * @param float $latitude * @param float $longitude * @return array */ public function getObjectListBySenderId($senderId, $limit, $latitude = null, $longitude = null) { if (!isInt($senderId) || !isFloat($latitude) || !isFloat($longitude) || !isInt($limit)) { return []; } $pdo = PDOConnection::getInstance(); if ($latitude === null || $longitude === null) { $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and latest_sender_id = ? limit ?' ); $stmt->bindValue(1, (time() - 60*60*24)); $stmt->bindValue(2, $senderId); $stmt->bindValue(3, $limit); } else { $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and latest_sender_id = ? order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?)) limit ?' ); $stmt->bindValue(1, (time() - 60*60*24)); $stmt->bindValue(2, $senderId); $stmt->bindValue(3, $latitude); $stmt->bindValue(4, $longitude); $stmt->bindValue(5, $limit); } $stmt->execute(); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (is_array($records) && !empty($records)) { return $this->_getObjectListFromRecords($records); } // No object found, return empty array return []; } /** * Get related object list by station id (stations with same name but different SSID) * * @param int $stationId * @param int $limit * @param int $durationHours (Number of hours to consider as active, default 24) * @return array */ public function getRelatedObjectListByStationId($stationId, $limit, $durationHours = 24) { if (!isInt($stationId) || !isInt($limit)) { return []; } $station = $this->getObjectById($stationId); $name = $station->name; $pos = strrpos($name, '-'); if ($pos) { $call = substr($name, 0, $pos); } else { // Primary stations are supposed to use the -0 suffix. // Some APRS software will drop the -0 and transmit just the callsign. $call = $name; } if ($station->latestPacketTimestamp !== null) { $time = $station->latestPacketTimestamp; } else { $time = time(); } $pdo = PDOConnection::getInstance(); if ($station->latestConfirmedLatitude === null || $station->latestConfirmedLongitude === null) { $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and name like ? limit ?' ); $stmt->bindValue(1, $time - 60*60*$durationHours, PDO::PARAM_STR); $stmt->bindValue(2, "$call%", PDO::PARAM_STR); $stmt->bindValue(3, $limit); } else { $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and name like ? order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?)) limit ?' ); $stmt->bindValue(1, $station->latestPacketTimestamp - 60*60*$durationHours, PDO::PARAM_STR); $stmt->bindValue(2, "$call%", PDO::PARAM_STR); $stmt->bindValue(3, $station->latestConfirmedLatitude); $stmt->bindValue(4, $station->latestConfirmedLongitude); $stmt->bindValue(5, $limit); } $stmt->execute(); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (is_array($records) && !empty($records)) { return $this->_getObjectListFromRecords($records); } // No object found, return empty array return []; } /** * Get the most recently used station (-SSID) from station call (no SSID) * * @param int $stationName * @param int $limit * @param int $durationHours (Number of hours to consider as active, default 60 days) * @return array */ public function getMostRecentStationByName($stationName, $limit = 1, $durationHours = 24*60) { if (!isInt($limit)) { return []; } $call = trim($stationName); $pdo = PDOConnection::getInstance(); $stmt = $pdo->prepare( 'select * from station where latest_confirmed_packet_timestamp is not null and latest_confirmed_packet_timestamp > ? and name like ? order by latest_confirmed_packet_timestamp desc limit ?' ); $stmt->bindValue(1, time() - 60*60*$durationHours, PDO::PARAM_STR); $stmt->bindValue(2, "$call%", PDO::PARAM_STR); $stmt->bindValue(3, $limit); $stmt->execute(); $records = $stmt->fetchAll(PDO::FETCH_ASSOC); if (is_array($records) && !empty($records)) { return $this->_getObjectListFromRecords($records); } // No object found, return empty array return []; } /** * Get object list of stations near specified position * * @param int $latitude * @param int $longitude * @param int $maxDistanceInKm * @param int $limit * @param string $sourceFilter * @return array */ public function getCloseByObjectListByPosition($latitude, $longitude, $maxDistanceInKm = 100, $limit = 10000, $sourceFilter = null) { if (!isFloat($latitude) || !isFloat($longitude) || !isInt($maxDistanceInKm) || !isInt($limit)) { return []; } if ($maxDistanceInKm <= 10) { // Plus 0.1 should be about 11km $minLatitude = $latitude - 0.1; $maxLatitude = $latitude + 0.1; $minLongitude = $longitude - 0.1; $maxLongitude = $longitude + 0.1; } else { // Plus 1 should be about 111km $minLatitude = $latitude - 1; $maxLatitude = $latitude + 1; $minLongitude = $longitude - 1; $maxLongitude = $longitude + 1; } $minTimestamp = time() - 60*60; // Latest 1h is pretty fast $sourceFilterSQL = ''; if ($sourceFilter != null && strlen($sourceFilter) > 0) { $sourceFilterSQL = " and source_id IN($sourceFilter) "; } // The order by used here is not 100% accurate, but it's fast :-) $list = $this->getObjectListFromSql( 'select * from station where latest_confirmed_latitude > ? and latest_confirmed_latitude < ? and latest_confirmed_longitude > ? and latest_confirmed_longitude < ? and latest_confirmed_packet_timestamp > ? ' . $sourceFilterSQL . ' order by (abs(latest_confirmed_latitude - ?) + abs(latest_confirmed_longitude - ?)) limit ?', [$minLatitude, $maxLatitude, $minLongitude, $maxLongitude, $minTimestamp, $latitude, $longitude, $limit] ); $orderedList = []; foreach ($list as $closeByStation) { $distance = $closeByStation->getDistance($latitude, $longitude); if ($distance !== null && $distance <= ($maxDistanceInKm*1000)) { $key = intval($distance); while (isset($orderedList[$key])) { $key++; } $orderedList[$key] = $closeByStation; } } ksort($orderedList); return array_values($orderedList); } /** * Get object list of stations near specified station * * @param int $stationId * @param int $limit * @param string $filterSource * @return array */ public function getCloseByObjectListByStationId($stationId, $limit, $filterSource) { if (!isInt($stationId) || !isInt($limit)) { return []; } $station = $this->getObjectById($stationId); if ($station->latestConfirmedLatitude === null || $station->latestConfirmedLongitude === null) { return []; } $closeByStationList = self::getCloseByObjectListByPosition($station->latestConfirmedLatitude, $station->latestConfirmedLongitude, 100, $limit + 1, $filterSource); $result = Array(); foreach ($closeByStationList as $key => $closeByStation) { if ($station->getId() == $closeByStation->getId()) { break; } } if (isset($key)) { unset($closeByStationList[$key]); } return $closeByStationList; } /** * Super-basic utility function to get object list from raw SQL query * * @param int $sqlQuery * @param array $queryParams * @return array */ public function getObjectListWithByRawSQLQuery($sqlQuery, $queryParams=[]) { if (empty($sqlQuery)) { return []; } return $this->getObjectListFromSql($sqlQuery, $queryParams); } }