Browse Source

Merge pull request #7 from plepe/master

Sync
master
Igor Eliezer 6 years ago
committed by GitHub
parent
commit
4d0998a74a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      README.md
  2. 24
      doc/Icons.md
  3. 5
      index.php
  4. 7
      lang/ast.json
  5. 7
      lang/ca.json
  6. 7
      lang/cs.json
  7. 7
      lang/da.json
  8. 7
      lang/de.json
  9. 7
      lang/el.json
  10. 7
      lang/en.json
  11. 7
      lang/es.json
  12. 7
      lang/et.json
  13. 7
      lang/fr.json
  14. 7
      lang/hu.json
  15. 7
      lang/it.json
  16. 7
      lang/ja.json
  17. 7
      lang/nl.json
  18. 7
      lang/pl.json
  19. 7
      lang/pt-br.json
  20. 7
      lang/pt.json
  21. 7
      lang/ro.json
  22. 7
      lang/ru.json
  23. 7
      lang/sr.json
  24. 7
      lang/template.json
  25. 7
      lang/uk.json
  26. 2
      lib/modulekit/form
  27. 2
      modulekit.php
  28. 11
      package.json
  29. 12
      repo.php
  30. 4
      src/CategoryBase.js
  31. 19
      src/CategoryIndex.js
  32. 21
      src/CategoryOverpass.js
  33. 34
      src/ExportGeoJSON.js
  34. 24
      src/ExportOSMJSON.js
  35. 20
      src/ExportOSMXML.js
  36. 33
      src/RepositoryGit.php
  37. 96
      src/addCategories.js
  38. 9
      src/chunkSplit.js
  39. 153
      src/exportAll.js
  40. 21
      src/index.js
  41. 37
      src/maki.js
  42. 19
      style.css

10
README.md

@ -105,6 +105,16 @@ The following values are possible for categories (the only mandatory value is qu
* noClip: (unclosed ways only) Disable polyline clipping. (boolean, false)
* nodeFeature: (nodes only) Which type of feature to show on nodes. Valid values: 'Marker', 'Circle', 'CircleMarker'. (string, 'CircleMarker')
* radius: (nodes with nodeFeature 'Circle' or 'CircleMarker' only) Radius of the circle (for 'Circle': in meters, for 'CircleMarker': in pixels, default: 10). (number)
* pattern: false/empty: no pattern, 'arrowHead', 'dash', 'marker'
* pattern-offset: Offset of the first pattern symbol, from the start point of the line. Default: 0.
* pattern-endOffset: Minimum offset of the last pattern symbol, from the end point of the line. Default: 0.
* pattern-repeat: Repetition interval of the pattern symbols. Defines the distance between each consecutive symbol's anchor point.
* pattern-polygon: true/false (arrowHead only)
* pattern-pixelSize: size of pattern (arrowHead and dash only)
* pattern-headAngle: Angle of the digits (arrowHead only)
* pattern-angleCorrection: degrees (arrowHead and marker only)
* pattern-rotate: false (marker only)
* pattern-path-*: Options for the path, e.g. pattern-path-width, pattern-path-color.
* title: the title of the feature popup, the object in the list and the details page. (default: localized tags for 'name', 'operator' or 'ref', default: 'unknown')
* body: the body for the feature popup and the details page.
* description: a short description shown in the list next to the title.

24
doc/Icons.md

@ -9,34 +9,42 @@ A drawback of Unicode icons is, that the display will differ from system to syst
#### Self defined icons
You may upload images to your repository and use them via a relative image link:
```html
<img src='img/foobar.svg'>
<img data-src='img/foobar.svg'>
```
This will include the image from your repository (when uploaded to your 'img' directory).
#### Font Awesome Icons
Font Awesome 4 is included in OpenStreetBrowser, therefore you can use, e.g.:
[Font Awesome 5](https://fontawesome.com/) is included in OpenStreetBrowser, therefore you can use, e.g.:
```html
<i class="fa fa-compass" aria-hidden="true"></i>
<i class="fas fa-compass" aria-hidden="true"></i> <!-- solid -->
<i class="far fa-compass" aria-hidden="true"></i> <!-- regular -->
```
You can use normal CSS to modify its look, e.g.
```html
<i style="color: red;" class="fa fa-compass" aria-hidden="true"></i>
<i style="color: red;" class="fas fa-compass" aria-hidden="true"></i>
```
#### Mapbox Maki Icons
Mapbox Maki Icons 4 are also included in OpenStreetBrowser. They can be accessed as images with protocol 'maki', e.g.:
[Mapbox Maki Icons 5](https://www.mapbox.com/maki-icons/) are also included in OpenStreetBrowser. They can be accessed as images with protocol 'maki', e.g.:
```html
<img src="maki:park">
<img data-src="maki:park">
```
This will include the park-15.svg icon. Mapbox Maki icons come in two sizes: 11 and 15. Default is 15, if you want to use 11 pass the size parameter with value 11:
```html
<img src="maki:park?size=11">
<img data-src="maki:park?size=11">
```
You can pass URL options to the icon to modify its look. Note that every icon is a [SVG path](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) and all [style options](https://developer.mozilla.org/de/docs/Web/SVG/Tutorial/Fills_and_Strokes) are available:
```html
<img src="maki:park?size=11&amp;fill=red&amp;stroke=black&amp;stroke-width=0.5">
<img data-src="maki:park?size=11&amp;fill=red&amp;stroke=black&amp;stroke-width=0.5">
```
#### Temaki Icons
[Temaki icons](http://www.7thposition.com/temaki/docs/) are additions to the Mapbox Maki Icons with the difference that they only exist in to size of 15px.
```html
<img data-src="temaki:shinto">
<img data-src="temaki:shinto?fill=red">
```

5
index.php

@ -39,7 +39,8 @@ html_export_var(array(
<link rel="manifest" href="manifest.json" />
<link rel="icon" type="image/png" href="img/osb-192.png" />
<link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css" />
<link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css" />
<link rel="stylesheet" href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" />
<link rel="stylesheet" href="node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css" />
<link rel="stylesheet" href="node_modules/leaflet-geosearch/assets/css/leaflet.css" />
<link rel="stylesheet" href="node_modules/leaflet.locatecontrol/dist/L.Control.Locate.min.css" />
<link rel="stylesheet" href="node_modules/leaflet.polylinemeasure/Leaflet.PolylineMeasure.css" />
@ -48,6 +49,7 @@ html_export_var(array(
<script src="node_modules/leaflet-textpath/leaflet.textpath.js"></script>
<script src="node_modules/leaflet-polylineoffset/leaflet.polylineoffset.js"></script>
<script src="node_modules/leaflet.polylinemeasure/Leaflet.PolylineMeasure.js"></script>
<script src="node_modules/leaflet-polylinedecorator/dist/leaflet.polylineDecorator.js"></script>
<?php print modulekit_to_javascript(); /* pass modulekit configuration to JavaScript */ ?>
<?php print modulekit_include_js(); /* prints all js-includes */ ?>
<?php print modulekit_include_css(); /* prints all css-includes */ ?>
@ -63,6 +65,7 @@ html_export_var(array(
<img src='img/osb_logo.png'>
<div id='title'>OpenStreet <span class='large'>Browser</span><div class='version' title='<?=$modulekit['version']?>'><?php print substr($modulekit['version'], 0, strpos($modulekit['version'], '+')); ?></div></div>
</div>
<div id='globalTabs'></div>
<div id='content' class='list'>
<div id='contentList'></div>
<div id='contentDetails'></div>

7
lang/ast.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/ca.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/cs.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/da.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/de.json

@ -1,14 +1,19 @@
{
"available_branches": "",
"back": "zurück",
"category-info-tooltip": "Info & Legende",
"closed": "geschlossen",
"default": "Standard",
"download:geojson": "Als GeoJSON runterladen",
"edit": "bearbeiten",
"error": {
"message": "Fehler",
"!=1": "Fehler"
},
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "Als GeoJSON runterladen",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "Einrichtungen",
"header:attributes": "Attribute",
"header:export": "Export",

7
lang/el.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/en.json

@ -1,14 +1,19 @@
{
"available_branches": "Available branches",
"back": "back",
"category-info-tooltip": "Info & Map key",
"closed": "closed",
"default": "default",
"download:geojson": "Download as GeoJSON",
"edit": "edit",
"error": {
"message": "Error",
"!=1": "Errors"
},
"export-all": "Export all visible map features",
"export-prepare": "Prepare download",
"export:GeoJSON": "Download as GeoJSON",
"export:OSMJSON": "Download as OSM JSON",
"export:OSMXML": "Download as OSM XML",
"facilities": "Facilities",
"header:attributes": "Attributes",
"header:export": "Export",

7
lang/es.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/et.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/fr.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "Retour",
"category-info-tooltip": "Info & Légende",
"closed": "Fermé",
"default": "Par défaut",
"download:geojson": "Télécharger comme GeoJSON",
"edit": "éditer",
"error": "Erreur",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "Télécharger comme GeoJSON",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "Aménagements",
"header:attributes": "Attributs",
"header:export": "Export",

7
lang/hu.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "Vissza",
"category-info-tooltip": "Információk és jelmagyarázat",
"closed": "Lezárva",
"default": "Alapértelmezett",
"download:geojson": "Letöltés GeoJSON formátumban",
"edit": "Szerkesztés",
"error": "Hiba",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "Letöltés GeoJSON formátumban",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "Létesítmények",
"header:attributes": "Tulajdonságok",
"header:export": "Exportálás",

7
lang/it.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/ja.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/nl.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/pl.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/pt-br.json

@ -1,14 +1,19 @@
{
"available_branches": "",
"back": "voltar",
"category-info-tooltip": "Info & Legenda",
"closed": "fechado",
"default": "padrão",
"download:geojson": "Baixar como GeoJSON",
"edit": "editar",
"error": {
"message": "Erro",
"!=1": "Erros"
},
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "Baixar como GeoJSON",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "Instalações",
"header:attributes": "Atributos",
"header:export": "Exportar",

7
lang/pt.json

@ -1,14 +1,19 @@
{
"available_branches": "",
"back": "voltar",
"category-info-tooltip": "Info & Legenda",
"closed": "fechado",
"default": "padrão",
"download:geojson": "Descarregar como GeoJSON",
"edit": "editar",
"error": {
"message": "Erro",
"!=1": "Erros"
},
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "Descarregar como GeoJSON",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "Instalações",
"header:attributes": "Atributos",
"header:export": "Exportar",

7
lang/ro.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/ru.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/sr.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/template.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

7
lang/uk.json

@ -1,11 +1,16 @@
{
"available_branches": "",
"back": "",
"category-info-tooltip": "",
"closed": "",
"default": "",
"download:geojson": "",
"edit": "",
"error": "",
"export-all": "",
"export-prepare": "",
"export:GeoJSON": "",
"export:OSMJSON": "",
"export:OSMXML": null,
"facilities": "",
"header:attributes": "",
"header:export": "",

2
lib/modulekit/form

@ -1 +1 @@
Subproject commit 8a2d3015ec181b14441037a79e7db3869453b135
Subproject commit 5d39b2f61b7eda9a635414acaeb3c4e0dc524490

2
modulekit.php

@ -25,4 +25,4 @@ $include = array(
'style.css',
),
);
$version = "4.0";
$version = "4.2";

11
package.json

@ -1,13 +1,14 @@
{
"name": "openstreetbrowser",
"version": "4.0.0",
"version": "4.2.1",
"description": "A re-make of the famous OpenStreetBrowser (pure JS, using Overpass API)",
"main": "src/export.js",
"repository": "https://github.com/plepe/openstreetbrowser",
"author": "Stephan Bösch-Plepelits <skunk@xover.mud.at>",
"license": "GPL-3.0",
"dependencies": {
"@mapbox/maki": "^4.0.0",
"@fortawesome/fontawesome-free": "^5.5.0",
"@mapbox/maki": "^5.0.0",
"async": "^2.5.0",
"async-foreach": "^0.1.3",
"babelify": "^8.0.0",
@ -15,7 +16,7 @@
"babel-core": "^6.26.0",
"babel-preset-env": "^1.6.1",
"color-interpolate": "^1.0.2",
"font-awesome": "^4.7.0",
"file-saver": "^2.0.0",
"i18next-client": "^1.11.4",
"ip-location": "^1.0.1",
"json-multiline-strings": "^0.1.0",
@ -26,7 +27,7 @@
"leaflet.locatecontrol": "^0.61.0",
"leaflet.polylinemeasure": "https://github.com/ppete2/Leaflet.PolylineMeasure.git",
"md5": "^2.2.1",
"modulekit-tabs": "^0.1.0",
"modulekit-tabs": "^0.2.0",
"moment": "^2.18.1",
"natsort": "^1.0.6",
"opening_hours": "^3.5.0",
@ -36,6 +37,7 @@
"overpass-layer": "^2.0.0",
"query-string": "^5.0.0",
"sheet-router": "^4.2.3",
"temaki": "^1.0.0",
"weight-sort": "^1.3.0"
},
"browserify": {
@ -72,6 +74,7 @@
"devDependencies": {
"browserify": "^14.4.0",
"browserify-css": "^0.14.0",
"leaflet-polylinedecorator": "https://github.com/plepe/Leaflet.PolylineDecorator.git",
"standard": "^10.0.2",
"watchify": "^3.9.0"
},

12
repo.php

@ -34,6 +34,8 @@ if (!isset($_REQUEST['repo'])) {
}
$repoId = $_REQUEST['repo'];
list($repoId, $branchId) = explode('~', $repoId);
if (!array_key_exists($repoId, $allRepositories)) {
Header("HTTP/1.1 404 Repository not found");
exit(0);
@ -42,6 +44,16 @@ if (!array_key_exists($repoId, $allRepositories)) {
$repoData = $allRepositories[$repoId];
$repo = getRepo($repoId, $repoData);
if ($branchId) {
try {
$repo->setBranch($branchId);
}
catch (Exception $e) {
Header("HTTP/1.1 404 No such branch");
exit(0);
}
}
$cacheDir = null;
$ts = $repo->timestamp($path);
if (isset($config['cache'])) {

4
src/CategoryBase.js

@ -204,4 +204,8 @@ CategoryBase.prototype.notifyChildLoadEnd = function (category) {
}
}
CategoryBase.prototype.allMapFeatures = function (callback) {
callback(null, [])
}
module.exports = CategoryBase

19
src/CategoryIndex.js

@ -106,5 +106,24 @@ CategoryIndex.prototype.toggleCategory = function (id) {
}.bind(this))
}
CategoryIndex.prototype.allMapFeatures = function (callback) {
let result = []
async.each(this.childrenCategories,
(category, done) => category.allMapFeatures(
(err, data) => {
if (err) {
return done(err)
}
result = result.concat(data)
global.setTimeout(done, 0)
}
),
(err) => callback(err, result)
)
}
OpenStreetBrowserLoader.registerType('index', CategoryIndex)
module.exports = CategoryIndex

21
src/CategoryOverpass.js

@ -151,16 +151,17 @@ CategoryOverpass.prototype.updateAssets = function (div) {
for (var i = 0; i < imgs.length; i++) {
let img = imgs[i]
var src = img.getAttribute('src')
// TODO: 'src' is deprecated, use only data-src
var src = img.getAttribute('src') || img.getAttribute('data-src')
if (src === null) {
} else if (src.match(/^maki:.*/)) {
let m = src.match(/^maki:([a-z0-9-]*)(?:\?(.*))?$/)
} else if (src.match(/^(maki|temaki):.*/)) {
let m = src.match(/^(maki|temaki):([a-z0-9-]*)(?:\?(.*))?$/)
if (m) {
let span = document.createElement('span')
img.parentNode.insertBefore(span, img)
img.parentNode.removeChild(img)
i--
maki(m[1], m[2] ? qs(m[2]) : {}, function (err, result) {
maki(m[1], m[2], m[3] ? qs(m[3]) : {}, function (err, result) {
if (err === null) {
span.innerHTML = result
}
@ -377,7 +378,8 @@ CategoryOverpass.prototype.show = function (id, options, callback) {
}
let layerOptions = {
styles: [ 'selected' ]
styles: [ 'selected' ],
flags: [ 'selected' ]
}
let idParts = id.split(/:/)
@ -417,6 +419,7 @@ CategoryOverpass.prototype.notifyPopupOpen = function (object, popup) {
let layerOptions = {
styles: [ 'selected' ],
flags: [ 'selected' ],
sublayer_id: object.sublayer_id
}
@ -488,5 +491,13 @@ CategoryOverpass.prototype.renderTemplate = function (object, templateId, callba
})
}
CategoryOverpass.prototype.allMapFeatures = function (callback) {
if (!this.isOpen) {
return callback(null, [])
}
callback(null, Object.values(this.layer.mainlayer.visibleFeatures))
}
OpenStreetBrowserLoader.registerType('overpass', CategoryOverpass)
module.exports = CategoryOverpass

34
src/ExportGeoJSON.js

@ -0,0 +1,34 @@
class ExportGeoJSON {
constructor (conf) {
this.conf = conf
}
each (ob, callback) {
ob.object.exportGeoJSON(this.conf, callback)
}
finishOne (object) {
return {
content: JSON.stringify(object, null, ' '),
fileType: 'application/json',
extension: 'geojson'
}
}
finish (list) {
if (!this.conf.singleFeature) {
list = {
type: 'FeatureCollection',
features: list
}
}
return {
content: JSON.stringify(list, null, ' '),
fileType: 'application/json',
extension: 'geojson'
}
}
}
module.exports = ExportGeoJSON

24
src/ExportOSMJSON.js

@ -0,0 +1,24 @@
class ExportOSMXML {
constructor (conf) {
this.conf = conf
this.elements = {}
}
each (ob, callback) {
ob.object.exportOSMJSON(this.conf, this.elements, callback)
}
finish (list) {
return {
content: JSON.stringify({
version: '0.6',
generator: 'OpenStreetBrowser',
elements: Object.values(this.elements)
}, null, ' '),
fileType: 'application/json',
extension: 'osm.json'
}
}
}
module.exports = ExportOSMXML

20
src/ExportOSMXML.js

@ -0,0 +1,20 @@
class ExportOSMXML {
constructor (conf) {
this.conf = conf
this.parentNode = document.createElement('osm')
}
each (ob, callback) {
ob.object.exportOSMXML(this.conf, this.parentNode, callback)
}
finish (list) {
return {
content: '<?xml version="1.0" encoding="UTF-8"?><osm version="0.6" generator="OpenStreetBrowser">' + this.parentNode.innerHTML + '</osm>',
fileType: 'application/xml',
extension: 'osm.xml'
}
}
}
module.exports = ExportOSMXML

33
src/RepositoryGit.php

@ -2,10 +2,27 @@
class RepositoryGit extends RepositoryBase {
function __construct ($id, $def) {
parent::__construct($id, $def);
$this->branch = $def['branch'] ?? 'HEAD';
if (array_key_exists('branch', $def)) {
$this->branch = $def['branch'];
}
else {
$this->branch = chop(shell_exec("cd " . escapeShellArg($this->path) . "; git rev-parse --abbrev-ref HEAD"));
}
$this->branchEsc = escapeShellArg($this->branch);
}
function setBranch ($branch) {
$this->branch = $branch;
$this->branchEsc = escapeShellArg($this->branch);
exec("cd " . escapeShellArg($this->path) . "; git ls-tree {$this->branchEsc}", $output, $return);
if ($return !== 0) {
throw new Exception('no such branch');
}
}
function timestamp () {
$ts = (int)shell_exec("cd " . escapeShellArg($this->path) . "; git log -1 {$this->branchEsc} --pretty=format:%ct");
@ -40,6 +57,20 @@ class RepositoryGit extends RepositoryBase {
}
pclose($d);
if (!array_key_exists('branch', $this->def)) {
$d = popen("cd " . escapeShellArg($this->path) . "; git for-each-ref --sort=-committerdate refs/heads/", "r");
$data['branch'] = $this->branch;
$data['branches'] = array();
while ($r = fgets($d)) {
if (preg_match("/^([0-9a-f]{40}) commit\trefs\/heads\/(.*)$/", $r, $m)) {
$data['branches'][$m[2]] = array(
'commit' => $m[1],
);
}
}
pclose($d);
}
return $data;
}

96
src/addCategories.js

@ -1,34 +1,31 @@
/* global OverpassLayer, repositoriesGitea */
require('./addCategories.css')
const tabs = require('modulekit-tabs')
const weightSort = require('weight-sort')
const OpenStreetBrowserLoader = require('./OpenStreetBrowserLoader')
var content
let tab
function addCategoriesShow (repo) {
if (!content) {
content = document.createElement('div')
content.id = 'contentAddCategories'
document.getElementById('content').appendChild(content)
let content = tab.content
let repoId
let branchId
if (repo) {
[ repoId, branchId ] = repo.split(/~/)
}
content.innerHTML = 'Loading ...'
document.getElementById('content').className = 'addCategories'
content.innerHTML = '<h3>' + lang('more_categories') + '</h3>' + '<i class="fa fa-spinner fa-pulse fa-fw"></i> ' + lang('loading')
OpenStreetBrowserLoader.getRepo(repo, {}, function (err, repoData) {
if (err) {
alert(err)
}
while (content.firstChild) { content.removeChild(content.firstChild) }
var backLink = document.createElement('a')
backLink.className = 'back'
backLink.href = '#'
backLink.innerHTML = '<i class="fa fa-chevron-circle-left" aria-hidden="true"></i> '
backLink.appendChild(document.createTextNode(lang('back')))
content.innerHTML = '<h3>' + lang('more_categories') + '</h3>'
var categoryUrl = null
if (repoData.categoryUrl) {
@ -38,6 +35,12 @@ function addCategoriesShow (repo) {
var list = {}
if (repo) {
var backLink = document.createElement('a')
backLink.className = 'back'
backLink.href = '#'
backLink.innerHTML = '<i class="fa fa-chevron-circle-left" aria-hidden="true"></i> '
backLink.appendChild(document.createTextNode(lang('back')))
backLink.onclick = function () {
addCategoriesShow()
return false
@ -45,21 +48,11 @@ function addCategoriesShow (repo) {
content.appendChild(backLink)
let h = document.createElement('h2')
h.appendChild(document.createTextNode(repo))
h.appendChild(document.createTextNode(repoId))
content.appendChild(h)
list = repoData.categories
} else {
backLink.onclick = function () {
addCategoriesHide()
return false
}
content.appendChild(backLink)
let h = document.createElement('h2')
h.innerHTML = lang('more_categories')
content.appendChild(h)
if (typeof repositoriesGitea === 'object' && repositoriesGitea.url) {
let a = document.createElement('a')
a.href = repositoriesGitea.url
@ -74,6 +67,35 @@ function addCategoriesShow (repo) {
})
}
if ('branches' in repoData) {
let text = document.createElement('span')
text.innerHTML = lang('available_branches') + ': '
content.appendChild(text)
let branchSelector = document.createElement('select')
branchSelector.onchange = () => {
let branch = branchSelector.value
addCategoriesShow(repoId + '~' + branch)
}
Object.keys(repoData.branches).forEach(
branch => {
let option = document.createElement('option')
option.value = branch
option.appendChild(document.createTextNode(branch))
if (repoData.branch === branch) {
option.selected = true
}
branchSelector.appendChild(option)
}
)
content.appendChild(branchSelector)
}
var ul = document.createElement('ul')
for (var id in list) {
@ -133,14 +155,20 @@ function addCategoriesHide () {
}
register_hook('init', function (callback) {
var link = document.createElement('a')
link.className = 'addCategories'
link.href = '#'
link.onclick = function () {
addCategoriesShow()
return false
}
link.innerHTML = '<i class="fa fa-chevron-circle-down" aria-hidden="true"></i> ' + lang('more_categories')
tab = new tabs.Tab({
id: 'addCategories'
})
global.tabs.add(tab)
tab.header.innerHTML = '<i class="fa fa-plus" aria-hidden="true"></i>'
tab.header.title = lang('more_categories')
document.getElementById('contentList').appendChild(link)
let initialized = false
tab.on('select', () => {
if (!initialized) {
addCategoriesShow()
initialized = true
}
})
})

9
src/chunkSplit.js

@ -0,0 +1,9 @@
module.exports = function chunkSplit (data, size=1000) {
let result = []
for (let i = 0; i < data.length; i += size) {
result.push(data.slice(i, i + size))
}
return result
}

153
src/exportAll.js

@ -0,0 +1,153 @@
const tabs = require('modulekit-tabs')
const async = require('async')
const FileSaver = require('file-saver')
const chunkSplit = require('./chunkSplit')
const types = {
GeoJSON: require('./ExportGeoJSON'),
OSMXML: require('./ExportOSMXML'),
OSMJSON: require('./ExportOSMJSON')
}
let tab
let formExport
function prepareDownload (callback) {
let conf = formExport.get_data()
global.baseCategory.allMapFeatures((err, data) => {
if (err) {
return callback(err)
}
createDownload(conf, data, callback)
})
}
function createDownload (conf, data, callback) {
let type = types[conf.type]
let exportFun = new type(conf)
let chunks = chunkSplit(data, 1000)
let parentNode
async.mapLimit(
chunks,
1,
(chunk, done) => {
async.map(chunk,
(ob, done) => exportFun.each(ob, done),
(err, result) => {
global.setTimeout(() => done(err, result), 0)
}
)
},
(err, data) => {
if (err) {
return callback(err)
}
if (data.length !== 0) {
data = data.reduce((all, chunk) => all.concat(chunk))
}
let result = exportFun.finish(data)
var blob = new Blob([ result.content ], { type: result.fileType + ';charset=utf-8' })
FileSaver.saveAs(blob, 'openstreetbrowser.' + result.extension)
callback()
}
)
}
function formDef () {
let values = {}
Object.keys(types).forEach(type =>
values[type] = lang('export:' + type)
)
return {
type: {
name: 'Type',
type: 'radio',
values,
default: Object.keys(types)[0]
}
}
}
register_hook('init', function () {
tab = new tabs.Tab({
id: 'export'
})
global.tabs.add(tab)
tab.header.innerHTML = '<i class="fa fa-download" aria-hidden="true"></i>'
tab.header.title = lang('export-all')
tab.content.innerHTML = '<h3>' + lang('export-all') + '</h3>'
formExport = new form('export', formDef())
let domForm = document.createElement('form')
tab.content.appendChild(domForm)
formExport.show(domForm)
let submit = document.createElement('input')
submit.type = 'submit'
submit.value = lang('export-prepare')
submit.onclick = () => {
let progressIndicator = document.createElement('div')
progressIndicator.innerHTML = '<i class="fa fa-spinner fa-pulse fa-fw"></i> ' + lang('loading')
tab.content.appendChild(progressIndicator)
submit.style.display = 'none'
prepareDownload((err) => {
if (err) {
alert(err)
}
submit.style.display = 'block'
tab.content.removeChild(progressIndicator)
tab.unselect()
})
}
tab.content.appendChild(submit)
tab.on('select', () => {
formExport.resize()
})
})
module.exports = (data, div) => {
let formExport = new form('exportOne', formDef())
let domForm = document.createElement('form')
div.appendChild(domForm)
formExport.show(domForm)
let submit = document.createElement('input')
submit.type = 'submit'
submit.value = lang('export-prepare')
submit.onclick = () => {
let progressIndicator = document.createElement('div')
progressIndicator.innerHTML = '<i class="fa fa-spinner fa-pulse fa-fw"></i> ' + lang('loading')
div.appendChild(progressIndicator)
submit.style.display = 'none'
let conf = formExport.get_data()
conf.singleFeature = true
createDownload(conf, [ data ], (err) => {
if (err) {
alert(err)
}
submit.style.display = 'block'
div.removeChild(progressIndicator)
})
}
div.appendChild(submit)
}

21
src/index.js

@ -1,6 +1,7 @@
/* globals map:true, overpassFrontend:true, currentPath:true, options:true, baseCategory:true, overpassUrl:true showDetails */
var LeafletGeoSearch = require('leaflet-geosearch')
const tabs = require('modulekit-tabs')
var OverpassFrontend = require('overpass-frontend')
var OpenStreetBrowserLoader = require('./OpenStreetBrowserLoader')
@ -17,6 +18,7 @@ global.baseCategory = null
global.overpassUrl = null
global.overpassFrontend = null
global.currentPath = null
global.tabs = null
var lastPopupClose = 0
// Optional modules
@ -31,6 +33,7 @@ require('./categories')
require('./wikipedia')
require('./image')
require('./addCategories')
let exportAll = require('./exportAll')
window.onload = function () {
var initState = config.defaultView
@ -42,6 +45,8 @@ window.onload = function () {
options = {}
}
global.tabs = new tabs.Tabs(document.getElementById('globalTabs'))
call_hooks('init')
call_hooks_callback('init_callback', initState, onload2.bind(this, initState))
@ -163,6 +168,10 @@ function onload2 (initState) {
call_hooks('initFinish')
}
global.allMapFeatures = function (callback) {
global.baseCategory.allMapFeatures(callback)
}
window.setPath = function (path, state) {
currentPath = path
@ -285,17 +294,9 @@ window.showDetails = function (data, category) {
h.innerHTML = lang('header:export')
dom.appendChild(h)
div = document.createElement('ul')
div = document.createElement('div')
dom.appendChild(div)
li = document.createElement('li')
div.appendChild(li)
a = document.createElement('a')
a.download = data.id + '.json'
a.href = 'data:application/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(data.object.GeoJSON(), null, ' '))
a.innerHTML = lang('download:geojson')
li.appendChild(a)
exportAll(data, div)
h = document.createElement('h3')
h.innerHTML = lang('header:attributes')

37
src/maki.js

@ -1,6 +1,10 @@
/* global openstreetbrowserPrefix */
var loadClash = {}
var cache = {}
var paths = {
maki: 'node_modules/@mapbox/maki/icons/ID-SIZE.svg',
temaki: 'node_modules/temaki/icons/ID.svg'
}
function applyOptions (code, options) {
var style = ''
@ -14,40 +18,41 @@ function applyOptions (code, options) {
return code.replace(/<path/i, '<path style="' + style + '"')
}
function maki (file, options, callback) {
var size = options.size || 15
function maki (set, file, options, callback) {
var m = file.match(/^(.*)-(11|15)/)
if (!m) {
file += '-' + size
if (m) {
file = m[1]
options.size = m[2]
}
var url = (typeof openstreetbrowserPrefix === 'undefined' ? './' : openstreetbrowserPrefix) +
'node_modules/@mapbox/maki/icons/' + file + '.svg'
paths[set]
.replace('ID', file)
.replace('SIZE', options.size || 15)
if (file in cache) {
return callback(null, applyOptions(cache[file], options))
if (url in cache) {
return callback(null, applyOptions(cache[url], options))
}
if (file in loadClash) {
loadClash[file].push([ options, callback ])
if (url in loadClash) {
loadClash[url].push([ options, callback ])
return
} else {
loadClash[file] = [ [ options, callback ] ]
loadClash[url] = [ [ options, callback ] ]
}
var req = new XMLHttpRequest()
req.addEventListener('load', function () {
if (req.status !== 200) {
loadClash[file].forEach(p => p[1](req.statusText, null))
delete loadClash[file]
loadClash[url].forEach(p => p[1](req.statusText, null))
delete loadClash[url]
return
}
cache[file] = req.responseText
cache[url] = req.responseText
loadClash[file].forEach(p => p[1](null, applyOptions(cache[file], p[0])))
delete loadClash[file]
loadClash[url].forEach(p => p[1](null, applyOptions(cache[url], p[0])))
delete loadClash[url]
})
req.open('GET', url)
req.send()

19
style.css

@ -77,6 +77,20 @@ a:active {
font-size: 16px;
}
#sidebar > #globalTabs {
padding-left: 10px;
padding-right: 10px;
}
#sidebar > #globalTabs ul.tabs-list {
font-size: 15pt;
}
.tabs-section {
padding: 0.1em;
}
.tabs-section > h3:first-of-type {
margin-top: 0;
}
#sidebar > #content {
flex: 1;
flex-shrink: 0;
@ -384,3 +398,8 @@ a:active {
top: 0; bottom: 0; left: 0; right: 0;
margin: auto;
}
.info .sign,
.overpass-layer-icon .sign {
white-space: nowrap;
}
Loading…
Cancel
Save