Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Zverik committed Oct 27, 2013
0 parents commit 4a23a64
Show file tree
Hide file tree
Showing 45 changed files with 5,469 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !(index|auth)\.php$
RewriteCond %{REQUEST_FILENAME} !lib\/
RewriteRule . /index.php [L]
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# MapBBCode Share

This is a website originally made for sharing maps, based on [MapBBCode](https://github.com/mapbbcode/mapbbcode) javascript library. But since it has powerful import/export feature, it also can be used for planning routes, generating standalone map pages and converting between formats.

## Installation

In order to reduce load on the main [share.mapbbcode.org](http://share.mapbbcode.org) website, cloning the service is encouraged. Requirements are few:

* Apache with mod_rewrite
* PHP 5.2 or newer
* A separate domain (or subdomain) name

Scripts and `.htaccess` file expect that they are placed in a root directory. That is, at `http://any.domain.name/index.php`. If that's not your case, a pull request relaxing this requirement would be welcome.

To install, copy al files (except `README.md`) to a document root, modify `config.php` according to your setup, and open `http://domain.name/initdb`. If you see a map with a yellow message saying tables were created, you're good to go. Check that saving and signing in work.

## Formats

At the moment MapBBCode Share can import 11 file types and export 10. Writing a plugin for a new file format is easy: see examples in `formats` directory. Those are loaded automatically, you won't have to modify other scripts. Keep in mind this is not a universal convertor: [GPSBabel](http://www.gpsbabel.org/) is. This is not a GPS trace storage, and traces are simplified: we already have [GPSies](http://www.gpsies.com/) for full GPX downloads, elevation and speed profiles. MapBBCode Share goals are simple and outlined in the first section, and supported formats list should be kept short.

## Included Libraries

Writing the service would be much harder it it weren't for those libraries:

* [MapBBCode](https://github.com/mapbbcode/mapbbcode) (WTFPL)
* [Leaflet](http://leafletjs.com/) (BSD 2-clause)
* [Bing.js](https://github.com/shramov/leaflet-plugins/blob/master/layer/tile/Bing.js) (BSD 2-clause)
* [LightOpenID](http://code.google.com/p/lightopenid/) (MIT)
* [OpenID Selector](http://code.google.com/p/openid-selector/) (BSD 3-clause)
* [Simplify.js](http://mourner.github.io/simplify-js/) (BSD 2-clause)
* [Simplify.php](https://github.com/AKeN/simplify-php) (unknown license)

## License

All files except that did not come from other libraries are released under [WTFPL](http://www.wtfpl.net/).
92 changes: 92 additions & 0 deletions auth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
require('openid.php');

$close = false;
try {
$openid = new LightOpenID('http://share.mapbbcode.org'); // host
if( !$openid->mode ) {
if( isset($_POST['openid_identifier']) ) {
$openid->identity = $_POST['openid_identifier'];
header('Location: '.$openid->authUrl());
} else { ?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login to MapBBCode Share with OpenID</title>
<link type="text/css" rel="stylesheet" href="lib/openid.css" />
<script type="text/javascript" src="lib/openid-no-framework.js"></script>
<script type="text/javascript" src="lib/openid-mapbb.js"></script>
<style type="text/css">
body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; }
h1 { font-size: 14pt; }
</style>
</head>

<body>
<h1>Login to MapBBCode Share with OpenID</h1>
<form action="/auth.php" method="post" id="openid_form">
<input id="openid_form_submit" type="submit" style="display: none;" />
<div id="openid_choice">
<p>Please click your account provider:</p>
<div id="openid_btns"></div>
</div>
<div id="openid_input_area">
<input id="openid_identifier" name="openid_identifier" type="text" value="http://" />
<input id="openid_submit" type="submit" value="Sign-In"/>
</div>
<noscript>
<p>OpenID is service that allows you to log-on to many different websites using a single indentity.
Find out <a href="http://openid.net/what/">more about OpenID</a> and <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p>
</noscript>
<input type="button" value="No, thanks" onclick="window.close();">
</form>
<script>openid.init('openid_identifier');</script>
</body>
</html>
<?php
}
} elseif( $openid->mode == 'cancel' ) {
$message = 'You have cancelled authentication';
} elseif( !$openid->validate() ) {
$message = 'Authentication was unsuccessful. Sorry';
} else {
$id = $openid->identity;
ini_set('session.gc_maxlifetime', 7776000);
ini_set('session.cookie_lifetime', 7776000);
session_set_cookie_params(7776000);
session_start();
$_SESSION['user_id'] = $id;
$message = 'You have logged in as '.$id;
$close = true;
}
} catch( ErrorException $e ) {
$message = 'Error: '.$e->getMessage();
}

if( !isset($message) )
exit;
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login to MapBBCode Share with OpenID</title>
<style type="text/css">
body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; }
h1 { font-size: 14pt; }
</style>
</head>

<body>
<h1><?=htmlspecialchars($message) ?></h1>
<input type="button" value="Close" onclick="window.close();">
<?php if( $close ) { ?>
<script>
if( window.opener && window.opener.submit )
window.opener.submit();
window.close();
</script>
<?php } ?>
</body>
</html>
12 changes: 12 additions & 0 deletions config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
const DB_HOST = 'localhost';
const DB_USER = '';
const DB_PASSWORD = '';
const DB_DATABASE = '';
const DB_TABLE = 'sharemap'; // for other tables this is the prefix
const NEED_INIT_DB = true; // set to false after calling /initdb
const HASH_LENGTH = 5; // code hash
const EDIT_HASH_LENGTH = 5; // hash for editing
const MAX_IMPORT_NODES = 200; // nodes in imported gps traces
const MAX_TOTAL_NODES = 1000; // total nodes in all imported gps traces
?>
191 changes: 191 additions & 0 deletions convert.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php
require_once('mapbbcode.php');

const CONVERT_OK = 0;
const CONVERT_NOT_SUPPORTED = 1;
const CONVERT_EMPTY = 2;

interface Format {
// -- all properties are optional
// string $mime - mime type of the format
// string $title - displayed title of the format
// string $ext - file extension
// int $priority - determines when format is queried for import (lower = later)
// boolean $simplify - if true, paths are simplified after importing
// boolean $import_filename - import() requires filename instead of file handle (e.g. because of XMLReader)

// returns string representation of data array (see mapbbcode.php)
public /* string */ function export( $data );

// checks if data (represented by its header) can be imported as this format
public /* boolean */ function test( $header );

// parses file ($file is a handler) and returns objects array (see mapbbcode.php)
public /* object */ function import( $file );

// last line of every format script should include
// $formats['type'] = new ClassName();
}

$formats = array();
foreach( glob('formats/*.php') as $file ) {
include($file);
}

// find an object for given format
function get_format( $type ) {
return isset($formats[$type]) ? $formats[$type] : false;
}

// return array(types, titles) of all supported formats
function get_format_arrays() {
global $formats;
$etypes = array();
$etitles = array();
foreach( $formats as $type => $fmt ) {
if( isset($fmt->title) ) {
$etypes[] = $type;
$etitles[] = $fmt->title;
}
}
return array('types' => $etypes, 'titles' => $etitles);
}

// parses bbcode and prints exported file. Returns CONVERT_* status
function export( $type, $title, $bbcode, $codeid = '', $break_on_empty = true ) {
global $formats;
if( isset($formats[$type]) ) {
$fmt = $formats[$type];
require_once('mapbbcode.php');
$data = mapbbcode_to_array($bbcode);
$data['title'] = $title;
if( count($data['objs']) > 0 || isset($data['zoom']) ) {
$content = $fmt->export($data);
if( !$break_on_empty || strlen($content) > 0 ) {
$basename = preg_match('/^\w+$/', $codeid) ? $codeid : 'shared';
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
header('Content-Type: '.(isset($fmt->mime) ? $fmt->mime : 'text/plain'));
header('Content-Disposition: attachment; filename='.$basename.'.'.(isset($fmt->ext) ? $fmt->ext : $type));
header('Content-Length: '.mb_strlen($content, '8bit'));
print $content;
return CONVERT_OK;
}
}
return CONVERT_EMPTY;
}
return CONVERT_NOT_SUPPORTED;
}

// format comparator, by priority
function compare_formats( $fmta, $fmtb ) {
$pa = isset($fmta->priority) ? $fmta->priority : 0;
$pb = isset($fmtb->priority) ? $fmtb->priority : 0;
return $pa < $pb ? -1 : ($pa == $pb ? 0 : 1);
}

// determines format for given file header
function import_get_format( $header ) {
global $formats;
$fmts = array();
foreach( $formats as $type => $fmt ) {
$fmts[] = $fmt;
}
usort($fmts, 'compare_formats');
foreach( $fmts as $fmt ) {
if( $fmt->test($header) )
return $fmt;
}
return false;
}

// reads file and returns array(title, bbcode) for it. On error returns array('', '')
function import( $filename ) {
$data = false;
if( ($handle = fopen($filename, 'r')) !== false ) {
$header = fread($handle, 2000);
$fmt = import_get_format($header);
if( $fmt !== false ) {
if( isset($fmt->import_filename) && $fmt->import_filename ) {
fclose($handle);
$data = $fmt->import($filename);
} else {
if( !rewind($handle) ) {
fclose($handle);
$handle = fopen($filename, 'r');
}
$data = $fmt->import($handle);
fclose($handle);
}
if( $data && isset($data['objs']) && count($data['objs']) > 0 && isset($fmt->simplify) && $fmt->simplify )
$data['objs'] = reduce_points($data['objs']);
} else
fclose($handle);
}

if( $data ) {
$title = isset($data['title']) ? $data['title'] : '';
$bbcode = array_to_mapbbcode($data);
return array($title, $bbcode);
}
return array('', '');
}

// simplifies paths in the array
function reduce_points( $objs ) {
global $message;
$nodes = 0;
$count = 0;
foreach( $objs as $obj ) {
if( count($obj['coords']) > 1 ) {
$nodes += count($obj['coords']);
$count++;
}
}
$max = $nodes > MAX_TOTAL_NODES ? min(floor(MAX_TOTAL_NODES / $count), MAX_IMPORT_NODES) : MAX_IMPORT_NODES;
$nodes2 = $nodes;

foreach( $objs as &$obj ) {
if( count($obj['coords']) > $max) {
$orig = count($obj['coords']);
$obj['coords'] = iterate_simplify($obj['coords'], $max);
$nodes2 -= $orig - count($obj['coords']);
}
}
unset($obj);

if( $nodes2 < $nodes )
$message = "Traces were simplified: $nodes points down to $nodes2";

return $objs;
}

require_once('simplify.php');

// iterative simplify, so number of points is between 90% and 100% of $max
function iterate_simplify( $points, $max ) {
if( count($points) <= $max )
return $points;
//echo 'Reducing points (max='.$max.') from '.count($points);
$min = round($max * 0.9);
$tolerance = 0.00001;
$tol1 = 0;
$tol2 = 0;
$steps = 0;
while( $steps++ < 10 ) {
$p = simplify($points, $tolerance);
$cnt = count($p);
if( $cnt > $max ) {
$tol1 = $tolerance;
$tolerance = $tol2 < $tolerance ? $tolerance * 2 : ($tolerance + $tol2) / 2;
} elseif( $cnt < $min ) {
$tol2 = $tolerance;
$tolerance = ($tol1 + $tolerance) / 2;
} else
break;
}
//echo ' to '.count($p).' ('.$steps.' iterations)<br>';
return $p;
}

?>
Loading

0 comments on commit 4a23a64

Please sign in to comment.