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);
?>