diff --git a/helpers/position.php b/helpers/position.php index 60162ac..519f8c4 100644 --- a/helpers/position.php +++ b/helpers/position.php @@ -17,7 +17,7 @@ * along with this program; if not, see . */ - require_once(ROOT_DIR . "/helpers/db.php"); +require_once(ROOT_DIR . "/helpers/db.php"); require_once(ROOT_DIR . "/helpers/track.php"); require_once(ROOT_DIR . "/helpers/upload.php"); @@ -332,7 +332,7 @@ require_once(ROOT_DIR . "/helpers/upload.php"); $lon2 = deg2rad($target->longitude); $latD = $lat2 - $lat1; $lonD = $lon2 - $lon1; - $bearing = 2 * asin(sqrt(pow(sin($latD / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($lonD / 2), 2))); + $bearing = 2 * asin(sqrt((sin($latD / 2) ** 2) + cos($lat1) * cos($lat2) * (sin($lonD / 2) ** 2))); return $bearing * 6371000; } diff --git a/js/src/position.js b/js/src/position.js new file mode 100644 index 0000000..96d4064 --- /dev/null +++ b/js/src/position.js @@ -0,0 +1,86 @@ +/* + * μlogger + * + * Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net) + * + * This 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. + * + * This program 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 this program; if not, see . + */ + +import uUtils from './utils.js'; + +/** + * @class uPosition + * @property {number} id + * @property {number} latitude + * @property {number} longitude + * @property {?number} altitude + * @property {?number} speed + * @property {?number} bearing + * @property {?number} accuracy + * @property {?string} provider + * @property {?string} comment + * @property {?string} image + * @property {string} username + * @property {string} trackname + * @property {number} trackid + * @property {number} timestamp + * @property {number} distance + * @property {number} seconds + * @property {number} totalDistance + * @property {number} totalSeconds + */ +export default class uPosition { + + /** + * @throws On invalid input + * @param {Object} pos + * @returns {uPosition} + */ + static fromJson(pos) { + const position = new uPosition(); + position.id = uUtils.getInteger(pos.id); + position.latitude = uUtils.getFloat(pos.latitude); + position.longitude = uUtils.getFloat(pos.longitude); + position.altitude = uUtils.getInteger(pos.altitude, true); // may be null + position.speed = uUtils.getInteger(pos.speed, true); // may be null + position.bearing = uUtils.getInteger(pos.bearing, true); // may be null + position.accuracy = uUtils.getInteger(pos.accuracy, true); // may be null + position.provider = uUtils.getString(pos.provider, true); // may be null + position.comment = uUtils.getString(pos.comment, true); // may be null + position.image = uUtils.getString(pos.image, true); // may be null + position.username = uUtils.getString(pos.username); + position.trackname = uUtils.getString(pos.trackname); + position.trackid = uUtils.getInteger(pos.trackid); + position.timestamp = uUtils.getInteger(pos.timestamp); + position.distance = uUtils.getInteger(pos.distance); + position.seconds = uUtils.getInteger(pos.seconds); + position.totalDistance = 0; + position.totalSeconds = 0; + return position; + } + + /** + * @return {boolean} + */ + hasComment() { + return (this.comment != null && this.comment.length > 0); + } + + /** + * @return {boolean} + */ + hasImage() { + return (this.image != null && this.image.length > 0); + } +} diff --git a/js/test/position.test.js b/js/test/position.test.js new file mode 100644 index 0000000..6678b0b --- /dev/null +++ b/js/test/position.test.js @@ -0,0 +1,214 @@ +/* + * μlogger + * + * Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net) + * + * This 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. + * + * This program 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 this program; if not, see . + */ + +import uPosition from '../src/position.js'; + +describe('Position tests', () => { + + const nullableProperties = [ + 'altitude', + 'speed', + 'bearing', + 'accuracy', + 'provider', + 'comment', + 'image' + ]; + const nonNullableProperties = [ + 'id', + 'latitude', + 'longitude', + 'timestamp', + 'username', + 'trackid', + 'trackname', + 'distance', + 'seconds' + ]; + const properties = nullableProperties.concat(nonNullableProperties); + + let posId; + let latitude; + let longitude; + let altitude; + let speed; + let bearing; + let timestamp; + let accuracy; + let provider; + let comment; + let image; + let username; + let trackid; + let trackname; + let distance; + let seconds; + + let jsonPosition; + + beforeEach(() => { + posId = 110286; + latitude = 11.221871666666999; + longitude = 22.018848333333001; + altitude = -39; + speed = 0; + bearing = null; + timestamp = 1564250017; + accuracy = 9; + provider = 'gps'; + comment = null; + image = '134_5d3c8fa92ebac.jpg'; + username = 'test'; + trackid = 134; + trackname = 'Test name'; + distance = 0; + seconds = 0; + + jsonPosition = { + 'id': posId, + 'latitude': latitude, + 'longitude': longitude, + 'altitude': altitude, + 'speed': speed, + 'bearing': bearing, + 'timestamp': timestamp, + 'accuracy': accuracy, + 'provider': provider, + 'comment': comment, + 'image': image, + 'username': username, + 'trackid': trackid, + 'trackname': trackname, + 'distance': distance, + 'seconds': seconds + }; + }); + + it('should create uPosition instance from json object', () => { + // when + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.id).toBe(posId); + expect(position.latitude).toBe(latitude); + expect(position.longitude).toBe(longitude); + expect(position.speed).toBe(speed); + expect(position.bearing).toBe(bearing); + expect(position.timestamp).toBe(timestamp); + expect(position.accuracy).toBe(accuracy); + expect(position.provider).toBe(provider); + expect(position.comment).toBe(comment); + expect(position.image).toBe(image); + expect(position.username).toBe(username); + expect(position.trackid).toBe(trackid); + expect(position.trackname).toBe(trackname); + expect(position.distance).toBe(distance); + expect(position.seconds).toBe(seconds); + }); + + describe('should raise error on undefined property', () => { + properties.forEach((prop) => { + it(`testing property: ${prop}`, () => { + // given + const posCopy = { ...jsonPosition }; + // when + delete posCopy[prop]; + // then + expect(() => { uPosition.fromJson(posCopy); }).toThrow(new Error('Illegal value')); + }); + }); + }); + + describe('should raise error on null non-nullable property', () => { + nonNullableProperties.forEach((prop) => { + it(`testing property: ${prop}`, () => { + // given + const posCopy = { ...jsonPosition }; + // when + posCopy[prop] = null; + // then + expect(() => { uPosition.fromJson(posCopy); }).toThrow(new Error('Illegal value')); + }); + }); + }); + + describe('should not raise error on null nullable property', () => { + nullableProperties.forEach((prop) => { + it(`testing property: ${prop}`, () => { + // given + const posCopy = { ...jsonPosition }; + // when + posCopy[prop] = null; + let pos = {}; + // then + expect(() => { pos = uPosition.fromJson(posCopy); }).not.toThrow(new Error('Illegal value')); + expect(pos[prop]).toBeNull(); + }); + }); + }); + + it('should result false on null comment', () => { + // when + jsonPosition.comment = null; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasComment()).toBe(false); + }); + + it('should result false on empty comment', () => { + // when + jsonPosition.comment = ''; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasComment()).toBe(false); + }); + + it('should result true on non-null comment', () => { + // when + jsonPosition.comment = 'comment'; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasComment()).toBe(true); + }); + + + it('should result false on null image', () => { + // when + jsonPosition.image = null; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasImage()).toBe(false); + }); + + it('should result false on empty image', () => { + // when + jsonPosition.image = ''; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasImage()).toBe(false); + }); + + it('should result true on non-null image', () => { + // when + jsonPosition.image = 'image'; + const position = uPosition.fromJson(jsonPosition); + // then + expect(position.hasImage()).toBe(true); + }); + +}); diff --git a/utils/getpositions.php b/utils/getpositions.php index 5bbf9f2..5f827ad 100644 --- a/utils/getpositions.php +++ b/utils/getpositions.php @@ -49,42 +49,35 @@ if ($userId) { } } -header("Content-type: text/xml"); -$xml = new XMLWriter(); -$xml->openURI("php://output"); -$xml->startDocument("1.0"); -$xml->setIndent(true); -$xml->startElement('root'); - -if (!empty($positionsArr)) { +$result = []; +if ($positionsArr === false) { + $result = [ "error" => true ]; +} else if (!empty($positionsArr)) { foreach ($positionsArr as $position) { - /** @var uPosition $prevPosition */ - $xml->startElement("position"); - $xml->writeAttribute("id", $position->id); - $xml->writeElement("latitude", $position->latitude); - $xml->writeElement("longitude", $position->longitude); - $xml->writeElement("altitude", ($position->altitude) ? round($position->altitude) : $position->altitude); - $xml->writeElement("speed", $position->speed); - $xml->writeElement("bearing", $position->bearing); - $xml->writeElement("timestamp", $position->timestamp); - $xml->writeElement("accuracy", $position->accuracy); - $xml->writeElement("provider", $position->provider); - $xml->writeElement("comment", $position->comment); - $xml->writeElement("image", $position->image); - $xml->writeElement("username", $position->userLogin); - $xml->writeElement("trackid", $position->trackId); - $xml->writeElement("trackname", $position->trackName); - $distance = !$last && isset($prevPosition) ? $position->distanceTo($prevPosition) : 0; - $xml->writeElement("distance", round($distance)); - $seconds = !$last && isset($prevPosition) ? $position->secondsTo($prevPosition) : 0; - $xml->writeElement("seconds", $seconds); - $xml->endElement(); + $distance = !$last && isset($prevPosition) ? $position->distanceTo($prevPosition) : 0; + $seconds = !$last && isset($prevPosition) ? $position->secondsTo($prevPosition) : 0; + $result[] = [ + "id" => $position->id, + "latitude" => $position->latitude, + "longitude" => $position->longitude, + "altitude" => ($position->altitude) ? round($position->altitude) : $position->altitude, + "speed" => $position->speed, + "bearing" => $position->bearing, + "timestamp" => $position->timestamp, + "accuracy" => $position->accuracy, + "provider" => $position->provider, + "comment" => $position->comment, + "image" => $position->image, + "username" => $position->userLogin, + "trackid" => $position->trackId, + "trackname" => $position->trackName, + "distance" => round($distance), + "seconds" => $seconds + ]; $prevPosition = $position; } } - -$xml->endElement(); -$xml->endDocument(); -$xml->flush(); +header("Content-type: application/json"); +echo json_encode($result); ?>