diff --git a/classes/Geo.php b/classes/Geo.php index 7967b98..cb68c95 100644 --- a/classes/Geo.php +++ b/classes/Geo.php @@ -16,7 +16,14 @@ class Geo $delta_lat = ($lat2 - $lat1); $delta_lng = ($lng2 - $lng1); - return (6378137 * acos(cos($lat1) * cos($lat2) * cos($lng1 - $lng2) + sin($lat1) * sin($lat2))); + $distance = (6378137 * acos(cos($lat1) * cos($lat2) * cos($lng1 - $lng2) + sin($lat1) * sin($lat2))); + + if (isset($latlng1['alt']) && isset($latlng2['alt']) && $latlng1['alt'] > 0 && $latlng1['alt'] <> $latlng2['alt']) { + $altDelta = abs($latlng1['alt'] - $latlng2['alt']); + $distance = sqrt(pow($distance, 2) + pow($altDelta, 2)); + } + + return $distance; } static function formatedDistance($distance) diff --git a/classes/Model.php b/classes/Model.php index 83250b3..c2f53d3 100644 --- a/classes/Model.php +++ b/classes/Model.php @@ -85,6 +85,9 @@ abstract class Model return $model::model()->getOne($targetField, $this->$localField); break; case self::TO_MANY: + if ($order) { + return $model::model()->getAll(array('where' => "`$targetField` = '" . $this->$localField . "'", 'order' => $order)); + } return $model::model()->getAll(array('where' => "`$targetField` = '" . $this->$localField . "'")); break; default: @@ -115,13 +118,14 @@ abstract class Model * @param const $relationType - тип связи (к одному / ко многим). * @return obkect/array */ - public function setRelation($nameOfRelation, $localField, $targetModel, $targetField, $relationType) + public function setRelation($nameOfRelation, $localField, $targetModel, $targetField, $relationType, $order = NULL) { return $this->_relations_[$nameOfRelation] = array( 'localField' => $localField, 'targetModel' => $targetModel, 'targetField' => $targetField, 'relationType' => $relationType, + 'order' => $order, ); } diff --git a/controllers/EdittrackController.php b/controllers/EdittrackController.php index 5a779fe..2766ccb 100644 --- a/controllers/EdittrackController.php +++ b/controllers/EdittrackController.php @@ -81,7 +81,7 @@ class EdittrackController extends Controller $trip->json = ''; $trip->save(); - App::redirect('/tracks/edit/id/'.$trip->id); + App::redirect('/tracks/edit/id/' . $trip->id); } function actionMergetrack() @@ -99,7 +99,7 @@ class EdittrackController extends Controller } $takeIt = $mergetId = $merget = FALSE; - $tracks = Tracks::model()->getAll(array('where'=>'`tripId` = ?'), array($trip->id)); + $tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id)); foreach ($tracks as $key => $value) { if ($takeIt) { $mergetId = $key; @@ -129,7 +129,7 @@ class EdittrackController extends Controller $trip->json = ''; $trip->save(); - App::redirect('/tracks/edit/id/'.$trip->id); + App::redirect('/tracks/edit/id/' . $trip->id); } function actionWaypoint() @@ -170,7 +170,7 @@ class EdittrackController extends Controller $trip->json = ''; $trip->save(); - App::redirect('/tracks/edit/id/'.$trip->id); + App::redirect('/tracks/edit/id/' . $trip->id); } function actionGettrackdetails() @@ -187,8 +187,105 @@ class EdittrackController extends Controller App::error404(); } - $trackpoints = TrackPoints::model()->getAll(array('where' => '`trackId` = '.$track->id, 'asArray' => TRUE, 'noKeys' => TRUE,)); + $trackpoints = TrackPoints::model()->getAll(array('where' => '`trackId` = ' . $track->id, 'asArray' => TRUE, 'noKeys' => TRUE, 'order' => '`id`')); self::renderPartial('json.php', $trackpoints); } -} \ No newline at end of file + + function actionCut() + { + $id = intval(App::getParam('id')); + + $trackPoint = TrackPoints::model()->getByPK($id); + if ($trackPoint->isEmpty()) { + App::error404(); + } + + $track = Tracks::model()->getByPK($trackPoint->trackId); + if ($track->isEmpty()) { + App::error404(); + } + + $trip = Trips::model()->getByPK($track->tripId); + if ($trip->isEmpty() || $trip->owner != App::$user->id) { + App::error404(); + } + + $tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id)); + foreach ($tracks as $trk) { + $newTrack = Tracks::model()->add( + array( + 'tripId' => $trip->id, + 'name' => $trk->name, + 'distance' => $trk->distance, + 'description' => $trk->description, + ) + ); + + + if ($trk->id == $track->id) { + $trackPoints = TrackPoints::model()->getAll(array('where'=>'`trackId` = ? AND `id` <= ?', 'order' => 'id'), array($trk->id, $trackPoint->id)); + foreach ($trackPoints as $tpt) { + TrackPoints::model()->add( + array( + 'trackId' => $newTrack->id, + 'lat' => floatval($tpt->lat), + 'lng' => floatval($tpt->lng), + 'alt' => $tpt->alt, + 'createdAt' => $tpt->createdAt, + 'speed' => $tpt->speed, + ) + ); + + } + + $newTrack = Tracks::model()->add( + array( + 'tripId' => $trip->id, + 'name' => $trk->name . ' / #2', + 'distance' => $trk->distance, + 'description' => $trk->description, + ) + ); + + $trackPoints = TrackPoints::model()->getAll(array('where'=>'`trackId` = ? AND `id` >= ?', 'order' => 'id'), array($trk->id, $trackPoint->id)); + foreach ($trackPoints as $tpt) { + TrackPoints::model()->add( + array( + 'trackId' => $newTrack->id, + 'lat' => floatval($tpt->lat), + 'lng' => floatval($tpt->lng), + 'alt' => $tpt->alt, + 'createdAt' => $tpt->createdAt, + 'speed' => $tpt->speed, + ) + ); + } + } else { + $trackPoints = TrackPoints::model()->getAll(array('where'=>'`trackId` = ?', 'order' => 'id'), array($trk->id)); + foreach ($trackPoints as $tpt) { + TrackPoints::model()->add( + array( + 'trackId' => $newTrack->id, + 'lat' => floatval($tpt->lat), + 'lng' => floatval($tpt->lng), + 'alt' => $tpt->alt, + 'createdAt' => $tpt->createdAt, + 'speed' => $tpt->speed, + ) + ); + } + } + + App::DB()->query("DELETE FROM `trackPoints` WHERE `trackId` = ".$trk->id); + $trk->delete($trk->id); + } + + $trip->json = ''; + $trip->calculateDist(); + $trip->save(); + + App::redirect('/tracks/edit/id/' . $trip->id); + } + +} diff --git a/controllers/JsonController.php b/controllers/JsonController.php index dae5b09..b130d00 100644 --- a/controllers/JsonController.php +++ b/controllers/JsonController.php @@ -621,11 +621,11 @@ class JsonController extends Controller } - $tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id)); + $tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?', 'order' => 'id'), array($trip->id)); if ($tracks) { foreach ($tracks as $track) { $resLatlngs = array(); - $trackPoints = TrackPoints::model()->getAll(array('where' => '`trackId` = ?'), array($track->id)); + $trackPoints = TrackPoints::model()->getAll(array('where' => '`trackId` = ?', 'order'=>'id'), array($track->id)); if ($trackPoints) { foreach ($trackPoints as $trackPoint) { $resLatlngs[] = array( diff --git a/controllers/TracksController.php b/controllers/TracksController.php index 63bafd6..947b23f 100644 --- a/controllers/TracksController.php +++ b/controllers/TracksController.php @@ -30,6 +30,12 @@ class TracksController extends Controller function actionEdit() { + self::addScript('/js/leaflet.js'); + self::addStyle('/css/leaflet.css'); + + self::addScript('/js/editTrack.js'); + self::addStyle('/css/editTrack.css'); + $id = intval(App::getParam('id')); if (!$id) { @@ -108,6 +114,22 @@ class TracksController extends Controller )); } + function actionExport() + { + $id = intval(App::getParam('id')); + $trip = Trips::model()->getByPK($id); + + if ($trip->isEmpty() || $trip->owner != App::$user->id) { + App::error404(); + } + + self::renderPartial('/tracks/gpx.php', array( + 'trip' => $trip, + 'waypoints' => WayPoints::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id)), + 'tracks' => Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id)), + )); + } + function actionImport() { $id = (int) App::getParam('id'); @@ -206,25 +228,29 @@ class TracksController extends Controller foreach ($trkpt as $tpt) { $speed = isset($tpt->extensions->geotrackerMeta['s']) ? floatval($tpt->extensions->geotrackerMeta['s']) : 0; + $alt = isset($tpt->ele) ? floatval($tpt->ele) : 0; $trackPoint = TrackPoints::model()->add( array( 'trackId' => $track->id, 'lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lon']), - 'alt' => isset($tpt->ele) ? floatval($tpt->ele) : 0, + 'alt' => $alt, 'createdAt' => isset($tpt->time) ? $tpt->time : date('c'), 'speed' => $speed, ) ); if ($prevPoint) { - $distanceTmp += Geo::distance($prevPoint, array('lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lon']))); + $distanceTmp += Geo::distance($prevPoint, array('lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lon']), 'alt' => $alt)); } $prevPoint = array( 'lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lon']), ); + if (isset($tpt->ele)) { + $prevPoint['alt'] = floatval($tpt->ele); + } $tptCount++; } diff --git a/models/Tracks.php b/models/Tracks.php index 05fc827..bd3dfd4 100644 --- a/models/Tracks.php +++ b/models/Tracks.php @@ -7,6 +7,7 @@ class Tracks extends Model { $this->_tableName_ = 'tracks'; parent::__construct($fromArray); + $this->setRelation('points', 'id', 'TrackPoints', 'trackId', self::TO_MANY, '`id` ASC'); } static function model() @@ -18,4 +19,32 @@ class Tracks extends Model { return Geo::formatedDistance($this->distance); } + + /** + * Считает/пересчитывает длину фрагмента трека и сохраняет значение в БД. + */ + function calculateDist() + { + $points = $this->points; + $distance = 0; + $prevPoint = array(); + foreach ($points as $point) { + if ($prevPoint) { + $distance += Geo::distance( + $prevPoint, + array('lat' => floatval($point->lat), 'lng' => floatval($point->lng), 'alt' => floatval($point->alt))); + } + + $prevPoint = array( + 'lat' => floatval($point->lat), + 'lng' => floatval($point->lng), + 'alt' => floatval($point->alt), + ); + } + $this->distance = $distance; + $this->save(); + + return $distance; + } + } diff --git a/models/Trips.php b/models/Trips.php index ae2b4ae..e3882b9 100644 --- a/models/Trips.php +++ b/models/Trips.php @@ -11,6 +11,7 @@ class Trips extends Model { $this->_tableName_ = 'trips'; parent::__construct($fromArray); + $this->setRelation('tracks', 'id', 'Tracks', 'tripId', self::TO_MANY, '`id` ASC'); } static function model() @@ -22,4 +23,22 @@ class Trips extends Model { return Geo::formatedDistance($this->distance); } + + /** + * Считает/пересчитывает длину трека и всех его фрагментов + */ + function calculateDist() + { + $tracks = $this->tracks; + $distance = 0; + + foreach ($tracks as $track) { + $distance += $track->calculateDist(); + } + + $this->distance = $distance; + $this->save(); + + return $distance; + } } diff --git a/public/css/editTrack.css b/public/css/editTrack.css new file mode 100644 index 0000000..bff06a7 --- /dev/null +++ b/public/css/editTrack.css @@ -0,0 +1,11 @@ +.savedOK {color: #999; display: none;} +.savedError {color: #900; display: none;} + +.trackpointsContainer {display: none;} +.trackpointsContainer table {height: 500px; width: 100%; margin-bottom: 20px;} +.trackpointsContainer table td {width: 50%;} + +.trackPoint:hover {background-color: #ddd;} + +.trackpointsList {overflow-y: scroll; height: 500px;} +.trackpointsMap {height: 500px;} \ No newline at end of file diff --git a/public/js/editTrack.js b/public/js/editTrack.js new file mode 100644 index 0000000..f60b252 --- /dev/null +++ b/public/js/editTrack.js @@ -0,0 +1,140 @@ +var tracks = new Array; +var maps = new Array; +var markers = new Array; +var icon = L.icon({ + iconUrl: '/ico/marker.png', + iconSize: [32, 37], + iconAnchor: [16, 37], + popupAnchor: [0, 1] +}); + +function saveChangesTrip() +{ + $('#tripSavedOK').hide(); + $('#tripSavedError').hide(); + + $.ajax({ + url: "/edittrack/trip/id/= $trip->id ?>", + method: "POST", + data: { + name: $('#tripName').val(), + description: $('#tripDescription').val(), + isPublic: $('#tripIsPublic').prop('checked') + }, + success: function (data) { + $('#tripSavedOK').show(); + setTimeout(function () { + $('#tripSavedOK').fadeOut(300); + }, 2000); + }, + error: function (data) { + $('#tripSavedError').show(); + } + }); +} + +function saveChangesTrack(id) +{ + $('#trackSavedOK').hide(); + $('#trackSavedError').hide(); + + $.ajax({ + url: "/edittrack/track/id/" + id, + method: "POST", + data: { + name: $('#trackName' + id).val(), + description: $('#trackDescription' + id).val() + }, + success: function (data) { + $('#trackSavedOK').show(); + setTimeout(function () { + $('#trackSavedOK').fadeOut(300); + }, 2000); + }, + error: function (data) { + $('#trackSavedError').show(); + } + }); +} + +function saveChangesWaypoint(id) +{ + $('#waypointSavedOK').hide(); + $('#waypointSavedError').hide(); + + $.ajax({ + url: "/edittrack/waypoint/id/" + id, + method: "POST", + data: { + name: $('#waypointName' + id).val(), + description: $('#waypointDescription' + id).val() + }, + success: function (data) { + $('#waypointSavedOK').show(); + setTimeout(function () { + $('#waypointSavedOK').fadeOut(300); + }, 2000); + }, + error: function (data) { + $('#waypointSavedError').show(); + } + }); +} + +function loadTrackDetails(id) +{ + $('#trackpointsContainer' + id).toggle(); + if ($('#trackpointsMap' + id).text() != '') { + return true; + } + + $.ajax({ + url: "/edittrack/gettrackdetails/id/" + id, + method: "POST", + success: function (data) { + $('#trackpointsList' + id).html(''); + if (typeof (data) != 'undefined') { + latlngs = new Array(); + for (var i = 0; i < data.length; i++) { + $(makeRow(data[i])).appendTo('#trackpointsList' + id); + latlngs.push({lat: data[i]['lat'], lng: data[i]['lng']}) + + } + + maps[id] = L.map('trackpointsMap' + id, {zoomControl: true, scrollWheelZoom: false}).setView([latlngs[0]['lat'], latlngs[0]['lng']], 13); + L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(maps[id]); + + tracks[id] = L.polyline(latlngs, {color: '#F00', width: 4, opacity: 0.8}); + maps[id].addLayer(tracks[id]); + + markers[id] = L.marker([latlngs[1]['lat'], latlngs[1]['lng']], {icon: icon}); + maps[id].addLayer(markers[id]) + + maps[id].fitBounds(tracks[id].getBounds()); + } + }, + error: function (data) { + console.log(data); + } + }); +} + +function makeRow(data) +{ + row = '
| + | + |