From b2a24fb0b142c765e3d07977006b3a7ef6fdd086 Mon Sep 17 00:00:00 2001 From: BatPio Date: Tue, 29 Aug 2017 19:58:22 +0100 Subject: [PATCH 01/12] Photos lat/long scanning --- appinfo/application.php | 14 ++ appinfo/database.xml | 35 +++++ appinfo/info.xml | 3 + appinfo/routes.php | 5 +- lib/Command/RescanPhotos.php | 77 +++++++++++ lib/Controller/PhotosController.php | 44 +++++++ lib/DB/Geophoto.php | 29 +++++ lib/DB/GeophotoMapper.php | 46 +++++++ lib/Hook/FileHooks.php | 89 +++++++++++++ lib/Service/GeophotoService.php | 87 +++++++++++++ lib/Service/PhotofilesService.php | 190 ++++++++++++++++++++++++++++ 11 files changed, 618 insertions(+), 1 deletion(-) create mode 100644 lib/Command/RescanPhotos.php create mode 100644 lib/Controller/PhotosController.php create mode 100644 lib/DB/Geophoto.php create mode 100644 lib/DB/GeophotoMapper.php create mode 100644 lib/Hook/FileHooks.php create mode 100644 lib/Service/GeophotoService.php create mode 100644 lib/Service/PhotofilesService.php diff --git a/appinfo/application.php b/appinfo/application.php index bf55beee0..d129f72fc 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -15,6 +15,8 @@ use OC\AppFramework\Utility\SimpleContainer; use \OCP\AppFramework\App; use OCA\Maps\Controller\PageController; +use OCA\Maps\Hook\FileHooks; +use OCA\Maps\Service\PhotofilesService; class Application extends App { @@ -22,5 +24,17 @@ public function __construct (array $urlParams=array()) { parent::__construct('maps', $urlParams); $container = $this->getContainer(); + + $this->getContainer()->registerService('FileHooks', function($c) { + return new FileHooks( + $c->query('ServerContainer')->getRootFolder(), + \OC::$server->query(PhotofilesService::class), + $c->query('ServerContainer')->getLogger(), + $c->query('AppName') + ); + }); + + $this->getContainer()->query('FileHooks')->register(); } + } diff --git a/appinfo/database.xml b/appinfo/database.xml index 0a092fcb3..a475c2444 100644 --- a/appinfo/database.xml +++ b/appinfo/database.xml @@ -65,4 +65,39 @@ + + *dbprefix*maps_photos + + + id + integer + 0 + true + 1 + 41 + + + user_id + text + true + 64 + + + file_id + integer + true + 10 + + + lat + float + true + + + lng + float + true + + +
diff --git a/appinfo/info.xml b/appinfo/info.xml index a44a326ae..31e79a8e8 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -20,4 +20,7 @@ maps.page.index + + OCA\Maps\Command\RescanPhotos + diff --git a/appinfo/routes.php b/appinfo/routes.php index 47100e439..cb4a3513d 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,6 +10,9 @@ return [ 'routes' => [ ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], - ['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'], + ['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'], + + //photos + ['name' => 'photos#getPhotosFromDb', 'url' => '/photos/all', 'verb' => 'GET'], ] ]; diff --git a/lib/Command/RescanPhotos.php b/lib/Command/RescanPhotos.php new file mode 100644 index 000000000..aa54d15dc --- /dev/null +++ b/lib/Command/RescanPhotos.php @@ -0,0 +1,77 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\Command; + +use OCP\Encryption\IManager; +use OCP\Files\NotFoundException; +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +use OCA\Maps\Service\PhotofilesService; + +class RescanPhotos extends Command { + + protected $userManager; + + protected $output; + + protected $encryptionManager; + + private $photofilesService; + + public function __construct(IUserManager $userManager, + IManager $encryptionManager, + PhotofilesService $photofilesService) { + parent::__construct(); + $this->userManager = $userManager; + $this->encryptionManager = $encryptionManager; + $this->photofilesService = $photofilesService; + } + protected function configure() { + $this->setName('maps:scan-photos') + ->setDescription('Rescan photos GPS exif data') + ->addArgument( + 'user_id', + InputArgument::OPTIONAL, + 'Rescan photos GPS exif data for the given user' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + if ($this->encryptionManager->isEnabled()) { + $output->writeln('Encryption is enabled. Aborted.'); + return 1; + } + $this->output = $output; + $userId = $input->getArgument('user_id'); + if ($userId === null) { + $this->userManager->callForSeenUsers(function (IUser $user) { + $this->rescanUserPhotos($user->getUID()); + }); + } else { + $user = $this->userManager->get($userId); + if ($user !== null) { + $this->rescanUserPhotos($userId); + } + } + return 0; + } + + private function rescanUserPhotos($userId) { + $this->photofilesService->rescan($userId); + } +} \ No newline at end of file diff --git a/lib/Controller/PhotosController.php b/lib/Controller/PhotosController.php new file mode 100644 index 000000000..878b26b4f --- /dev/null +++ b/lib/Controller/PhotosController.php @@ -0,0 +1,44 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\Controller; + +use OCP\IRequest; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Controller; +use OCP\ILogger; + +use OCA\Maps\Service\GeophotoService; + +class PhotosController extends Controller { + private $userId; + private $geophotoService; + private $logger; + + public function __construct($AppName, ILogger $logger, IRequest $request, GeophotoService $GeophotoService, $UserId){ + parent::__construct($AppName, $request); + $this->logger = $logger; + $this->userId = $UserId; + $this->geophotoService = $GeophotoService; + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function getPhotosFromDb() { + $result = $this->geophotoService->getAllFromDB($this->userId); + return new DataResponse($result); + } + +} diff --git a/lib/DB/Geophoto.php b/lib/DB/Geophoto.php new file mode 100644 index 000000000..993fa54a2 --- /dev/null +++ b/lib/DB/Geophoto.php @@ -0,0 +1,29 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\DB; + +use OCP\AppFramework\Db\Entity; + +class Geophoto extends Entity { + + protected $fileId; + protected $lat; + protected $lng; + protected $userId; + + public function __construct() { + $this->addType('fileId', 'integer'); + $this->addType('lat', 'float'); + $this->addType('lng', 'float'); + } +} \ No newline at end of file diff --git a/lib/DB/GeophotoMapper.php b/lib/DB/GeophotoMapper.php new file mode 100644 index 000000000..d17e6e9c1 --- /dev/null +++ b/lib/DB/GeophotoMapper.php @@ -0,0 +1,46 @@ + + * @copyright Piotr Bator 2017 + */ + + namespace OCA\Maps\DB; + +use OCP\IDBConnection; +use OCP\AppFramework\Db\Mapper; + +class GeophotoMapper extends Mapper { + + public function __construct(IDBConnection $db) { + parent::__construct($db, 'maps_photos'); + } + + public function find($id) { + $sql = 'SELECT * FROM `*PREFIX*maps_photos` ' . + 'WHERE `id` = ?'; + return $this->findEntity($sql, [$id]); + } + + + public function findAll($userId, $limit=null, $offset=null) { + $sql = 'SELECT * FROM `*PREFIX*maps_photos` where `user_id` = ?'; + return $this->findEntities($sql, [$userId], $limit, $offset); + } + + public function deleteByFileId($fileId) { + $sql = 'DELETE FROM `*PREFIX*maps_photos` where `file_id` = ?'; + return $this->execute($sql, [$fileId]); + } + + public function deleteAll($userId) { + $sql = 'DELETE FROM `*PREFIX*maps_photos` where `user_id` = ?'; + return $this->execute($sql, [$userId]); + } + +} diff --git a/lib/Hook/FileHooks.php b/lib/Hook/FileHooks.php new file mode 100644 index 000000000..e5ef124d7 --- /dev/null +++ b/lib/Hook/FileHooks.php @@ -0,0 +1,89 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\Hook; + +use OC\Files\Filesystem; +use OC\Files\View; +use OCP\Files\FileInfo; +use OCP\ILogger; +use OCP\Files\Node; +use OCP\Files\IRootFolder; +use OCP\Util; + +use OCA\Maps\Service\PhotofilesService; + +/** + * Handles files events + */ +class FileHooks { + + private $photofilesService; + + private $logger; + + private $root; + + public function __construct(IRootFolder $root, PhotofilesService $photofilesService, ILogger $logger, $appName) { + $this->photofilesService = $photofilesService; + $this->logger = $logger; + $this->root = $root; + } + + public function register() { + $fileWriteCallback = function(\OCP\Files\Node $node) { + if($this->isUserNode($node)) { + $this->photofilesService->addByFile($node); + } + }; + $this->root->listen('\OC\Files', 'postWrite', $fileWriteCallback); + + $fileDeletionCallback = function(\OCP\Files\Node $node) { + if($this->isUserNode($node)) { + if ($node->getType() === FileInfo::TYPE_FOLDER) { + $this->photofilesService->deleteByFolder($node); + } else { + $this->photofilesService->deleteByFile($node); + } + } + }; + $this->root->listen('\OC\Files', 'preDelete', $fileDeletionCallback); + + Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $this, 'restore'); + } + + public static function restore($params) { + $node = $this->getNodeForPath($params['filePath']); + if($this->isUserNode($node)) { + if ($node->getType() === FileInfo::TYPE_FOLDER) { + $this->photofilesService->addByFolder($node); + } else { + $this->photofilesService->addByFile($node); + } + } + } + + private function getNodeForPath($path) { + $user = \OC::$server->getUserSession()->getUser(); + $fullPath = Filesystem::normalizePath('/' . $user->getUID() . '/files/' . $path); + return $this->root->get($fullPath); + } + + /** + * Ugly Hack, find API way to check if file is added by user. + */ + private function isUserNode(\OCP\Files\Node $node) { + //return strpos($node->getStorage()->getId(), "home::", 0) === 0; + return $node->getStorage()->instanceOfStorage('\OC\Files\Storage\Home'); + } + +} \ No newline at end of file diff --git a/lib/Service/GeophotoService.php b/lib/Service/GeophotoService.php new file mode 100644 index 000000000..e8fa0f78f --- /dev/null +++ b/lib/Service/GeophotoService.php @@ -0,0 +1,87 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\Service; + +use OCP\Files\FileInfo; +use OCP\IL10N; +use OCP\Files\IRootFolder; +use OCP\Files\Storage\IStorage; +use OCP\Files\Folder; +use OCP\ILogger; + +use OCA\Maps\DB\Geophoto; +use OCA\Maps\DB\GeophotoMapper; + +class GeophotoService { + + private $l10n; + private $root; + private $photoMapper; + private $logger; + + public function __construct (ILogger $logger, IRootFolder $root, IL10N $l10n, GeophotoMapper $photoMapper) { + $this->root = $root; + $this->l10n = $l10n; + $this->photoMapper = $photoMapper; + $this->logger = $logger; + } + + /** + * @param string $userId + * @return array with all notes in the current directory + */ + public function getAllFromDB ($userId) { + $photoEntities = $this->photoMapper->findAll($userId); + $userFolder = $this->getFolderForUser($userId); + $filesById = []; + $foldersById = []; + foreach ($photoEntities as $photoEntity) { + $path = \OC\Files\Filesystem::getPath($photoEntity->getFileId()); + $photoFile = \OC\Files\Filesystem::getFileInfo($path); + $photoFolder = $userFolder->get($path)->getParent(); + $file_object = new \stdClass(); + $file_object->fileId = $photoEntity->getFileId(); + $file_object->lat = $photoEntity->getLat(); + $file_object->lng = $photoEntity->getLng(); + $file_object->folderId = $photoFolder->getId(); + $file_object->path = $this->normalizePath($photoFile); + $filesById[] = $file_object; + $folder_object = new \stdClass(); + $folder_object->id = $photoFolder->getId(); + $folder_object->name = $photoFolder->getName(); + $folder_object->path = $this->normalizePath($photoFolder); + /*$folder_object->filesList = $this->getPhotosListForFolder($photoFolder);*/ + $foldersById[$photoFolder->getId()] = $folder_object; + } + return [$filesById, $foldersById]; + } + + private function normalizePath($node) { + return str_replace("files","", $node->getInternalPath()); + } + + /** + * @param string $userId the user id + * @return Folder + */ + private function getFolderForUser ($userId) { + $path = '/' . $userId . '/files'; + if ($this->root->nodeExists($path)) { + $folder = $this->root->get($path); + } else { + $folder = $this->root->newFolder($path); + } + return $folder; + } + +} diff --git a/lib/Service/PhotofilesService.php b/lib/Service/PhotofilesService.php new file mode 100644 index 000000000..c33ed81c6 --- /dev/null +++ b/lib/Service/PhotofilesService.php @@ -0,0 +1,190 @@ + + * @copyright Piotr Bator 2017 + */ + +namespace OCA\Maps\Service; + +use OCP\Files\FileInfo; +use OCP\IL10N; +use OCP\Files\IRootFolder; +use OCP\Files\Storage\IStorage; +use OCP\Files\Folder; +use OCP\Files\Node; +use OCP\ILogger; + +use OCA\Maps\DB\Geophoto; +use OCA\Maps\DB\GeophotoMapper; + +class PhotofilesService { + + private $l10n; + private $root; + private $photoMapper; + private $logger; + + public function __construct (ILogger $logger, IRootFolder $root, IL10N $l10n, GeophotoMapper $photoMapper) { + $this->root = $root; + $this->l10n = $l10n; + $this->photoMapper = $photoMapper; + $this->logger = $logger; + } + + public function rescan ($userId){ + $userFolder = $this->root->getUserFolder($userId); + $photos = $this->gatherPhotoFiles($userFolder, true); + $this->photoMapper->deleteAll($userId); + foreach($photos as $photo) { + $this->addPhoto($photo, $userId); + } + } + + public function addByFile(Node $file) { + $userFolder = $this->root->getUserFolder($file->getOwner()->getUID()); + if($this->isPhoto($file)) { + $this->addPhoto($file, $file->getOwner()->getUID()); + } + } + + public function addByFolder(Node $folder) { + $photos = $this->gatherPhotoFiles($folder, true); + foreach($photos as $photo) { + $this->addPhoto($photo, $folder->getOwner()->getUID()); + } + } + + public function deleteByFile(Node $file) { + $this->photoMapper->deleteByFileId($file->getId()); + } + + public function deleteByFolder(Node $folder) { + $photos = $this->gatherPhotoFiles($folder, true); + foreach($photos as $photo) { + $this->photoMapper->deleteByFileId($photo->getId()); + } + } + + private function addPhoto($photo, $userId) { + $exif = $this->getExif($photo); + if (!is_null($exif) AND !is_null($exif->lat)) { + $photoEntity = new Geophoto(); + $photoEntity->setFileId($photo->getId()); + $photoEntity->setLat($exif->lat); + $photoEntity->setLng($exif->lng); + $photoEntity->setUserId($userId); + $this->photoMapper->insert($photoEntity); + } + } + + private function normalizePath($node) { + return str_replace("files","", $node->getInternalPath()); + } + + public function getPhotosByFolder($userId, $path) { + $userFolder = $this->root->getUserFolder($userId); + $folder = $userFolder->get($path); + return $this->getPhotosListForFolder($folder); + } + + private function getPhotosListForFolder($folder) { + $FilesList = $this->gatherPhotoFiles($folder, false); + $notes = []; + foreach($FilesList as $File) { + $file_object = new \stdClass(); + $file_object->fileId = $File->getId(); + $file_object->path = $this->normalizePath($File); + $notes[] = $file_object; + } + return $notes; + } + + private function gatherPhotoFiles ($folder, $recursive) { + $notes = []; + $nodes = $folder->getDirectoryListing(); + foreach($nodes as $node) { + if($node->getType() === FileInfo::TYPE_FOLDER AND $recursive) { + $notes = array_merge($notes, $this->gatherPhotoFiles($node, $recursive)); + continue; + } + if($this->isPhoto($node)) { + $notes[] = $node; + } + } + return $notes; + } + + private function isPhoto($file) { + $allowedExtensions = ['jpg', 'jpeg']; + + if($file->getType() !== \OCP\Files\FileInfo::TYPE_FILE) return false; + if(!in_array( + pathinfo($file->getName(), PATHINFO_EXTENSION), + $allowedExtensions + )) return false; + + return true; + } + + private function hasExifGeoTags($exif) { + if (!isset($exif["GPSLatitude"]) OR !isset($exif["GPSLongitude"])) { + return false; + } + if (count($exif["GPSLatitude"]) != 3 OR count($exif["GPSLongitude"]) != 3) { + return false; + } + return true; + } + + private function getExif($file) { + $path = $file->getStorage()->getLocalFile($file->getInternalPath()); + $exif = @exif_read_data($path); + //Check photos are on the earth + if($this->hasExifGeoTags($exif) AND $exif["GPSLatitude"][0]<90 AND $exif["GPSLongitude"][0]<180){ + + //Check photos are not on NULL island, remove if they should be. + if($exif["GPSLatitude"][0]!=0 OR $exif["GPSLatitude"][1]!=0 OR $exif["GPSLongitude"][0]!=0 OR $exif["GPSLongitude"][1]!=0){ + //Check if there is exif infor + $LatM = 1; $LongM = 1; + if($exif["GPSLatitudeRef"] == 'S'){ + $LatM = -1; + } + if($exif["GPSLongitudeRef"] == 'W'){ + $LongM = -1; + } + //get the GPS data + $gps['LatDegree']=$exif["GPSLatitude"][0]; + $gps['LatMinute']=$exif["GPSLatitude"][1]; + $gps['LatgSeconds']=$exif["GPSLatitude"][2]; + $gps['LongDegree']=$exif["GPSLongitude"][0]; + $gps['LongMinute']=$exif["GPSLongitude"][1]; + $gps['LongSeconds']=$exif["GPSLongitude"][2]; + + //convert strings to numbers + foreach($gps as $key => $value){ + $pos = strpos($value, '/'); + if($pos !== false){ + $temp = explode('/',$value); + $gps[$key] = $temp[0] / $temp[1]; + } + } + $file_object = new \stdClass(); + //calculate the decimal degree + $file_object->lat = $LatM * ($gps['LatDegree'] + ($gps['LatMinute'] / 60) + ($gps['LatgSeconds'] / 3600)); + $file_object->lng = $LongM * ($gps['LongDegree'] + ($gps['LongMinute'] / 60) + ($gps['LongSeconds'] / 3600)); + if (isset($exif["DateTimeOriginal"])) { + $file_object->dateTaken = strtotime($exif["DateTimeOriginal"]); + } + return $file_object; + } + } + return null; + } + +} From 7df4ffe94e10afb4bcc90d197fed27777b691741 Mon Sep 17 00:00:00 2001 From: BatPio Date: Wed, 30 Aug 2017 22:40:37 +0100 Subject: [PATCH 02/12] Showing photos on map --- css/style.css | 44 +++++++++++++++++ js/photosController.js | 99 +++++++++++++++++++++++++++++++++++++ js/script.js | 4 ++ package.json | 1 + templates/content/index.php | 1 + templates/index.php | 1 + 6 files changed, 150 insertions(+) create mode 100644 js/photosController.js diff --git a/css/style.css b/css/style.css index a5a2c4943..351101b4a 100644 --- a/css/style.css +++ b/css/style.css @@ -105,3 +105,47 @@ tr.selected td { .leaflet-popup-content-wrapper { border-radius: 3px !important; } + +.leaflet-marker-photo { + border: 2px solid #fff; + box-shadow: 3px 3px 10px #888; +} + +.leaflet-marker-photo.photo-marker{ + top: -10px; +} + +.leaflet-marker-photo.photo-marker:after { + content:""; + position: relative; + bottom: 16px; + border-width: 10px 10px 0; + border-style: solid; + border-color: #fff transparent; + display: block; + width: 0; + margin-left: auto; + margin-right: auto; +} + +.leaflet-marker-photo .thumbnail { + width: 100%; + height: 100%; + background-size: cover; + background-position: center center; + background-repeat: no-repeat; +} + +.leaflet-marker-photo .label { + position: absolute; + top: -7px; + right: -11px; + color: #fff; + background-color: #333; + border-radius: 9px; + height: 18px; + min-width: 18px; + line-height: 12px; + text-align: center; + padding: 3px; +} \ No newline at end of file diff --git a/js/photosController.js b/js/photosController.js new file mode 100644 index 000000000..a011afdaf --- /dev/null +++ b/js/photosController.js @@ -0,0 +1,99 @@ +function PhotosController () { + this.PHOTO_MARKER_VIEW_SIZE = 40; +} + +PhotosController.prototype = { + + appendToMap : function(map) { + this.map = map; + this.photoLayer = L.markerClusterGroup({ + iconCreateFunction : this.createClusterView, + showCoverageOnHover : false, + maxClusterRadius: this.PHOTO_MARKER_VIEW_SIZE + 10, + icon: { + iconSize: [this.PHOTO_MARKER_VIEW_SIZE, this.PHOTO_MARKER_VIEW_SIZE] + } + }); + this.photoLayer.on('click', this.onPhotoViewClick); + this.photoLayer.addTo(this.map); + }, + + onPhotoViewClick : function(evt) { + evt.layer.bindPopup(L.Util.template('', evt.layer.data), { + className: 'leaflet-popup-photo', + minWidth: 400 + }).openPopup(); + }, + + createClusterView : function(cluster) { + var thumbnailUrl = cluster.getAllChildMarkers()[0].data.thumbnail; + var label = cluster.getChildCount(); + console.log(thumbnailUrl); + return new L.DivIcon(L.extend({ + className: 'leaflet-marker-photo cluster-marker', + html: '
' + label + '' + }, this.icon)); + }, + + createPhotoView: function(photo) { + return L.divIcon(L.extend({ + html: '
​', + className: 'leaflet-marker-photo photo-marker' + }, photo, { + iconSize: [this.PHOTO_MARKER_VIEW_SIZE, this.PHOTO_MARKER_VIEW_SIZE], + iconAnchor: [this.PHOTO_MARKER_VIEW_SIZE / 2, this.PHOTO_MARKER_VIEW_SIZE] + })); + }, + + addPhotosToMap : function(photos) { + var markers = this.preparePhotoMarkers(photos); + this.photoLayer.addLayers(markers); + }, + + preparePhotoMarkers : function(photos) { + var markers = []; + for (var i = 0; i < photos.length; i++) { + var markerData = { + lat: photos[i].lat, + lng: photos[i].lng, + url: this.generateImageUrl(photos[i].path), + thumbnail: this.generateThumbnailUrl(photos[i].path), + albumId: photos[i].folderId + }; + var marker = L.marker(markerData, { + icon: this.createPhotoView(markerData) + }); + marker.data = markerData; + markers.push(marker); + } + return markers; + }, + + callForImages: function() { + $.ajax({ + 'url' : OC.generateUrl('apps/maps/photos/all'), + 'type': 'GET', + 'success': (function(_controller) { + return function(response) { + if (response[0].length == 0) { + //showNoPhotosMessage(); + } else { + _controller.addPhotosToMap(response[0]); + } + } + })(this) + }); + }, + + /* Preview size 32x32 is used in files view, so it sould be generated */ + generateThumbnailUrl: function (filename) { + return "/index.php/core/preview.png?file=" + escape(filename) + "&x=32&y=32"; + }, + + /* Preview size 375x211 is used in files details view */ + generateImageUrl: function (filename) { + return "/index.php/core/preview.png?file=" + escape(filename) + "&x=375&y=211"; + } + +}; + diff --git a/js/script.js b/js/script.js index 16458f574..0f170ce9a 100644 --- a/js/script.js +++ b/js/script.js @@ -1,6 +1,8 @@ (function($, OC) { $(function() { mapController.initMap(); + photosController.appendToMap(mapController.map); + photosController.callForImages(); // Popup $(document).on('click', '#opening-hours-header', function() { @@ -65,6 +67,8 @@ } }; + var photosController = new PhotosController(); + var searchController = { isGeocodeabe: function(str) { var pattern = /^\s*\d+\.?\d*\,\s*\d+\.?\d*\s*$/; diff --git a/package.json b/package.json index 8c78a2fa5..15c6e9a5f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "gulp": "^3.9.1", "gulp-cli": "^1.4.0", "leaflet": "^1.2.0", + "leaflet.markercluster": "^1.1.0", "opening_hours": "^3.5.0" } } diff --git a/templates/content/index.php b/templates/content/index.php index 9a436e2ea..32ab20555 100644 --- a/templates/content/index.php +++ b/templates/content/index.php @@ -20,6 +20,7 @@ style('maps', '../node_modules/leaflet/dist/leaflet'); script('maps', '../node_modules/leaflet/dist/leaflet'); +script('maps', '../node_modules/leaflet.markercluster/dist/leaflet.markercluster'); script('maps', '../node_modules/opening_hours/opening_hours'); ?>