Merge branch 'tracks'

This commit is contained in:
Krivchikov Dmitry 2016-05-30 00:01:33 +03:00
commit d3d638831f
22 changed files with 1361 additions and 30 deletions

4
.gitignore vendored
View File

@ -23,5 +23,9 @@ public/photos/big/*.jpeg
public/photos/big/*.png
public/photos/big/*.gif
tracks/*.gpx
tracks/*.kml
tracks/*.kmz
logs/*
.idea

26
classes/Geo.php Normal file
View File

@ -0,0 +1,26 @@
<?php
class Geo
{
static function distance($latlng1, $latlng2)
{
// Convert degrees to radians.
$lat1 = deg2rad($latlng1['lat']);
$lng1 = deg2rad($latlng1['lng']);
$lat2 = deg2rad($latlng2['lat']);
$lng2 = deg2rad($latlng2['lng']);
// Calculate delta longitude and latitude.
$delta_lat = ($lat2 - $lat1);
$delta_lng = ($lng2 - $lng1);
return (6378137 * acos(cos($lat1) * cos($lat2) * cos($lng1 - $lng2) + sin($lat1) * sin($lat2)));
}
static function formatedDistance($distance)
{
return $distance > 1000 ? round($distance/1000, 2).'км' : round($distance).'м';
}
}

171
classes/Kml.php Normal file
View File

@ -0,0 +1,171 @@
<?php
class Kml
{
private $kml = NULL;
private $waypoints = array();
private $tracks = array();
public function __construct($path)
{
$xml = substr($path, -4) == '.kmz' ? $this->getKmzContent($path) : file_get_contents($path);
$xml = str_replace(array('<gx:', '</gx:'), array('<gx', '</gx'), $xml);
$this->kml = new DOMDocument;
$this->kml->loadXML($xml);
foreach ($this->get($this->kml, 'Placemark') as $placemark) {
if ($this->has($placemark, 'Point')) {
$this->waypoints[] = array(
'name' => trim($this->getValue($placemark, 'name')),
'description' => trim($this->getValue($placemark, 'description')),
'when' => date('c', strtotime($this->getValue($placemark, 'when'))),
'latlng' => $this->getCoords($placemark)[0],
);
}
if ($this->has($placemark, 'LineString')) {
$this->tracks[] = array(
'name' => trim($this->getValue($placemark, 'name')),
'description' => trim($this->getValue($placemark, 'description')),
'latlng' => $this->getCoords($placemark),
);
}
if ($this->has($placemark, 'gxMultiTrack')) {
$this->tracks = $this->getTracksGX($placemark);
}
};
}
public function getName()
{
return $this->getValue($this->kml, 'name') ? strval($this->getValue($this->kml, 'name')) : 'Track KML';
}
public function getDescription()
{
return $this->getValue($this->kml, 'description') ? strval($this->getValue($this->kml, 'description')) : '';
}
public function getTime()
{
return $this->getValue($this->kml, 'when') ? date('c', strtotime($this->getValue($this->kml, 'when'))) : date('c');
}
public function getWaypoints()
{
return $this->waypoints;
}
public function getTracks()
{
return $this->tracks;
}
private function has($src, $tagName)
{
return $this->get($src, $tagName)->length > 0;
}
private function get($src, $tagName)
{
return $src->getElementsByTagName($tagName);
}
private function getValue($src, $tagName)
{
foreach ($src->getElementsByTagName($tagName) AS $value) {
return $value->nodeValue;
}
}
private function getCoords($src)
{
foreach ($src->getElementsByTagName('coordinates') AS $value) {
$result = array();
$coords = $value->nodeValue;
$coords = str_replace(array(" ", "\n", "\r", "\t"), ' ', $coords);
// multi-space
$pr = '(\s{2,})';
$rep = ' ';
$coords = preg_replace($pr, $rep, $coords);
$coords = explode(' ', trim($coords));
foreach ($coords as $coord) {
$tmp = explode(',', $coord);
$result[] = array(
'lat' => $tmp[1],
'lng' => $tmp[0],
'alt' => $tmp[2],
);
}
return $result;
}
return array();
}
private function getTracksGX($placemark)
{
$result = array();
foreach ($placemark->getElementsByTagName('gxTrack') AS $gxTrack) {
$track = array();
foreach ($gxTrack->getElementsByTagName('gxcoord') AS $value) {
$coords = $value->nodeValue;
$coords = str_replace(array(" ", "\n", "\r", "\t"), ' ', $coords);
// multi-space
$pr = '(\s{2,})';
$rep = ' ';
$coords = preg_replace($pr, $rep, $coords);
$tmp = explode(' ', trim($coords));
$track[] = array(
'lat' => $tmp[1],
'lng' => $tmp[0],
'alt' => $tmp[2],
);
}
$result[] = array(
'name' => '',
'description' => '',
'latlng' => $track,
);
}
return $result;
}
private function getKmzContent($path)
{
$zip = zip_open($path);
if (!$zip) {
App::error404();
}
$zip_entry = zip_read($zip);
if (zip_entry_name($zip_entry) != 'doc.kml') {
App::error404();
}
if (zip_entry_open($zip, $zip_entry, "r")) {
$buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
zip_entry_close($zip_entry);
zip_close($zip);
return $buf;
}
App::error404();
}
}

View File

@ -204,30 +204,9 @@ abstract class Model
* @param array $values - ассоциативный массив соо значениями.
* @return object - id добавленной записи или false.
*/
function add2($values)
{
$values = $this->_vals_;
unset($values[$this->_primaryKey_]);
$q = array();
foreach ($values as $val) {
$q[] = '?';
}
var_dump('INSERT INTO `' . $this->_tableName_ . '` (`' . implode('`, `', array_keys($values)) . '`) VALUES (' . implode(', ', array_values($q)) . ') ');
die;
$res = App::DB()->prepare_query('INSERT INTO `' . $this->_tableName_ . '` (`' . implode('`, `', array_keys($values)) . '`) VALUES (' . implode(', ', array_values($q)) . ') ', self::arrayWithTypes($values));
if ($res) {
throw new Exception('MySQL error #' . $res->errno . ' - ' . $res->error);
}
return $this->getByPK(App::DB()->insert_id);
}
function add($values)
{
$request = App::DB()->prepare('INSERT INTO ' . $this->_tableName_ . ' (' . implode(',', array_keys($values)) . ') values (:' . implode(', :', array_keys($values)) . ')');
$request = App::DB()->prepare('INSERT INTO `' . $this->_tableName_ . '` (`' . implode('`, `', array_keys($values)) . '`) values (:' . implode(', :', array_keys($values)) . ')');
if ($request->execute((array) $values)) {
return $this->getByPK(App::DB()->lastInsertId());
} else {

View File

@ -0,0 +1,113 @@
<?php
class EdittrackController extends Controller
{
function __construct()
{
if (!App::$user->id) {
App::redirect('/');
}
}
function actionTrip()
{
$id = intval(App::getParam('id'));
$trip = Trips::model()->getByPK($id);
if (empty($_POST['name']) || $trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
$trip->name = trim($_POST['name']);
$trip->description = trim($_POST['description']);
$trip->status = ($_POST['isPublic'] == 'true') ? Trips::STATUS_PUBLIC : Trips::STATUS_PRIVATE;
$trip->json = '';
$trip->save();
}
function actionTrack()
{
$id = intval(App::getParam('id'));
$track = Tracks::model()->getByPK($id);
if (empty($_POST['name']) || $track->isEmpty()) {
App::error404();
}
$trip = Trips::model()->getByPK($track->tripId);
if ($trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
$track->name = trim($_POST['name']);
$track->description = trim($_POST['description']);
$track->save();
$trip->json = '';
$trip->save();
}
function actionDeletetrack()
{
$id = intval(App::getParam('id'));
$track = Tracks::model()->getByPK($id);
if ($track->isEmpty()) {
App::error404();
}
$trip = Trips::model()->getByPK($track->tripId);
if ($trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
$track->delete($id);
$trip->json = '';
$trip->save();
App::redirect('/tracks/edit/id/'.$trip->id);
}
function actionWaypoint()
{
$id = intval(App::getParam('id'));
$waypoint = WayPoints::model()->getByPK($id);
if (empty($_POST['name']) || $waypoint->isEmpty()) {
App::error404();
}
$trip = Trips::model()->getByPK($waypoint->tripId);
if ($trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
$waypoint->name = trim($_POST['name']);
$waypoint->description = trim($_POST['description']);
$waypoint->save();
}
function actionDeletewaypoint()
{
$id = intval(App::getParam('id'));
$waypoint = WayPoints::model()->getByPK($id);
if ($waypoint->isEmpty()) {
App::error404();
}
$trip = Trips::model()->getByPK($waypoint->tripId);
if ($trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
$waypoint->delete($id);
$trip->json = '';
$trip->save();
App::redirect('/tracks/edit/id/'.$trip->id);
}
}

View File

@ -28,6 +28,7 @@ class IndexController extends Controller
self::addScript('/js/navigation.js?ver=' . App::getConfig('version'));
self::addScript('/js/map.js?ver=' . App::getConfig('version'));
self::addScript('/js/fotorama.js');
self::addScript('/js/tracks.js?ver=' . App::getConfig('version'));
// self::addStyle('/css/style.css?ver=' . App::getConfig('version'));
self::addStyle('/css/leaflet.css');
@ -40,12 +41,12 @@ class IndexController extends Controller
$category = Category::model()->getAll(array('order' => 'ord ASC'));
$categoryArray = Category::model()->getAll(array('order' => 'ord ASC', 'asArray' => true));
$routes = array();
$favorites = array();
$routes = $favorites = $tracks = array();
if (App::$user && App::$user->id) {
self::addVar('user', App::$user->id);
$routes = Route::model()->getByAuthor(App::$user->id);
$favorites = Favorite::model()->getAllMy();
$tracks = Trips::model()->getAll(array('where' => '`owner` = ?'), array(App::$user->id));
} else {
self::addVar('user', '0');
}
@ -93,6 +94,7 @@ class IndexController extends Controller
'user' => App::$user,
'routes' => $routes,
'favorites' => $favorites,
'tracks' => $tracks,
'og' => $og,
));
}

View File

@ -577,4 +577,200 @@ class JsonController extends Controller
));
}
static function actionTrack()
{
$id = intval(App::getParam('id'));
if (!$id) {
App::error404();
}
$trip = Trips::model()->getByPK($id);
if ($trip->isEmpty() || ($trip->status != Trips::STATUS_PUBLIC && $trip->owner != App::$user->id)) {
App::error404();
}
if ($trip->json) {
header('Content-Type: application/json');
print $trip->json;
die;
}
$res = array(
'name' => $trip->name,
'time' => $trip->createdAt,
);
$resWayPoints = array();
$resTracks = array();
$resLatlngs = array();
$wayPoints = WayPoints::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id));
if ($wayPoints) {
foreach ($wayPoints as $block) {
$resWayPoints[] = array(
'lat' => floatval($block->lat),
'lng' => floatval($block->lng),
'time' => strtotime($block->createdAt),
'name' => (string) $block->name,
'description' => nl2br($block->description),
);
}
}
$tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id));
if ($tracks) {
foreach ($tracks as $track) {
$resLatlngs = array();
$trackPoints = TrackPoints::model()->getAll(array('where' => '`trackId` = ?'), array($track->id));
if ($trackPoints) {
foreach ($trackPoints as $trackPoint) {
$resLatlngs[] = array(
'lat' => $trackPoint->lat,
'lng' => $trackPoint->lng,
);
}
}
$resTracks[] = array(
'name' => (string) $track->name,
'length' => 0,
'latlngs' => $resLatlngs,
);
}
}
$res['wayPoints'] = $resWayPoints;
$res['tracks'] = $resTracks;
$json = self::renderPartial('json.php', $res, TRUE);
$trip->json = $json;
$trip->save();
header('Content-Type: application/json');
print $json;
die;
}
static function actionSimpletrack()
{
$id = intval(App::getParam('id'));
if (!$id) {
App::error404();
}
$trip = Trips::model()->getByPK($id);
if ($trip->isEmpty() || ($trip->status != Trips::STATUS_PUBLIC && $trip->owner != App::$user->id)) {
App::error404();
}
$res = array(
'name' => $trip->name,
'time' => $trip->createdAt,
);
$resWayPoints = array();
$resTracks = array();
$resLatlngs = array();
$totCnt = 0;
$wayPoints = WayPoints::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id));
if ($wayPoints) {
foreach ($wayPoints as $block) {
$resWayPoints[] = array(
'lat' => floatval($block->lat),
'lng' => floatval($block->lng),
'time' => strtotime($block->createdAt),
'name' => (string) $block->name,
'description' => nl2br($block->description),
);
}
}
$tracks = Tracks::model()->getAll(array('where' => '`tripId` = ?'), array($trip->id));
if ($tracks) {
foreach ($tracks as $track) {
$resLatlngs = array();
$trackPoints = TrackPoints::model()->getAll(array('where' => '`trackId` = ?', 'noKeys' => TRUE), array($track->id));
if ($trackPoints) {
for ($index = 0; $index < count($trackPoints); $index++) {
$trackPoint = $trackPoints[$index];
if ($index > 2 && $index < count($trackPoints)-2) {
$nextPoint = $trackPoints[$index+1];
$nextPoint = array(
'lat' => $nextPoint->lat,
'lng' => $nextPoint->lng,
);
$distance1 = Geo::distance($prevPoint, array('lat' => $trackPoint->lat, 'lng' => $trackPoint->lng));
if ($distance1 < 30) {
continue;
}
// $distance2 = Geo::distance($nextPoint, array('lat' => $trackPoint->lat, 'lng' => $trackPoint->lng));
// $distance3 = Geo::distance($prevPoint, $nextPoint);
//
//
// if (($distance3 / ($distance1+$distance2)) > 0.982) {
// continue;
// }
}
$prevPoint = $trackPoints[$index];
$prevPoint = array(
'lat' => $prevPoint->lat,
'lng' => $prevPoint->lng,
);
$resLatlngs[] = array(
'lat' => $trackPoint->lat,
'lng' => $trackPoint->lng,
);
}
// die;
}
$totCnt += count($resLatlngs);
$resTracks[] = array(
'name' => (string) $track->name,
'length' => 0,
'latlngs' => $resLatlngs,
'points' => count($resLatlngs),
);
}
}
$res['wayPoints'] = $resWayPoints;
$res['tracks'] = $resTracks;
$res['totCnt'] = $totCnt;
$json = self::renderPartial('json.php', $res, TRUE);
$trip->json = $json;
$trip->save();
header('Content-Type: application/json');
print $json;
die;
}
}

View File

@ -0,0 +1,340 @@
<?php
class TracksController extends Controller
{
function __construct()
{
if (!App::$user->id) {
App::redirect('/');
}
self::$layout = 'layouts/page.php';
self::addStyle('/css/style.css?ver=' . App::getConfig('version'));
self::addStyle('/css/pageStyle.css?ver=' . App::getConfig('version'));
self::addScript('/js/jquery-2.1.0.min.js');
self::addScript('/js/jquery-ui-1.10.4.custom.min.js');
self::addScript('/js/bootstrap.min.js');
self::addStyle('/css/bootstrap.min.css');
self::addStyle('/css/bootstrap-theme.min.css');
}
function actionIndex()
{
$trips = Trips::model()->getAll(array('where' => '`owner` = ?'), array(App::$user->id));
self::render('/tracks/index.php', array(
'trips' => $trips,
));
}
function actionEdit()
{
$id = intval(App::getParam('id'));
if (!$id) {
App::error404();
}
$trip = Trips::model()->getByPK($id);
if ($trip->isEmpty() || $trip->owner != App::$user->id) {
App::error404();
}
self::render('/tracks/edit.php', array(
'trip' => $trip,
'tracks' => Tracks::model()->getAll(array('where' => '`tripId` = ' . $trip->id)),
'waypoints' => WayPoints::model()->getAll(array('where' => '`tripId` = ' . $trip->id)),
));
}
function actionFiles()
{
$files = TrackFiles::model()->getAll(array('where' => '`owner` = :owner'), array('owner' => App::$user->id));
self::render('/tracks/files.php', array(
'files' => $files,
));
}
function actionUploadfile()
{
$temp = pathinfo($_FILES['file']['name']);
$filename = $_FILES['file']['name'];
$extension = strtolower($temp['extension']);
$uploaddir = App::getBasedir() . 'tracks/';
$supportedExtensions = array(TrackFiles::FORMAT_GPX, TrackFiles::FORMAT_KML, TrackFiles::FORMAT_KMZ);
if (!in_array(strtoupper($extension), $supportedExtensions)) {
App::error404('Неизвестный тип');
}
$tmp = TrackFiles::model()->add(array(
'owner' => App::$user->id,
'name' => 'Трек ' . date('d-m-Y H:i:s'),
'filename' => $filename,
'format' => strtoupper($extension),
'createdAt' => date('c'),
));
$newName = 'track' . $tmp->id . '.' . $extension;
if (!move_uploaded_file($_FILES['file']['tmp_name'], $uploaddir . $newName)) {
App::error404();
}
App::redirect('/tracks/import/id/' . $tmp->id);
}
function actionGetFile()
{
$id = intval(App::getParam('id'));
if (!$id) {
App::error404();
}
$trackFile = TrackFiles::model()->getByPK($id);
if ($trackFile->isEmpty() || $trackFile->owner != App::$user->id) {
App::error404();
}
self::renderPartial('file.php', array(
'filePath' => App::getBasedir() . 'tracks/track' . $id . '.' . strtolower($trackFile->format),
'fileName' => $trackFile->filename,
));
}
function actionImport()
{
$id = (int) App::getParam('id');
if (!$id) {
App::error404();
}
$trackFile = TrackFiles::model()->getByPK($id);
if ($trackFile->isEmpty() || $trackFile->owner != App::$user->id) {
App::error404();
}
switch ($trackFile->format) {
case TrackFiles::FORMAT_GPX:
self::importGpx($trackFile);
break;
case TrackFiles::FORMAT_KML:
case TrackFiles::FORMAT_KMZ:
self::importKml($trackFile);
break;
default:
App::error404();
break;
}
App::redirect('/tracks');
}
private static function importGpx($trackFile)
{
$gpxObject = simplexml_load_file(App::getBasedir() . 'tracks/track' . $trackFile->id . '.' . strtolower($trackFile->format));
$trip = Trips::model()->add(
array(
'name' => isset($gpxObject->metadata->name) ? (string) $gpxObject->metadata->name : 'Track',
'createdAt' => isset($gpxObject->metadata->time) ? $gpxObject->metadata->time : date('c'),
'owner' => App::$user->id,
'distance' => 0,
'description' => 'Трек создан ' . date('d-m-Y H:i') . ' из файла ' . $trackFile->filename,
'status' => Trips::STATUS_PRIVATE,
)
);
$distance = 0;
$wayPoints = array();
$tracks = array();
$latlngs = array();
$tracksCounter = 1;
$tracksCount = $tptCount = $wptCount = 0;
foreach ($gpxObject as $key => $block) {
if ($key == 'wpt') {
if (empty($block)) {
continue;
}
$wayPoint = WayPoints::model()->add(
array(
'tripId' => $trip->id,
'lat' => floatval($block['lat']),
'lng' => floatval($block['lon']),
'alt' => 0,
'speed' => 0,
'createdAt' => $block->time,
'name' => (string) $block->name,
'description' => (string) $block->desc,
)
);
$wptCount++;
}
if ($key == 'trk') {
if (empty($block->trkseg)) {
continue;
}
foreach ($block->trkseg as $trkpt) {
if ($trkpt) {
$track = Tracks::model()->add(
array(
'tripId' => $trip->id,
'name' => (string) $block->name ? (string) $block->name : 'Фрагмент трека №' . $tracksCounter,
'distance' => 0,
'description' => '',
)
);
$distanceTmp = 0;
$prevPoint = array();
$tracksCounter++;
$trackPoints = array();
foreach ($trkpt as $tpt) {
$trackPoint = TrackPoints::model()->add(
array(
'trackId' => $track->id,
'lat' => floatval($tpt['lat']),
'lng' => floatval($tpt['lon']),
'alt' => isset($tpt->ele) ? floatval($tpt->ele) : 0,
'createdAt' => isset($tpt->time) ? $tpt->time : date('c'),
'speed' => 0,
)
);
if ($prevPoint) {
$distanceTmp += Geo::distance($prevPoint, array('lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lon'])));
}
$prevPoint = array(
'lat' => floatval($tpt['lat']),
'lng' => floatval($tpt['lon']),
);
$tptCount++;
}
$distance += $distanceTmp;
$track->distance = $distanceTmp;
$track->save();
}
}
$tracksCount++;
}
}
$trip->distance = $distance;
$trip->save();
$trackFile->tracksCount = $tracksCount;
$trackFile->tptCount = $tptCount;
$trackFile->wptCount = $wptCount;
$trackFile->distance = $distance;
$trackFile->save();
}
private static function importKml($trackFile)
{
$kml = new Kml(App::getBasedir() . 'tracks/track' . $trackFile->id . '.' . strtolower($trackFile->format));
$trip = Trips::model()->add(
array(
'name' => $kml->getName(),
'createdAt' => $kml->getTime(),
'owner' => App::$user->id,
'distance' => 0,
'description' => 'Трек создан ' . date('d-m-Y H:i') . ' из файла ' . $trackFile->filename,
'status' => Trips::STATUS_PRIVATE,
)
);
$distance = 0;
$tracksCounter = 1;
$tracksCount = $tptCount = $wptCount = 0;
foreach ($kml->getWaypoints() as $wpt) {
$wayPoint = WayPoints::model()->add(
array(
'tripId' => $trip->id,
'lat' => floatval($wpt['latlng']['lat']),
'lng' => floatval($wpt['latlng']['lng']),
'alt' => floatval($wpt['latlng']['alt']),
'speed' => 0,
'createdAt' => $wpt['when'],
'name' => (string) $wpt['name'],
'description' => (string) $wpt['description'],
)
);
$wptCount++;
}
foreach ($kml->getTracks() as $trackPart) {
if ($trackPart) {
$track = Tracks::model()->add(
array(
'tripId' => $trip->id,
'name' => $trackPart['name'] ? $trackPart['name'] : 'Фрагмент трека №' . $tracksCounter,
'distance' => 0,
'description' => $trackPart['description'],
)
);
$distanceTmp = 0;
$prevPoint = array();
$tracksCounter++;
$trackPoints = array();
foreach ($trackPart['latlng'] as $tpt) {
$trackPoint = TrackPoints::model()->add(
array(
'trackId' => $track->id,
'lat' => floatval($tpt['lat']),
'lng' => floatval($tpt['lng']),
'alt' => floatval($tpt['alt']),
'createdAt' => 0,
'speed' => 0,
)
);
if ($prevPoint) {
$distanceTmp += Geo::distance($prevPoint, array('lat' => floatval($tpt['lat']), 'lng' => floatval($tpt['lng'])));
}
$prevPoint = array(
'lat' => floatval($tpt['lat']),
'lng' => floatval($tpt['lng']),
);
$tptCount++;
}
$distance += $distanceTmp;
$track->distance = $distanceTmp;
$track->save();
}
}
$trip->distance = $distance;
$trip->save();
$trackFile->tracksCount = count($kml->getTracks());
$trackFile->tptCount = $tptCount;
$trackFile->wptCount = $wptCount;
$trackFile->distance = $distance;
$trackFile->save();
}
}

21
models/TrackFiles.php Normal file
View File

@ -0,0 +1,21 @@
<?php
class TrackFiles extends Model
{
const FORMAT_GPX = 'GPX';
const FORMAT_KML = 'KML';
const FORMAT_KMZ = 'KMZ';
function __construct($fromArray = array())
{
$this->_tableName_ = 'trackFiles';
parent::__construct($fromArray);
}
static function model()
{
return new self();
}
}

17
models/TrackPoints.php Normal file
View File

@ -0,0 +1,17 @@
<?php
class TrackPoints extends Model
{
function __construct($fromArray = array())
{
$this->_tableName_ = 'trackPoints';
parent::__construct($fromArray);
}
static function model()
{
return new self();
}
}

21
models/Tracks.php Normal file
View File

@ -0,0 +1,21 @@
<?php
class Tracks extends Model
{
function __construct($fromArray = array())
{
$this->_tableName_ = 'tracks';
parent::__construct($fromArray);
}
static function model()
{
return new self();
}
function distance()
{
return Geo::formatedDistance($this->distance);
}
}

25
models/Trips.php Normal file
View File

@ -0,0 +1,25 @@
<?php
class Trips extends Model
{
const STATUS_PRIVATE = 'PRIVATE';
const STATUS_PUBLIC = 'PUBLIC';
const STATUS_DELETED = 'DELETED';
function __construct($fromArray = array())
{
$this->_tableName_ = 'trips';
parent::__construct($fromArray);
}
static function model()
{
return new self();
}
function distance()
{
return Geo::formatedDistance($this->distance);
}
}

17
models/WayPoints.php Normal file
View File

@ -0,0 +1,17 @@
<?php
class WayPoints extends Model
{
function __construct($fromArray = array())
{
$this->_tableName_ = 'wayPoints';
parent::__construct($fromArray);
}
static function model()
{
return new self();
}
}

BIN
public/ico/marker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -37,6 +37,13 @@ categories.simplepoint = L.icon({
popupAnchor: [7, 7]
});
categories.marker = L.icon({
iconUrl: '/ico/marker.png',
iconSize: [32, 37],
iconAnchor: [16, 37],
popupAnchor: [0, 1]
});
// -----------------------------------------------------------------------------
if (pointLat == pointLng) {
@ -234,6 +241,10 @@ function initPoints(useBounds)
.setContent('Координаты: N°' + getURLParameter('lat').substring(0,8) + ' E°' + getURLParameter('lng').substring(0,8))
.openOn(myMap);
}
} else if (getURLParameter('track') != 'null')
{
showTrack(getURLParameter('track'))
$('#trackShowHide' + getURLParameter('track')).prop('checked', true);
}
}
});
@ -256,12 +267,13 @@ function loadPointsInBounds()
bounds: bounds
},
success: function (data) {
if (typeof (data) != 'undefined' && data.length > 0) {
for(var i=0; i < data.length; i++) {
if (typeof points[data[i].id] == 'undefined') {
markers.addLayer(constructPoint(data[i]));
}
}
myMap.addLayer(markers);
}
}
});

118
public/js/tracks.js Normal file
View File

@ -0,0 +1,118 @@
var trips = new Object;
function showHideTrack(id)
{
if($('#trackShowHide'+id).prop('checked')) {
showTrack(id)
} else {
hideTrack(id)
}
}
function showTrack(id)
{
if (typeof (trips[id]) == 'undefined') {
loadTrack(id)
} else {
if (trips[id].status != 'show') {
trips[id].addTo(myMap)
trips[id].status = 'show';
}
myMap.fitBounds(trips[id].getBounds());
return true;
}
}
function hideTrack(id)
{
if (typeof (trips[id]) == 'undefined') {
return true;
} else {
if (trips[id].status == 'show') {
myMap.removeLayer(trips[id]);
}
trips[id].status = 'hide';
return true;
}
}
function loadTrack(id)
{
colors = ['#ff0000', '#ff8e00', '#ff0', '#0f0', '#003cff', '#ff00ad'];
colorIndex = 0;
tracks = new Array();
markers = new Array();
$.ajax({
url: "/json/track/id/" + id,
success: function (data) {
trips[id] = L.featureGroup();
if (typeof (data.tracks) != 'undefined') {
for (var i = 0; i < data.tracks.length; i++) {
color = colors[colorIndex];
colorIndex = (colorIndex == colors.length-1) ? 0 : colorIndex+1;
tracks[i] = L.polyline(data.tracks[i].latlngs, {color: color, width: 4, opacity: 0.8});
tracks[i].bindPopup(data.tracks[i].name);
trips[id].addLayer(tracks[i])
}
}
if (typeof (data.wayPoints) != 'undefined') {
for (var i = 0; i < data.wayPoints.length; i++) {
markers[i] = L.marker([data.wayPoints[i]['lat'], data.wayPoints[i]['lng']], {icon: categories.marker, title: data.wayPoints[i]['name']})
markers[i].bindPopup('<b>' + data.wayPoints[i]['name'] + '</b><br/>' + data.wayPoints[i]['description']);
trips[id].addLayer(markers[i])
}
}
showTrack(id)
},
error: function (data) {
alert('Не удалось загрузить трек. Возможно это приватный трек и вы не его владелец.')
}
});
}
function loadSimpleTrack(id)
{
colors = ['red', 'green', 'blue', 'orange'];
colors = ['red'];
colorIndex = 0;
tracks = new Array();
markers = new Array();
$.ajax({
url: "/json/simpletrack/id/" + id,
success: function (data) {
trips[id] = L.featureGroup();
if (typeof (data.tracks) != 'undefined') {
for (var i = 0; i < data.tracks.length; i++) {
color = colors[colorIndex];
colorIndex = (colorIndex == colors.length-1) ? 0 : colorIndex+1;
tracks[i] = L.polyline(data.tracks[i].latlngs, {color: color, width: 4, opacity: 0.8});
tracks[i].bindPopup(data.tracks[i].name);
trips[id].addLayer(tracks[i])
}
}
if (typeof (data.wayPoints) != 'undefined') {
for (var i = 0; i < data.wayPoints.length; i++) {
markers[i] = L.marker([data.wayPoints[i]['lat'], data.wayPoints[i]['lng']], {icon: categories.marker, title: data.wayPoints[i]['name']})
markers[i].bindPopup('<b>' + data.wayPoints[i]['name'] + '</b><br/>' + data.wayPoints[i]['description']);
trips[id].addLayer(markers[i])
}
}
showTrack(id)
},
error: function (data) {
alert('Не удалось загрузить трек. Возможно это приватный трек и вы не его владелец.')
}
});
}

0
tracks/.gitKeep Normal file
View File

20
views/file.php Normal file
View File

@ -0,0 +1,20 @@
<?php
if (file_exists($filePath)) {
if (ob_get_level()) {
ob_end_clean();
}
if (!isset($fileName)) {
$fileName = basename($filePath);
}
header('Connection: close');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $fileName);
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
// header('Transfer-Encoding: chunked');
// Читаем файл и отправляем его пользователю
readfile($filePath);
}

View File

@ -18,8 +18,9 @@ if (App::$user) {
<?php if (App::$user): ?>
<div class="glyphicon _t" id="addPointButton" title="Добавить точку" onclick="<?= $addWithoutConfirm ? 'activateAddPoint()' : 'showPointagreement()'; ?>"><img src="/img/buttons/addPoint.png" style="margin-top: -10px; height: 30px;" /></div>
<div class="glyphicon glyphicon-star _c boxButtonFavorites" onclick="openBox('Favorites')" title="Хочу посетить"></div>
<div class="glyphicon _b glyphicon-cloud-download boxButtonRoutes" onclick="openBox('Routes')" title="Мои маршруты"></div>
<div class="glyphicon _c glyphicon-star boxButtonFavorites" onclick="openBox('Favorites')" title="Хочу посетить"></div>
<div class="glyphicon _c glyphicon-cloud-download boxButtonRoutes" onclick="openBox('Routes')" title="Мои маршруты"></div>
<div class="glyphicon _b glyphicon-road boxButtonTracks" onclick="openBox('Tracks')" title="Мои маршруты"></div>
<?php else: ?>
<div class="glyphicon" id="addPointButton" onclick="$('#loginModal').modal('show');" title="Добавить точку"><img src="/img/buttons/addPoint.png" style="margin-top: -10px; height: 30px;" /></div>
<?php endif; ?>
@ -103,6 +104,32 @@ if (App::$user) {
<?php endif; ?>
</div>
</div>
<div id="boxTracks">
<h3>
Мои треки
<a href="/tracks/" title="Управление треками"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span></a>
</h3>
<hr/>
<div id="tracks">
<?php if ($tracks): ?>
<?php foreach ($tracks as $track) : ?>
<div style="border-bottom: solid 1px #CCC">
<input type="checkbox" id="trackShowHide<?= $track->id ?>" onchange="showHideTrack(<?= $track->id ?>)" />
<?= $track->name ?>
<b><?= $track->distance(); ?></b>
<?php if($track->status == Trips::STATUS_PUBLIC):?>
<a href="/?track=<?= $track->id ?>" title="Ссылка на трек. <?="\n"?>[клик правой кнопкой -> копировать адрес]">
<span class="glyphicon glyphicon-link" aria-hidden="true"></span>
</a>
<?php endif;?>
</div>
<?php endforeach; ?>
<?php else: ?>
У вас пока нет сохраненных треков.
Загляните в раздел управления треками <a href="/tracks/" title="Управление треками"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span></a>.
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<?= App::$user ? '' : self::renderPartial('modals/login.php', null, true) ?>

156
views/tracks/edit.php Normal file
View File

@ -0,0 +1,156 @@
<a href="/tracks/" class="btn btn-success pull-right">
<span class="glyphicon glyphicon-home" aria-hidden="true"></span>
Выйти из редактора
</a>
<a href="/?track=<?= $trip->id ?>" class="btn btn-success pull-right"><span class="glyphicon glyphicon-globe" aria-hidden="true"></span></a>
<h3 onclick="$('#tripBlock').toggle(100)" style="cursor: pointer;">Свойства трека</h3>
<form onfocusout="saveChangesTrip()" id="tripBlock">
<div class="form-group">
<label for="tripName">Название трека</label>
<input type="text" class="form-control" id="tripName" placeholder="Великолепное путешествие <?= date('Y') ?>" value="<?= $trip->name ?>">
</div>
<div class="form-group">
<label for="tripDescription">Описание Трека</label>
<input type="text" class="form-control" id="tripDescription" placeholder="Это было незабываемое путешествие!..." value="<?= $trip->description ?>">
</div>
<a href="/edittrack/delete/id/<?= $trip->id ?>" class="btn btn-danger pull-right" onclick="return confirm('Вы уверены, что хотите удалить трек?')">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
Удалить трек
</a>
<div class="checkbox">
<label>
<input type="checkbox" id="tripIsPublic" <?= ($trip->status == Trips::STATUS_PUBLIC) ? 'checked="checked"' : '' ?>> Публичный трек (его могут видеть другие пользователи)
</label>
</div>
<span id="tripSavedOK" style="color: #999; display: none;">изменения сохранены...</span>
<span id="tripSavedError" style="color: #900; display: none;">при сохранении возникла ошибка!</span>
<br/>
</form>
<?php if ($tracks): ?>
<h3 onclick="$('#tracksBlock').toggle()" style="cursor: pointer;">Фрагменты трека (<?= count($tracks) ?>)</h3>
<div id="tracksBlock" style="display: none;">
<!--<hr/>-->
<?php foreach ($tracks as $track): ?>
<form onfocusout="saveChangesTrack(<?= $track->id ?>)" class="form-horizontal">
<div class="form-group">
<div style="font-size: 20px" class="col-sm-1"><?= $track->distance() ?></div>
<div class="col-sm-2">
<input type="text" class="form-control" id="trackName<?= $track->id ?>" placeholder="День №__" value="<?= $track->name ?>">
</div>
<div class="col-sm-8">
<input type="text" class="form-control" id="trackDescription<?= $track->id ?>" placeholder="В этот день..." value="<?= $track->description ?>">
</div>
<div class="col-sm-1">
<a href="/edittrack/deletetrack/id/<?= $track->id ?>" class="btn btn-danger" onclick="return confirm('Вы уверены, что хотите удалить фрагмент трека?')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
</div>
</div>
</form>
<!--<hr/>-->
<?php endforeach; ?>
<span id="trackSavedOK" style="color: #999; display: none;">изменения сохранены...</span>
<span id="trackSavedError" style="color: #900; display: none;">при сохранении возникла ошибка!</span>
<br/>
</div>
<?php endif; ?>
<?php if ($waypoints): ?>
<h3 onclick="$('#waypointsBlock').toggle()" style="cursor: pointer;">Путевые точки (<?= count($waypoints) ?>)</h3>
<div id="waypointsBlock" style="display: none;">
<!--<hr/>-->
<?php foreach ($waypoints as $waypoint): ?>
<form onfocusout="saveChangesWaypoint(<?= $waypoint->id ?>)" class="form-horizontal">
<div class="form-group">
<div class="col-sm-3">
<input type="text" class="form-control" id="waypointName<?= $waypoint->id ?>" placeholder="А здесь..." value="<?= $waypoint->name ?>">
</div>
<div class="col-sm-8">
<input type="text" class="form-control" id="waypointDescription<?= $waypoint->id ?>" placeholder="Здесь очень..." value="<?= $waypoint->description ?>">
</div>
<div class="col-sm-1">
<a href="/edittrack/deletewaypoint/id/<?= $waypoint->id ?>" class="btn btn-danger" onclick="return confirm('Вы уверены, что хотите удалить путевую точку?')"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></a>
</div>
</div>
</form>
<!--<hr/>-->
<?php endforeach; ?>
<span id="waypointSavedOK" style="color: #999; display: none;">изменения сохранены...</span>
<span id="waypointSavedError" style="color: #900; display: none;">при сохранении возникла ошибка!</span>
</div>
<?php endif; ?>
<script lang="javascript">
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();
}
});
}
</script>

27
views/tracks/files.php Normal file
View File

@ -0,0 +1,27 @@
<h3>Ваши исходные файлы с треками</h3>
<a href="/tracks/"> вернуться к трекам</a>
<hr/>
<?php
if ($files) {
foreach ($files as $file): ?>
<?= $file->id ?>
&nbsp;
<a href="/tracks/getfile/id/<?= $file->id ?>" title="Скачать файл">
<span class="glyphicon glyphicon-download" aria-hidden="true"></span>
</a>
&nbsp;&nbsp;
<a href="/tracks/import/id/<?= $file->id ?>" title="Импортировать ещё раз">
<span class="glyphicon glyphicon-retweet" aria-hidden="true"></span>
</a>
&nbsp;
<?= $file->name ?>
<?= $file->filename ?>
[<?= $file->tracksCount?> . <?= $file->tptCount ?> . <?= $file->wptCount ?>]
<br/>
<?php endforeach;
} else {
print 'Пока ничего не загружено';
}

39
views/tracks/index.php Normal file
View File

@ -0,0 +1,39 @@
<table style="width: 100%;">
<tr>
<td>
<h3>Ваши треки</h3>
</td>
<td style="text-align: right;">
<form method="POST" action="/tracks/uploadfile/" enctype="multipart/form-data" class="form-inline">
<div class="form-group">
<label for="uploadfile">Загрузить новый файл</label>
<input type="file" name="file" id="uploadfile" class="form-control" accept=".gpx, .kml, .kmz"/>
<button type="submit" class="btn btn-success" title="Загрузить выбранный файл"><span class="glyphicon glyphicon-upload" aria-hidden="true"></span></button>
&nbsp;
<a href="/tracks/files" class="btn btn-default" title="Раздел с файлами треков"><span class="glyphicon glyphicon-download" aria-hidden="true"></span></a>
</div>
</form>
</td>
</tr>
</table>
<hr/>
<?php
if ($trips) {
foreach ($trips as $trip):
?>
<a href="/tracks/edit/id/<?= $trip->id ?>" class="btn btn-default">
<span class="glyphicon glyphicon-pencil" aria-hidden="true" title="Редактировать трек"></span>
</a>
<?= $trip->id ?>
<a href="/?track=<?= $trip->id ?>"><?= $trip->name ?></a> (<?= $trip->createdAt ?>)
<?php if($trip->status == Trips::STATUS_PUBLIC):?>
<span class="glyphicon glyphicon-eye-open" style="color: #aaa;" aria-hidden="true" title="Публичный трек. Его могут видеть другие пользователи."></span>
<?php endif;?>
<br/>
<?php
endforeach;
} else {
print 'Пока ничего не загружено. В заголовке справа представлена форма для загрузки файлов треков.';
}