public function testGetAddress() { $lat = "-37.13009600"; $lon = "145.07711000"; $Place = new Place($lat, $lon); $this->assertInternalType("array", $Place->getAddress()); }
public function testGetWeather() { $lat = "-37.13009600"; $lon = "145.07711000"; $Place = new Place($lat, $lon); $weather = $Place->getWeatherForecast(); $this->assertInternalType("array", $weather); $this->assertCount(14, $weather); }
/** * Get the street address of this event if applicable * @since Version 3.10.0 * @return string */ public function getAddress() { if (!empty($this->meta['address'])) { return $this->meta['address']; } if (!$this->Place instanceof Place) { return; } if ($this->Place instanceof Place) { $this->meta['address'] = $this->Place->getAddress(); $this->commit(); return $this->meta['address']; } }
/** * Constructor * @param string $code */ public function __construct($code) { parent::__construct(); $this->code = $code; $this->url = "/locations/" . strtolower($this->code); /** * Record this in the debug log */ if (function_exists("debug_recordInstance")) { debug_recordInstance(__CLASS__); } /** * Start the debug timer */ if (RP_DEBUG) { global $site_debug; $debug_timer_start = microtime(true); } /** * Fetch the WOE (Where On Earth) data from Yahoo */ $woe = Place::getWOEData(strtoupper($code)); /** * End the debug timer */ if (RP_DEBUG) { $site_debug[] = __CLASS__ . "::" . __FUNCTION__ . "() : fetched WOE data from Yahoo in " . round(microtime(true) - $debug_timer_start, 5) . "s"; } if (isset($woe['places']['place'][0]['name'])) { $woe = $woe['places']['place'][0]; $this->name = $woe['name']; if (isset($woe['country attrs'])) { $this->code = $woe['country attrs']['code']; $this->url = "/locations/" . strtolower($this->code); } $this->centre = new stdClass(); $this->centre->lat = $woe['centroid']['latitude']; $this->centre->lon = $woe['centroid']['longitude']; $this->boundingBox = new stdClass(); $this->boundingBox->northEast = new stdClass(); $this->boundingBox->northEast->lat = $woe['boundingBox']['northEast']['latitude']; $this->boundingBox->northEast->lon = $woe['boundingBox']['northEast']['longitude']; $this->boundingBox->southWest = new stdClass(); $this->boundingBox->southWest->lat = $woe['boundingBox']['southWest']['latitude']; $this->boundingBox->southWest->lon = $woe['boundingBox']['southWest']['longitude']; if (isset($woe['timezone'])) { $this->timezone = $woe['timezone']; } } }
/** * Constructor * * @param string $code */ public function __construct($code) { parent::__construct(); $this->code = $code; $this->url = new Url("/locations/" . strtolower($this->code)); $countries = ISO_3166::get_countries(); if (strlen($this->code) == 2) { $this->name = $countries[$code]['name']; } else { foreach ($countries as $cc => $data) { if (strtolower($data['name']) == strtolower($this->code)) { $this->code = $cc; $this->url = new Url("/locations/" . strtolower($this->code)); $this->name = $data['name']; } } } Debug::RecordInstance(); $timer = Debug::GetTimer(); if (!$this->loadFromCache() || empty($this->name)) { $woe = Place::getWOEData(strtoupper($code)); if (isset($woe['places']['place'][0]['name'])) { $woe = $woe['places']['place'][0]; $data = ["point" => new Zend_Db_Expr(sprintf("GeomFromText('POINT(%s %s)')", $woe['centroid']['latitude'], $woe['centroid']['longitude'])), "bb_southwest" => new Zend_Db_Expr(sprintf("GeomFromText('POINT(%s %s)')", $woe['boundingBox']['southWest']['latitude'], $woe['boundingBox']['southWest']['longitude'])), "bb_northeast" => new Zend_Db_Expr(sprintf("GeomFromText('POINT(%s %s)')", $woe['boundingBox']['northEast']['latitude'], $woe['boundingBox']['northEast']['longitude'])), "country_code" => $woe['country attrs']['code'], "country_name" => $woe['name'], "timezone" => isset($woe['timezone']) ? $woe['timezone'] : ""]; $this->db->insert("geoplace", $data); $this->name = $woe['name']; $this->centre = new stdClass(); $this->centre->lat = $woe['centroid']['latitude']; $this->centre->lon = $woe['centroid']['longitude']; $this->boundingBox = new stdClass(); $this->boundingBox->northEast = new stdClass(); $this->boundingBox->northEast->lat = $woe['boundingBox']['northEast']['latitude']; $this->boundingBox->northEast->lon = $woe['boundingBox']['northEast']['longitude']; $this->boundingBox->southWest = new stdClass(); $this->boundingBox->southWest->lat = $woe['boundingBox']['southWest']['latitude']; $this->boundingBox->southWest->lon = $woe['boundingBox']['southWest']['longitude']; } } /** * Fetch the WOE (Where On Earth) data from Yahoo */ Debug::LogEvent(__METHOD__, $timer); }
/** * Get the geoplace for this photo * @since Version 3.10.0 * @return \Railpage\Place */ public function getPlace() { if (is_null($this->lat) || is_null($this->lon)) { return false; } return Place::Factory($this->lat, $this->lon); }
/** * Constructor * @param string $country * @param string $region */ public function __construct($country, $region = false) { parent::__construct(); /** * Record this in the debug log */ if (function_exists("debug_recordInstance")) { debug_recordInstance(__CLASS__); } /** * Start the debug timer */ if (RP_DEBUG) { global $site_debug; $debug_timer_start = microtime(true); } /** * Fetch the WOE (Where On Earth) data from Yahoo */ if ($region == false && !preg_match("@[a-zA-Z]+@", $country)) { // Assume a WOE ID $woe = Place::getWOEData($country); } else { $woe = Place::getWOEData($region . ", " . strtoupper($country)); } /** * End the debug timer */ if (RP_DEBUG) { $site_debug[] = __CLASS__ . "::" . __FUNCTION__ . "() : fetched WOE data from Yahoo in " . round(microtime(true) - $debug_timer_start, 5) . "s"; } if (isset($woe['places']['place'][0]['name'])) { $row = $woe['places']['place'][0]; $this->slug = $region; $this->Country = new Country($country); } elseif (isset($woe['place'])) { $row = $woe['place']; $this->slug = $this->makeRegionSlug($row['name']); } if (isset($row)) { if (empty($this->Country->name) && !preg_match("@[a-zA-Z]+@", $country) && isset($row['country'])) { $this->Country = new Country($row['country']); } $this->name = $row['name']; $this->url = $this->Country->url . "/" . $this->slug; $this->centre = new stdClass(); $this->centre->lat = $row['centroid']['latitude']; $this->centre->lon = $row['centroid']['longitude']; $this->boundingBox = new stdClass(); $this->boundingBox->northEast = new stdClass(); $this->boundingBox->northEast->lat = $row['boundingBox']['northEast']['latitude']; $this->boundingBox->northEast->lon = $row['boundingBox']['northEast']['longitude']; $this->boundingBox->southWest = new stdClass(); $this->boundingBox->southWest->lat = $row['boundingBox']['southWest']['latitude']; $this->boundingBox->southWest->lon = $row['boundingBox']['southWest']['longitude']; if (isset($row['timezone'])) { $this->timezone = $row['timezone']; } } }
/** * Populate this object with pre-existing data * @since Version 3.2 * @return boolean */ public function fetch() { if (!filter_var($this->id, FILTER_VALIDATE_INT)) { throw new Exception("Cannot fetch sighting - no ID given"); } $mckey = sprintf("railpage:sighting=%d", $this->id); if (!($row = $this->Memcached->fetch($mckey))) { $query = "SELECT s.id, s.date, s.date_added, s.lat, s.lon, s.text, s.user_id, s.timezone, u.username, s.loco_ids, s.meta\n FROM sighting AS s\n LEFT JOIN nuke_users AS u ON s.user_id = u.user_id\n WHERE s.id = ?"; $row = $this->db->fetchRow($query, $this->id); $this->Memcached->save($mckey, $row, 0); } if (!isset($row) || !is_array($row)) { return false; } $this->lat = $row['lat']; $this->lon = $row['lon']; $this->text = $row['text']; $this->user_id = $row['user_id']; $this->username = $row['username']; $this->timezone = $row['timezone']; $this->meta = json_decode($row['meta'], true); $this->loco_ids = json_decode($row['loco_ids'], true); $this->date = new DateTime($row['date'], new DateTimeZone($this->timezone)); $this->date_added = new DateTime($row['date_added'], new DateTimeZone($this->timezone)); $this->url = new Url("/sightings/view/" . $this->id); $this->Place = Place::Factory($this->lat, $this->lon); return true; }
/** * Get photos for a given location * * This function uses mysqli::multi_query() as it was buggering up all subsequent SQL queries on the page * @since Version 3.0 * @version 3.7.5 * @param int $num * @param int $start */ public function getPhotosForSite($num = 10, $start = 0) { $Place = new Place($this->lat, $this->lon); return $Place->getPhotosFromSphinx($num); if (!$this->id || !$this->db) { return false; } $cachekey = sprintf("%s;photos;num=%s;start=%s", $this->mckey, intval($num), intval($start)); if ($result = $this->Memcached->fetch($cachekey)) { return $result; } $return = array(); // Ditch the stored procedure. Just do it through the database connection $lat = $this->lat; $lon = $this->lon; $query = "SELECT flickr_geodata.*, 3956 * 2 * ASIN(SQRT(POWER(SIN((" . $lat . " - flickr_geodata.lat) * pi() / 180 / 2), 2) + COS(" . $lat . " * pi() / 180) * COS(" . $lat . " * pi() / 180) * POWER(SIN((" . $lon . " - flickr_geodata.lon) * pi() / 180 / 2), 2))) AS distance \r\n FROM flickr_geodata, location \r\n WHERE location.id = " . $this->id . "\r\n AND flickr_geodata.lon BETWEEN (\r\n " . $lon . " - " . $this->photoRadius . " / abs(cos(radians(" . $lat . ")) * 69)\r\n ) AND (\r\n " . $lon . " + " . $this->photoRadius . " / abs(cos(radians(" . $lat . ")) * 69)\r\n )\r\n AND flickr_geodata.lat BETWEEN (\r\n " . $lat . " - (" . $this->photoRadius . " / 69) \r\n ) AND (\r\n " . $lat . " + (" . $this->photoRadius . " / 69) \r\n )\r\n HAVING distance < " . $this->photoRadius . "\r\n ORDER BY distance\r\n LIMIT ?"; $params = array($num); $return = array(); $square_size = 180; foreach ($this->db->fetchAll($query, $params) as $data) { $key = $data['photo_id']; $return[$key]['size_sq'] = RP_PROTOCOL . "://" . filter_input(INPUT_SERVER, "HTTP_HOST", FILTER_SANITIZE_STRING) . "/image_resize.php?q=90&w=" . $square_size . "&h=" . $square_size . "&square=true&image=" . str_replace("?zz=1", "", $data['size4']); $return[$key]['size_sq_w'] = $square_size; $return[$key]['size_sq_h'] = $square_size; $data['id'] = $data['photo_id']; $data['url_sq'] = $data['size0']; $data['width_sq'] = $data['size0_w']; $data['height_sq'] = $data['size0_h']; $data['url_t'] = $data['size1']; $data['width_t'] = $data['size1_w']; $data['height_t'] = $data['size1_h']; $data['url_s'] = $data['size2']; $data['width_s'] = $data['size2_w']; $data['height_s'] = $data['size2_h']; $data['url_q'] = NULL; $data['width_q'] = NULL; $data['height_q'] = NULL; $data['url_m'] = $data['size3']; $data['width_m'] = $data['size3_w']; $data['height_m'] = $data['size3_h']; $data['url_n'] = $data['size6']; $data['width_n'] = $data['size6_w']; $data['height_n'] = $data['size6_h']; $data['url_z'] = $data['size4']; $data['width_z'] = $data['size4_w']; $data['height_z'] = $data['size4_h']; $data['url_l'] = $data['size5']; $data['width_l'] = $data['size5_w']; $data['height_l'] = $data['size5_h']; $data['url_c'] = $data['size7']; $data['width_c'] = $data['size7_w']; $data['height_c'] = $data['size7_h']; $data['url_o'] = $data['size8']; $data['width_c'] = $data['size8_w']; $data['height_c'] = $data['size8_h']; $data['nicetags'] = explode(" ", $data['tags']); $return[$key] = $data; } $this->Memcached->save($cachekey, $return, strtotime("+1 day")); return $return; }
/** * Populate this image with fresh data * * @since Version 3.8.7 * @return $this * * @param boolean $force * @param int $option * * @throws \Exception if the photo cannot be found on the image provider * @todo Split this into utility functions */ public function populate($force = false, $option = null) { if ($force === false && !$this->isStale()) { return $this; } Debug::LogCLI("Fetching data from " . $this->provider . " for image ID " . $this->id . " (photo ID " . $this->photo_id . ")"); /** * Start the debug timer */ if (RP_DEBUG) { global $site_debug; $debug_timer_start = microtime(true); } /** * New and improved populator using image providers */ $Provider = $this->getProvider(); $data = false; try { $data = $Provider->getImage($this->photo_id, $force); } catch (Exception $e) { $expected = array(sprintf("Unable to fetch data from Flickr: Photo \"%s\" not found (invalid ID) (1)", $this->photo_id), "Unable to fetch data from Flickr: Photo not found (1)"); if (in_array($e->getMessage(), $expected)) { $where = ["image_id = ?" => $this->id]; $this->db->delete("image_link", $where); $where = ["id = ?" => $this->id]; $this->db->delete("image", $where); throw new Exception("Photo no longer available from " . $this->provider); } } if ($data) { $this->sizes = $data['sizes']; $this->title = empty($data['title']) ? "Untitled" : $data['title']; $this->description = $data['description']; $this->meta = array("dates" => array("posted" => $data['dates']['uploaded'] instanceof DateTime ? $data['dates']['uploaded']->format("Y-m-d H:i:s") : $data['dates']['uploaded']['date'], "taken" => $data['dates']['taken'] instanceof DateTime ? $data['dates']['taken']->format("Y-m-d H:i:s") : $data['dates']['taken']['date'])); $this->author = new stdClass(); $this->author->username = $data['author']['username']; $this->author->realname = !empty($data['author']['realname']) ? $data['author']['realname'] : $data['author']['username']; $this->author->id = $data['author']['id']; $this->author->url = "https://www.flickr.com/photos/" . $this->author->id; if ($user_id = UserUtility::findFromFlickrNSID($this->author->id)) { $data['author']['railpage_id'] = $user_id; } if (isset($data['author']['railpage_id']) && filter_var($data['author']['railpage_id'], FILTER_VALIDATE_INT)) { $this->author->User = UserFactory::CreateUser($data['author']['railpage_id']); } /** * Load the tags */ if (isset($data['tags']) && is_array($data['tags']) && count($data['tags'])) { foreach ($data['tags'] as $row) { $this->meta['tags'][] = $row['raw']; } } /** * Load the Place object */ if ($option != Images::OPT_NOPLACE && isset($data['location']) && !empty($data['location'])) { try { $this->Place = Place::Factory($data['location']['latitude'], $data['location']['longitude']); } catch (Exception $e) { // Throw it away. Don't care. } } $this->links = new stdClass(); $this->links->provider = isset($data['urls']['url'][0]['_content']) ? $data['urls']['url'][0]['_content'] : $data['urls'][key($data['urls'])]; $this->commit(); $this->cacheGeoData(); return true; } /** * Fetch data in various ways for different photo providers */ switch ($this->provider) { /** * Picasa */ case "picasaweb": if (empty($this->meta) && !is_null(filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_URL)) && strpos(filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_URL), "picasaweb.google.com")) { $album = preg_replace("@(http|https)://picasaweb.google.com/([a-zA-Z\\-\\.]+)/(.+)@", "\$2", filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_URL)); if (is_string($album)) { $update_url = sprintf("https://picasaweb.google.com/data/feed/api/user/%s/photoid/%s?alt=json", $album, $this->photo_id); } } if (isset($update_url)) { $data = file_get_contents($update_url); $json = json_decode($data, true); $this->meta = array("title" => $json['feed']['subtitle']['$t'], "description" => $json['feed']['title']['$t'], "dates" => array("posted" => date("Y-m-d H:i:s", $json['feed']['gphoto$timestamp']['$t'])), "sizes" => array("original" => array("width" => $json['feed']['gphoto$width']['$t'], "height" => $json['feed']['gphoto$height']['$t'], "source" => str_replace(sprintf("/s%d/", $json['feed']['media$group']['media$thumbnail'][0]['width']), sprintf("/s%d/", $json['feed']['gphoto$width']['$t']), $json['feed']['media$group']['media$thumbnail'][0]['url'])), "largest" => array("width" => $json['feed']['gphoto$width']['$t'], "height" => $json['feed']['gphoto$height']['$t'], "source" => str_replace(sprintf("/s%d/", $json['feed']['media$group']['media$thumbnail'][0]['width']), sprintf("/s%d/", $json['feed']['gphoto$width']['$t']), $json['feed']['media$group']['media$thumbnail'][0]['url']))), "photo_id" => $json['feed']['gphoto$id']['$t'], "album_id" => $json['feed']['gphoto$albumid']['$t'], "updateurl" => sprintf("%s?alt=json", $json['feed']['id']['$t'])); foreach ($json['feed']['media$group']['media$thumbnail'] as $size) { if ($size['width'] <= 500 && $size['width'] > 200) { $this->meta['sizes']['small'] = array("width" => $size['width'], "height" => $size['height'], "source" => $size['url']); } if ($size['width'] <= 200) { $this->meta['sizes']['small'] = array("width" => $size['width'], "height" => $size['height'], "source" => $size['url']); } if ($size['width'] <= 1024 && $size['width'] > 500) { $this->meta['sizes']['large'] = array("width" => $size['width'], "height" => $size['height'], "source" => $size['url']); } } foreach ($json['feed']['link'] as $link) { if ($link['rel'] == "alternate" && $link['type'] == "text/html") { $this->meta['source'] = $link['href']; } } if ($option != Images::OPT_NOPLACE && isset($json['feed']['georss$where']['gml$Point']) && is_array($json['feed']['georss$where']['gml$Point'])) { $pos = explode(" ", $json['feed']['georss$where']['gml$Point']['gml$pos']['$t']); $this->Place = Place::Factory($pos[0], $pos[1]); } $this->title = $this->meta['title']; $this->description = $this->meta['description']; $this->author = new stdClass(); $this->author->username = $album; $this->author->id = $album; $this->author->url = sprintf("%s/%s", $json['feed']['generator']['uri'], $album); } $this->sizes = $this->meta['sizes']; $this->commit(); break; /** * Vicsig */ /** * Vicsig */ case "vicsig": if (strpos(filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_URL), "vicsig.net/photo")) { $this->meta['source'] = filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_STRING); $response = $this->GuzzleClient->get($this->meta['source']); if ($response->getStatusCode() != 200) { throw new Exception(sprintf("Failed to fetch image data from %s: HTTP error %s", $this->provider, $response->getStatusCode())); } /** * Start fetching it */ $data = $response->getBody(); $doc = new DomDocument(); $doc->loadHTML($data); $images = $doc->getElementsByTagName("img"); foreach ($images as $element) { if (!empty($element->getAttribute("src")) && !empty($element->getAttribute("alt"))) { #$image_title = $element->getAttribute("alt"); $this->sizes['original'] = array("source" => $element->getAttribute("src"), "width" => $element->getAttribute("width"), "height" => $element->getAttribute("height")); if (substr($this->sizes['original']['source'], 0, 1) == "/") { $this->sizes['original']['source'] = "http://www.vicsig.net" . $this->sizes['original']['source']; } break; } } $desc = $doc->getElementsByTagName("i"); foreach ($desc as $element) { if (!isset($image_desc)) { $text = trim($element->nodeValue); $text = str_replace("\r\n", "\n", $text); $text = explode("\n", $text); /** * Loop through the exploded text and remove the obvious date/author/etc */ foreach ($text as $k => $line) { // Get the author if (preg_match("@Photo: @i", $line)) { $this->author = new stdClass(); $this->author->realname = str_replace("Photo: ", "", $line); $this->author->url = filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_STRING); unset($text[$k]); } // Get the date try { $this->meta['dates']['posted'] = (new DateTime($line))->format("Y-m-d H:i:s"); unset($text[$k]); } catch (Exception $e) { // Throw it away } } /** * Whatever's left must be the photo title and description */ foreach ($text as $k => $line) { if (empty($this->title)) { $this->title = $line; continue; } $this->description .= $line; } $this->links = new stdClass(); $this->links->provider = filter_input(INPUT_SERVER, "HTTP_REFERER", FILTER_SANITIZE_STRING); $this->commit(); } } } break; } /** * End the debug timer */ if (RP_DEBUG) { $site_debug[] = __CLASS__ . "::" . __FUNCTION__ . "() : completed in " . round(microtime(true) - $debug_timer_start, 5) . "s"; } return $this; }
/** * Get the regions in the locations database. * If $country is not specified, it will return all regions for all countries * @since Version 3.0 * @param string $country An optional two letter country code we want to search for * @return array */ public function getRegions($country = false) { $timer = Debug::GetTimer(); $return = false; $mckey = $country ? "railpage:locations.regions.country=" . $country : "railpage:locations.regions"; if ($return = $this->Memcached->fetch($mckey)) { return $return; } $return = array(); if ($country) { foreach ($this->db->fetchAll("SELECT DISTINCT region FROM location WHERE country = ? AND active = 1 ORDER BY region ASC", $country) as $row) { $woe = Place::getWOEData($country); if (isset($woe['places']['place'][0])) { $return[$country]['woe'] = $woe['places']['place'][0]; } $datarow = array("region" => $row['region'], "url" => $this->makeRegionPermalink($country, $row['region']), "count" => $this->db->fetchOne("SELECT COUNT(id) FROM location WHERE country = ? AND region = ?", array($country, $row['region']))); $woe = Place::getWOEData($row['region'] . "," . $country); if (isset($woe['places']['place'][0])) { $datarow['woe'] = $woe['places']['place'][0]; } $return[$country]['children'][] = $datarow; } $this->Memcached->save($mckey, $return, strtotime("+1 day")); Debug::LogEvent(__METHOD__ . "(" . $country . ")", $timer); return $return; } $query = "SELECT DISTINCT l.region, l.country, g.country_name, g.region_name \n FROM location AS l \n LEFT JOIN geoplace AS g ON l.geoplace = g.id \n WHERE l.active = 1 \n GROUP BY l.country \n ORDER BY l.region DESC"; foreach ($this->db->fetchAll($query) as $row) { if (empty($row['country'])) { continue; } $return[$row['country']]['woe'] = array("country" => $row['country_name']); if (empty($return[$row['country']]['woe']['country'])) { $woe = Place::getWOEData(strtoupper($row['region'])); $return[$row['country']]['woe'] = array("country" => $woe['places']['place'][0]['country']); } $return[$row['country']]['children'][] = $row['region']; } // Cache it $this->Memcached->save($mckey, $return, strtotime("+1 day")); Debug::LogEvent(__METHOD__ . "(" . $country . ")", $timer); return $return; }
/** * Get the WoE for this place * @since Version 3.9.1 * * @param string $country * @param string $region * * @return array */ private function fetchWoE($country, $region) { if ($region === false && !preg_match("@[a-zA-Z]+@", $country)) { // Assume a WOE ID $woe = Place::getWOEData($country); } else { $woe = Place::getWOEData($region . ", " . strtoupper($country)); } if (isset($woe['places']['place'][0]['name'])) { $this->slug = $region; $this->Country = new Country($country); return $woe['places']['place'][0]; } if (isset($woe['place'])) { $this->slug = $this->makeRegionSlug($woe['place']['name']); return $woe['place']; } return $woe; }
/** * Get important EXIF information from the image * @since Version 3.10.0 * @return array * @param \Railpage\Gallery\Image $imageObject */ public static function PopulateExif($imageObject) { $imageSource = Album::ALBUMS_DIR . $imageObject->path; /** * Read the IPTC data */ #$size = getimagesize($imageSource, $info); if (is_array($info)) { $iptc = iptcparse($info["APP13"]); if (isset($iptc['2#005'])) { $imageObject->title = $iptc['2#005'][0]; } } /** * Read the EXIF data */ $exif = exif_read_data($imageSource, 0, true); if (isset($exif['IFD0']['ImageDescription'])) { $imageObject->caption = $exif['IFD0']['ImageDescription']; } if (isset($exif['EXIF']['DateTimeOriginal'])) { $imageObject->DateTaken = new DateTime($exif['EXIF']['DateTimeOriginal']); } if (isset($exif['GPS']['GPSLatitude']) && isset($exif['GPS']['GPSLongitude'])) { $lat = self::getGps($exif['GPS']['GPSLatitude'], $exif['GPS']['GPSLatitudeRef']); $lon = self::getGps($exif['GPS']['GPSLongitude'], $exif['GPS']['GPSLongitudeRef']); $imageObject->Place = Place::Factory($lat, $lon); } return $imageObject; }