Browse Source

add static maps api

main
Aaron Parecki 8 years ago
parent
commit
2ff4606b23
68 changed files with 590 additions and 4 deletions
  1. +3
    -1
      composer.json
  2. +1
    -1
      composer.lock
  3. +1
    -1
      controllers/main.php
  4. +515
    -0
      p3k/geo/StaticMap.php
  5. +61
    -0
      p3k/geo/WebMercator.php
  6. BIN
      p3k/map/images/dot-large-blue.png
  7. BIN
      p3k/map/images/dot-large-gray.png
  8. BIN
      p3k/map/images/dot-large-green.png
  9. BIN
      p3k/map/images/dot-large-orange.png
  10. BIN
      p3k/map/images/dot-large-pink.png
  11. BIN
      p3k/map/images/dot-large-purple.png
  12. BIN
      p3k/map/images/dot-large-red.png
  13. BIN
      p3k/map/images/dot-large-yellow.png
  14. BIN
      p3k/map/images/dot-small-blue.png
  15. BIN
      p3k/map/images/dot-small-gray.png
  16. BIN
      p3k/map/images/dot-small-green.png
  17. BIN
      p3k/map/images/dot-small-orange.png
  18. BIN
      p3k/map/images/dot-small-pink.png
  19. BIN
      p3k/map/images/dot-small-purple.png
  20. BIN
      p3k/map/images/dot-small-red.png
  21. BIN
      p3k/map/images/dot-small-yellow.png
  22. BIN
      p3k/map/images/fb.png
  23. BIN
      p3k/map/images/google.png
  24. BIN
      p3k/map/images/large-blue-blank.png
  25. BIN
      p3k/map/images/large-blue-cutout.png
  26. BIN
      p3k/map/images/large-gray-blank.png
  27. BIN
      p3k/map/images/large-gray-cutout.png
  28. BIN
      p3k/map/images/large-gray-user.png
  29. BIN
      p3k/map/images/large-green-blank.png
  30. BIN
      p3k/map/images/large-green-cutout.png
  31. BIN
      p3k/map/images/large-orange-blank.png
  32. BIN
      p3k/map/images/large-orange-cutout.png
  33. BIN
      p3k/map/images/large-pink-blank.png
  34. BIN
      p3k/map/images/large-pink-cutout.png
  35. BIN
      p3k/map/images/large-purple-blank.png
  36. BIN
      p3k/map/images/large-purple-cutout.png
  37. BIN
      p3k/map/images/large-red-blank.png
  38. BIN
      p3k/map/images/large-red-cutout.png
  39. BIN
      p3k/map/images/large-yellow-blank.png
  40. BIN
      p3k/map/images/large-yellow-cutout.png
  41. BIN
      p3k/map/images/large-yellow-message.png
  42. BIN
      p3k/map/images/large-yellow-user.png
  43. BIN
      p3k/map/images/powered-by-esri.png
  44. BIN
      p3k/map/images/powered-by-esri.psd
  45. BIN
      p3k/map/images/small-blue-blank.png
  46. BIN
      p3k/map/images/small-blue-cutout.png
  47. BIN
      p3k/map/images/small-gray-blank.png
  48. BIN
      p3k/map/images/small-gray-cutout.png
  49. BIN
      p3k/map/images/small-gray-message.png
  50. BIN
      p3k/map/images/small-gray-user.png
  51. BIN
      p3k/map/images/small-green-blank.png
  52. BIN
      p3k/map/images/small-green-cutout.png
  53. BIN
      p3k/map/images/small-green-user.png
  54. BIN
      p3k/map/images/small-orange-blank.png
  55. BIN
      p3k/map/images/small-orange-cutout.png
  56. BIN
      p3k/map/images/small-pink-blank.png
  57. BIN
      p3k/map/images/small-pink-cutout.png
  58. BIN
      p3k/map/images/small-pink-user.png
  59. BIN
      p3k/map/images/small-purple-blank.png
  60. BIN
      p3k/map/images/small-purple-cutout.png
  61. BIN
      p3k/map/images/small-red-blank.png
  62. BIN
      p3k/map/images/small-red-cutout.png
  63. BIN
      p3k/map/images/small-yellow-blank.png
  64. BIN
      p3k/map/images/small-yellow-cutout.png
  65. BIN
      p3k/map/images/small-yellow-user.png
  66. BIN
      public/assets/sample-map.png
  67. +9
    -1
      views/layout.php
  68. +0
    -0
      views/map.php

+ 3
- 1
composer.json View File

@ -16,7 +16,9 @@
},
"files": [
"lib/config.php",
"lib/helpers.php"
"lib/helpers.php",
"p3k/geo/WebMercator.php",
"p3k/geo/StaticMap.php"
]
}
}

+ 1
- 1
composer.lock View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "08543114c4fc08343de999396b54476d",
"hash": "67cc4cf59f1b61424cfb0b5d47668c2f",
"packages": [
{
"name": "p3k/slim-savant",

+ 1
- 1
controllers/main.php View File

@ -12,6 +12,6 @@ $app->get('/map', function() use($app) {
$app->get('/map/img', function() use($app) {
$params = $app->request()->params();
$app->response['Content-type'] = 'image/png';
$assetPath = dirname(__FILE__) . '/../p3k/timezone/images';
$assetPath = dirname(__FILE__) . '/../p3k/map/images';
$map = p3k\geo\StaticMap\generate($params, null, $assetPath);
});

+ 515
- 0
p3k/geo/StaticMap.php View File

@ -0,0 +1,515 @@
<?php
namespace p3k\geo\StaticMap;
use p3k\geo\WebMercator;
use Imagick, ImagickPixel, ImagickDraw;
function generate($params, $filename, $assetPath) {
$bounds = array(
'minLat' => 90,
'maxLat' => -90,
'minLng' => 180,
'maxLng' => -180
);
// If any markers are specified, choose a default lat/lng as the center of all the markers
$markers = array();
if($markersTemp=k($params,'marker')) {
if(!is_array($markersTemp))
$markersTemp = array($markersTemp);
// If no latitude is set, use the center of all the markers
foreach($markersTemp as $i=>$m) {
if(preg_match_all('/(?P<k>[a-z]+):(?P<v>[^;]+)/', $m, $matches)) {
$properties = array();
foreach($matches['k'] as $j=>$key) {
$properties[$key] = $matches['v'][$j];
}
// Skip invalid marker definitions, show error in a header
if(array_key_exists('icon', $properties) && (
(array_key_exists('lat', $properties) && array_key_exists('lng', $properties))
|| array_key_exists('location', $properties)
)
) {
// Geocode the provided location and return lat/lng
if(array_key_exists('location', $properties)) {
$result = ArcGISGeocoder::geocode($properties['location']);
if(!$result->success) {
#header('X-Marker-' . ($i+1) . ': error geocoding location "' . $properties['location'] . '"');
continue;
}
$properties['lat'] = $result->latitude;
$properties['lng'] = $result->longitude;
}
if(preg_match('/https?:\/\/(.+)/', $properties['icon'], $match)) {
// Looks like an external image, attempt to download it
$ch = curl_init($properties['icon']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$img = curl_exec($ch);
$properties['iconImg'] = @imagecreatefromstring($img);
if(!$properties['iconImg']) {
$properties['iconImg'] = false;
}
} else {
$properties['iconImg'] = imagecreatefrompng($assetPath . '/' . $properties['icon'] . '.png');
}
if($properties['iconImg']) {
$markers[] = $properties;
}
if($properties['lat'] < $bounds['minLat'])
$bounds['minLat'] = $properties['lat'];
if($properties['lat'] > $bounds['maxLat'])
$bounds['maxLat'] = $properties['lat'];
if($properties['lng'] < $bounds['minLng'])
$bounds['minLng'] = $properties['lng'];
if($properties['lng'] > $bounds['maxLng'])
$bounds['maxLng'] = $properties['lng'];
} else {
#header('X-Marker-' . ($i+1) . ': missing icon, or lat/lng/location parameters');
}
}
}
}
$paths = array();
if($pathsTemp=k($params,'path')) {
if(!is_array($pathsTemp))
$pathsTemp = array($pathsTemp);
foreach($pathsTemp as $i=>$path) {
$properties = array();
if(preg_match_all('/(?P<k>[a-z]+):(?P<v>[^;]+)/', $path, $matches)) {
foreach($matches['k'] as $j=>$key) {
$properties[$key] = $matches['v'][$j];
}
}
// Set default color and weight if none specified
if(!array_key_exists('color', $properties))
$properties['color'] = '333333';
if(!array_key_exists('weight', $properties))
$properties['weight'] = 3;
// Now parse the points into an array
if(preg_match_all('/(?P<point>\[[0-9\.-]+,[0-9\.-]+\])/', $path, $matches)) {
$properties['path'] = json_decode('[' . implode(',', $matches['point']) . ']');
// Adjust the bounds to fit the path
foreach($properties['path'] as $point) {
if($point[1] < $bounds['minLat'])
$bounds['minLat'] = $point[1];
if($point[1] > $bounds['maxLat'])
$bounds['maxLat'] = $point[1];
if($point[0] < $bounds['minLng'])
$bounds['minLng'] = $point[0];
if($point[0] > $bounds['maxLng'])
$bounds['maxLng'] = $point[0];
}
}
if(array_key_exists('path', $properties))
$paths[] = $properties;
}
}
$defaultLatitude = $bounds['minLat'] + (($bounds['maxLat'] - $bounds['minLat']) / 2);
$defaultLongitude = $bounds['minLng'] + (($bounds['maxLng'] - $bounds['minLng']) / 2);
if(k($params,'latitude') !== false) {
$latitude = k($params,'latitude');
$longitude = k($params,'longitude');
} elseif(k($params,'location') !== false) {
$result = ArcGISGeocoder::geocode(k($params,'location'));
if(!$result->success) {
$latitude = $defaultLatitude;
$longitude = $defaultLongitude;
#header('X-Geocode: error');
#header('X-Geocode-Result: ' . $result->raw);
} else {
$latitude = $result->latitude;
$longitude = $result->longitude;
#header('X-Geocode: success');
#header('X-Geocode-Result: ' . $latitude . ', ' . $longitude);
}
} else {
$latitude = $defaultLatitude;
$longitude = $defaultLongitude;
}
$width = k($params, 'width', 300);
$height = k($params, 'height', 300);
// If no zoom is specified, choose a zoom level that will fit all the markers and the path
if(k($params,'zoom')) {
$zoom = k($params,'zoom');
} else {
// start at max zoom level (20)
$fitZoom = 20;
$doesNotFit = true;
while($fitZoom > 1 && $doesNotFit) {
$center = webmercator\latLngToPixels($latitude, $longitude, $fitZoom);
$leftEdge = $center['x'] - $width/2;
$topEdge = $center['y'] - $height/2;
// check if the bounding rectangle fits within width/height
$sw = webmercator\latLngToPixels($bounds['minLat'], $bounds['minLng'], $fitZoom);
$ne = webmercator\latLngToPixels($bounds['maxLat'], $bounds['maxLng'], $fitZoom);
$fitHeight = abs($ne['y'] - $sw['y']);
$fitWidth = abs($ne['x'] - $sw['x']);
if($fitHeight <= $height && $fitWidth <= $width) {
$doesNotFit = false;
}
$fitZoom--;
}
$zoom = $fitZoom;
}
if(k($params,'maxzoom') && k($params,'maxzoom') < $zoom) {
$zoom = k($params,'maxzoom');
}
$tileServices = array(
'streets' => array(
'http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{Z}/{Y}/{X}'
),
'satellite' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}'
),
'hybrid' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}',
'http://server.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer/tile/{Z}/{Y}/{X}'
),
'topo' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{Z}/{Y}/{X}'
),
'gray' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{Z}/{Y}/{X}',
'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Reference/MapServer/tile/{Z}/{Y}/{X}'
),
'gray-background' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{Z}/{Y}/{X}',
),
'oceans' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{Z}/{Y}/{X}'
),
'national-geographic' => array(
'http://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{Z}/{Y}/{X}'
),
'osm' => array(
'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png'
),
'stamen-toner' => array(
'http://tile.stamen.com/toner/{Z}/{X}/{Y}.png'
),
'stamen-toner-background' => array(
'http://tile.stamen.com/toner-background/{Z}/{X}/{Y}.png'
),
'stamen-toner-lite' => array(
'http://tile.stamen.com/toner-lite/{Z}/{X}/{Y}.png'
),
'stamen-terrain' => array(
'http://tile.stamen.com/terrain/{Z}/{X}/{Y}.png'
),
'stamen-terrain-background' => array(
'http://tile.stamen.com/terrain-background/{Z}/{X}/{Y}.png'
),
'stamen-watercolor' => array(
'http://tile.stamen.com/watercolor/{Z}/{X}/{Y}.png'
)
);
if(k($params,'basemap') && k($tileServices, k($params,'basemap'))) {
$tileURL = $tileServices[k($params,'basemap')][0];
if(array_key_exists(1, $tileServices[k($params,'basemap')]))
$overlayURL = $tileServices[k($params,'basemap')][1];
else
$overlayURL = 0;
} else {
$tileURL = $tileServices['gray'][0];
$overlayURL = false;
}
function urlForTile($x, $y, $z, $tileURL) {
return str_replace(array(
'{X}', '{Y}', '{Z}'
), array(
$x, $y, $z
), $tileURL);
}
$im = imagecreatetruecolor($width, $height);
// Find the pixel coordinate of the center of the map
$center = webmercator\latLngToPixels($latitude, $longitude, $zoom);
$leftEdge = $center['x'] - $width/2;
$topEdge = $center['y'] - $height/2;
$tilePos = webmercator\pixelsToTile($center['x'], $center['y']);
// print_r($tilePos);
// echo '<br />';
$pos = webmercator\positionInTile($center['x'], $center['y']);
// print_r($pos);
// echo '<br />';
// For the given number of pixels, determine how many tiles are needed in each direction
$neTile = webmercator\pixelsToTile($center['x'] + $width/2, $center['y'] + $height/2);
// print_r($neTile);
// echo '<br />';
$swTile = webmercator\pixelsToTile($center['x'] - $width/2, $center['y'] - $height/2);
// print_r($swTile);
// echo '<br />';
// Now download all the tiles
$tiles = array();
$overlays = array();
$chs = array();
$mh = curl_multi_init();
$numTiles = 0;
for($x = $swTile['x']; $x <= $neTile['x']; $x++) {
if(!array_key_exists("$x", $tiles)) {
$tiles["$x"] = array();
$overlays["$x"] = array();
$chs["$x"] = array();
$ochs["$x"] = array();
}
for($y = $swTile['y']; $y <= $neTile['y']; $y++) {
$url = urlForTile($x, $y, $zoom, $tileURL);
$tiles["$x"]["$y"] = false;
$chs["$x"]["$y"] = curl_init($url);
curl_setopt($chs["$x"]["$y"], CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $chs["$x"]["$y"]);
if($overlayURL) {
$url = urlForTile($x, $y, $zoom, $overlayURL);
$overlays["$x"]["$y"] = false;
$ochs["$x"]["$y"] = curl_init($url);
curl_setopt($ochs["$x"]["$y"], CURLOPT_RETURNTRANSFER, TRUE);
curl_multi_add_handle($mh, $ochs["$x"]["$y"]);
}
$numTiles++;
}
}
$running = null;
// Execute the handles. Blocks until all are finished.
do {
$mrc = curl_multi_exec($mh, $running);
} while($running > 0);
// In case any of the tiles fail, they will be grey instead of throwing an error
$blank = imagecreatetruecolor(256, 256);
$grey = imagecolorallocate($im, 224, 224, 224);
imagefill($blank, 0,0, $grey);
foreach($chs as $x=>$yTiles) {
foreach($yTiles as $y=>$ch) {
$content = curl_multi_getcontent($ch);
if($content)
$tiles["$x"]["$y"] = @imagecreatefromstring($content);
else
$tiles["$x"]["$y"] = $blank;
}
}
if($overlayURL) {
foreach($ochs as $x=>$yTiles) {
foreach($yTiles as $y=>$ch) {
$content = curl_multi_getcontent($ch);
if($content)
$overlays["$x"]["$y"] = @imagecreatefromstring($content);
else
$overlays["$x"]["$y"] = $blank;
}
}
}
// Assemble all the tiles into a new image positioned as appropriate
foreach($tiles as $x=>$yTiles) {
foreach($yTiles as $y=>$tile) {
$x = intval($x);
$y = intval($y);
$ox = (($x - $tilePos['x']) * TILE_SIZE) - $pos['x'] + ($width/2);
$oy = (($y - $tilePos['y']) * TILE_SIZE) - $pos['y'] + ($height/2);
imagecopy($im, $tile, $ox,$oy, 0,0, imagesx($tile),imagesy($tile));
}
}
if($overlayURL) {
foreach($overlays as $x=>$yTiles) {
foreach($yTiles as $y=>$tile) {
$x = intval($x);
$y = intval($y);
$ox = (($x - $tilePos['x']) * TILE_SIZE) - $pos['x'] + ($width/2);
$oy = (($y - $tilePos['y']) * TILE_SIZE) - $pos['y'] + ($height/2);
imagecopy($im, $tile, $ox,$oy, 0,0, imagesx($tile),imagesy($tile));
}
}
}
if(count($paths)) {
// Draw the path with ImageMagick because GD sucks as anti-aliased lines
$mg = new Imagick();
$mg->newImage($width, $height, new ImagickPixel('none'));
$draw = new ImagickDraw();
$colors = array();
foreach($paths as $path) {
$draw->setStrokeColor(new ImagickPixel('#'.$path['color']));
$draw->setStrokeWidth($path['weight']);
$previous = false;
foreach($path['path'] as $point) {
if($previous) {
$from = webmercator\latLngToPixels($previous[1], $previous[0], $zoom);
$to = webmercator\latLngToPixels($point[1], $point[0], $zoom);
$draw->line($from['x'] - $leftEdge,$from['y']-$topEdge, $to['x']-$leftEdge,$to['y']-$topEdge);
}
$previous = $point;
}
}
$mg->drawImage($draw);
$mg->setImageFormat( "png" );
$pathImg = imagecreatefromstring($mg);
imagecopy($im, $pathImg, 0,0, 0,0, $width,$height);
}
// Add markers
foreach($markers as $marker) {
// Icons that start with 'dot-' do not have a shadow
$shadow = !preg_match('/^dot-/', $marker['icon']);
if($width < 120 || $height < 120) {
$shrinkFactor = 1.5;
} else {
$shrinkFactor = 1;
}
// Icons with a shadow are centered at the bottom middle pixel.
// Icons with no shadow are centered in the center pixel.
$px = webmercator\latLngToPixels($marker['lat'], $marker['lng'], $zoom);
$pos = array(
'x' => $px['x'] - $leftEdge,
'y' => $px['y'] - $topEdge
);
if($shrinkFactor > 1) {
$markerImg = imagecreatetruecolor(round(imagesx($marker['iconImg'])/$shrinkFactor), round(imagesy($marker['iconImg'])/$shrinkFactor));
imagealphablending($markerImg, true);
$color = imagecolorallocatealpha($markerImg, 0, 0, 0, 127);
imagefill($markerImg, 0,0, $color);
imagecopyresampled($markerImg, $marker['iconImg'], 0,0, 0,0, imagesx($markerImg),imagesy($markerImg), imagesx($marker['iconImg']),imagesy($marker['iconImg']));
} else {
$markerImg = $marker['iconImg'];
}
if($shadow) {
$iconPos = array(
'x' => $pos['x'] - round(imagesx($markerImg)/2),
'y' => $pos['y'] - imagesy($markerImg)
);
} else {
$iconPos = array(
'x' => $pos['x'] - round(imagesx($markerImg)/2),
'y' => $pos['y'] - round(imagesy($markerImg)/2)
);
}
imagecopy($im, $markerImg, $iconPos['x'],$iconPos['y'], 0,0, imagesx($markerImg),imagesy($markerImg));
}
if(k($params,'attribution') != 'none') {
$logo = imagecreatefrompng($assetPath . '/powered-by-esri.png');
// Shrink the esri logo if the image is small
if($width > 120) {
if($width < 220) {
$shrinkFactor = 2;
imagecopyresampled($im, $logo, $width-round(imagesx($logo)/$shrinkFactor)-4, $height-round(imagesy($logo)/$shrinkFactor)-4, 0,0, round(imagesx($logo)/$shrinkFactor),round(imagesy($logo)/$shrinkFactor), imagesx($logo),imagesy($logo));
} else {
imagecopy($im, $logo, $width-imagesx($logo)-4, $height-imagesy($logo)-4, 0,0, imagesx($logo),imagesy($logo));
}
}
}
#header('Cache-Control: max-age=' . (60*60*24*30) . ', public');
#header('X-Tiles-Downloaded: ' . $numTiles);
// TODO: add caching
$fmt = k($params,'format', 'png');
switch($fmt) {
case "jpg":
case "jpeg":
header('Content-type: image/jpg');
$quality = k($params, 'quality', 75);
imagejpeg($im, $filename, $quality);
break;
case "png":
default:
header('Content-type: image/png');
imagepng($im, $filename);
break;
}
imagedestroy($im);
/**
* http://msdn.microsoft.com/en-us/library/bb259689.aspx
* http://derickrethans.nl/php-mapping.html
*/
}
function k($a, $k, $default=false) {
if(is_array($a) && array_key_exists($k, $a) && $a[$k])
return $a[$k];
elseif(is_object($a) && property_exists($a, $k) && $a->$k)
return $a->$k;
else
return $default;
}

+ 61
- 0
p3k/geo/WebMercator.php View File

@ -0,0 +1,61 @@
<?php
namespace p3k\geo\WebMercator;
define('TILE_SIZE', 256);
function totalPixelsForZoomLevel($zoom) {
return pow(2, $zoom) * TILE_SIZE;
}
function lngToX($longitude, $zoom) {
return round((($longitude + 180) / 360) * totalPixelsForZoomLevel($zoom));
}
function latToY($latitude, $zoom) {
return round(((atanh(sin(deg2rad(-$latitude))) / pi()) + 1) * totalPixelsForZoomLevel($zoom - 1));
}
function latLngToPixels($latitude, $longitude, $zoom) {
return array(
'x' => lngToX($longitude, $zoom),
'y' => latToY($latitude, $zoom)
);
}
function xToLng($x, $zoom) {
return (($x * 360) / totalPixelsForZoomLevel($zoom)) - 180;
}
function yToLat($y, $zoom) {
$a = pi() * (($y / totalPixelsForZoomLevel($zoom - 1)) - 1);
return -1 * (rad2deg(asin(tanh($a))));
}
function pixelsToLatLng($x, $y, $zoom) {
return array(
'lat' => yToLat($y, $zoom),
'lng' => xToLng($x, $zoom)
);
}
function tileToPixels($x, $y) {
return array(
'x' => $x * TILE_SIZE,
'y' => $y * TILE_SIZE
);
}
function pixelsToTile($x, $y) {
return array(
'x' => floor($x / TILE_SIZE),
'y' => floor($y / TILE_SIZE)
);
}
function positionInTile($x, $y) {
$tile = pixelsToTile($x, $y);
return array(
'x' => round(TILE_SIZE * (($x / TILE_SIZE) - $tile['x'])),
'y' => round(TILE_SIZE * (($y / TILE_SIZE) - $tile['y']))
);
}

BIN
p3k/map/images/dot-large-blue.png View File

Before After
Width: 19  |  Height: 19  |  Size: 474 B

BIN
p3k/map/images/dot-large-gray.png View File

Before After
Width: 19  |  Height: 19  |  Size: 423 B

BIN
p3k/map/images/dot-large-green.png View File

Before After
Width: 19  |  Height: 19  |  Size: 453 B

BIN
p3k/map/images/dot-large-orange.png View File

Before After
Width: 19  |  Height: 19  |  Size: 464 B

BIN
p3k/map/images/dot-large-pink.png View File

Before After
Width: 19  |  Height: 19  |  Size: 449 B

BIN
p3k/map/images/dot-large-purple.png View File

Before After
Width: 19  |  Height: 19  |  Size: 478 B

BIN
p3k/map/images/dot-large-red.png View File

Before After
Width: 19  |  Height: 19  |  Size: 459 B

BIN
p3k/map/images/dot-large-yellow.png View File

Before After
Width: 19  |  Height: 19  |  Size: 413 B

BIN
p3k/map/images/dot-small-blue.png View File

Before After
Width: 13  |  Height: 12  |  Size: 380 B

BIN
p3k/map/images/dot-small-gray.png View File

Before After
Width: 14  |  Height: 12  |  Size: 347 B

BIN
p3k/map/images/dot-small-green.png View File

Before After
Width: 13  |  Height: 12  |  Size: 358 B

BIN
p3k/map/images/dot-small-orange.png View File

Before After
Width: 13  |  Height: 13  |  Size: 377 B

BIN
p3k/map/images/dot-small-pink.png View File

Before After
Width: 13  |  Height: 12  |  Size: 359 B

BIN
p3k/map/images/dot-small-purple.png View File

Before After
Width: 13  |  Height: 12  |  Size: 375 B

BIN
p3k/map/images/dot-small-red.png View File

Before After
Width: 13  |  Height: 12  |  Size: 377 B

BIN
p3k/map/images/dot-small-yellow.png View File

Before After
Width: 13  |  Height: 13  |  Size: 344 B

BIN
p3k/map/images/fb.png View File

Before After
Width: 17  |  Height: 22  |  Size: 1.5 KiB

BIN
p3k/map/images/google.png View File

Before After
Width: 56  |  Height: 34  |  Size: 2.3 KiB

BIN
p3k/map/images/large-blue-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-blue-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-gray-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.5 KiB

BIN
p3k/map/images/large-gray-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.7 KiB

BIN
p3k/map/images/large-gray-user.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-green-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-green-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-orange-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-orange-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-pink-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-pink-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-purple-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-purple-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.9 KiB

BIN
p3k/map/images/large-red-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-red-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/large-yellow-blank.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.5 KiB

BIN
p3k/map/images/large-yellow-cutout.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.6 KiB

BIN
p3k/map/images/large-yellow-message.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.9 KiB

BIN
p3k/map/images/large-yellow-user.png View File

Before After
Width: 39  |  Height: 50  |  Size: 1.8 KiB

BIN
p3k/map/images/powered-by-esri.png View File

Before After
Width: 65  |  Height: 36  |  Size: 4.2 KiB

BIN
p3k/map/images/powered-by-esri.psd View File


BIN
p3k/map/images/small-blue-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.7 KiB

BIN
p3k/map/images/small-blue-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.9 KiB

BIN
p3k/map/images/small-gray-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-gray-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.7 KiB

BIN
p3k/map/images/small-gray-message.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.9 KiB

BIN
p3k/map/images/small-gray-user.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-green-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-green-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-green-user.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.9 KiB

BIN
p3k/map/images/small-orange-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-orange-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-pink-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-pink-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-pink-user.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.9 KiB

BIN
p3k/map/images/small-purple-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-purple-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-red-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.6 KiB

BIN
p3k/map/images/small-red-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
p3k/map/images/small-yellow-blank.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.5 KiB

BIN
p3k/map/images/small-yellow-cutout.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.7 KiB

BIN
p3k/map/images/small-yellow-user.png View File

Before After
Width: 31  |  Height: 43  |  Size: 1.8 KiB

BIN
public/assets/sample-map.png View File

Before After
Width: 600  |  Height: 240  |  Size: 130 KiB

+ 9
- 1
views/layout.php View File

@ -4,15 +4,23 @@
<title>Atlas</title>
<link rel="stylesheet" href="/assets/pure-0.6.0/pure-min.css">
<link rel="stylesheet" href="/assets/pure-0.6.0/grids-responsive-min.css">
<link rel="stylesheet" href="/assets/font-awesome-4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/assets/font-awesome-4.4.0/css/font-awesome.min.css">
<link rel="stylesheet" href="/assets/styles.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<header>
<h1>Atlas</h1>
<p>Atlas is a set of APIs for looking up information about locations.</p>
</header>
<?= $this->fetch($this->page . '.php') ?>
<footer>
<div class="right">
<a href="https://github.com/aaronpk/Atlas">Source Code</a>
</div>
&copy; 2015 by Aaron Parecki
</footer>
</body>

+ 0
- 0
views/map.php View File


Loading…
Cancel
Save