{"id":13608142,"url":"https://github.com/nekromoff/bicycle-master-plan","last_synced_at":"2025-03-23T13:32:22.829Z","repository":{"id":46277012,"uuid":"176778316","full_name":"nekromoff/bicycle-master-plan","owner":"nekromoff","description":"Bicycle Master Plan is a bike map that allows visualizing cycling related data from different sources in multiple layers. Built heavily around data from OpenStreetMap, it allows full visual customization using CSS.","archived":false,"fork":false,"pushed_at":"2023-10-17T22:41:40.000Z","size":650,"stargazers_count":11,"open_issues_count":9,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T20:54:03.704Z","etag":null,"topics":["app","bicycle","bicycle-infrastructure","bike","cycling","cycling-safety","exif-tags","google-sheets","gps-data","map","mapping","mapping-tools","maps","mapserver","markers","openstreetmap","openstreetmaps","osm","overpass-api","overpass-turbo"],"latest_commit_sha":null,"homepage":"https://mapa.cyklokoalicia.sk/bratislava/public/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nekromoff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2019-03-20T16:52:26.000Z","updated_at":"2023-11-14T21:05:33.000Z","dependencies_parsed_at":"2024-04-17T20:47:57.723Z","dependency_job_id":null,"html_url":"https://github.com/nekromoff/bicycle-master-plan","commit_stats":{"total_commits":224,"total_committers":5,"mean_commits":44.8,"dds":0.1071428571428571,"last_synced_commit":"a784b90a9f8dce3329358c5c60424b37c9d30941"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nekromoff%2Fbicycle-master-plan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nekromoff%2Fbicycle-master-plan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nekromoff%2Fbicycle-master-plan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nekromoff%2Fbicycle-master-plan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nekromoff","download_url":"https://codeload.github.com/nekromoff/bicycle-master-plan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245108282,"owners_count":20562019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["app","bicycle","bicycle-infrastructure","bike","cycling","cycling-safety","exif-tags","google-sheets","gps-data","map","mapping","mapping-tools","maps","mapserver","markers","openstreetmap","openstreetmaps","osm","overpass-api","overpass-turbo"],"created_at":"2024-08-01T19:01:24.619Z","updated_at":"2025-03-23T13:32:22.158Z","avatar_url":"https://github.com/nekromoff.png","language":"PHP","funding_links":[],"categories":["PHP","Projects"],"sub_categories":[],"readme":"# Bicycle Master Plan\nBicycle Master Plan is a tool / web app for displaying various bicycle infrastructure related data on a map. Actually, you can use any OpenStreetMap / custom data on a map, not just bicycle-related data. Extensive visual customization is possible by automatical conversion of OSM keys and values to CSS classes.\n\nIt supports the following sources of data:\n- OSM data (JSON format; tiles, paths/ways and markers)\n- database data (seed your database; markers)\n- GPS EXIF-tagged photos (automatic using default seeder; markers)\n- custom feed data (from any public URL/API in JSON format; markers)\n- custom data (from rows in Google Sheets; markers only)\n- data provided by users via form (if editable layer enabled; markers)\n\nBuilt with:\n- Laravel (PHP)\n- Leaflet (JS)\n\n## Demo / working version for the city of Bratislava:\nhttps://mapa.cyklokoalicia.sk/bratislava/public/\n\n## Installation\n1. download the code (git clone or ZIP file)\n2. run `composer install` to download dependencies\n3. run `php artisan migrate` to setup database\n4. check/set `public/.htaccess` file permissions, if necessary\n5. check/set `storage/app/` permissions, if necessary (755 for writing)\n6. create `storage/app/public/uploads/` directory (755), if an editable layer is enabled\n7. create `storage/app/osm/` directory (755), if OSM layer download is enabled (see below)\n8. symlink `public/storage/` (TARGET) to `storage/app/public/uploads/` (SOURCE), if an editable layer is enabled (see https://laravel.com/docs/7.x/filesystem#the-public-disk)\n\n## Configuration\n1. Edit `config/map.php`\n    - Set basic info such as map name, language, bounding box, center and zoom\n    - Configure `layers`:\n        - a base layer (usually a background map tile layer) is always `0` in config file\n        - `type` = `path`, `marker`, `combined`\n        - `name` = name of a layer (can contain HTML tags)\n        - `class` = CSS class to be used to mark up layer items (markers / paths)\n        - `icon` = layer item icon (markers only) will be created from either `name` or `filename` in database\n        - *optional* `file` = OSM JSON file containing layer content (markers or paths/ways downloaded from OSM)\n        - *optional* `cluster` = `true` for layers to group/cluster items/markers\n        - *optional* `editable` = `true` for the user editable layer (user submitted items require admin approval, see below)\n        - *optional* `types` = *array* a layer can contain multiple types of items such as different sets of markers etc.\n    - *optional* Configure OSM data/layers to download:\n        - `osm_server` = `https://lz4.overpass-api.de/api/interpreter` (use any OSM server)\n        - `osm_data` = *array* of map layers with `file` parameter:\n            - `file` = filename to save the file\n            - `data` = overpass query to download OSM data, e.g. `[out:json]; (relation[network=lcn]({{bbox}}); ); out body; \u003e; out skel qt;`, see https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_API_by_Example\n    - *optional* Configure admin emails (see below on how to enable admin functionality):\n        - `admins` = *array* of admin emails, e.g. ['someone@example.com', 'other@example.com']\n2. *optional* If you enable an editable layer, user submitted items require admin approval to be displayed. Set `approved` column to `1` in database. If user marked an item as `outdated` (`1` in database), set `deleted` to `1` in database to hide it from a map.\n3. *optional* If you want to display bikeshare data on your map, see file `config/bikeshare.example.php` for details on how to enable data download (public bikeshare API endpoint required)\n4. *optional* If you want to display additional custom data/markers on your map from **Google Sheets**, see `config/google.example.php` file on how to configure it (you will need to create a project with service access with `client_id` and JSON `keyfile` at https://console.developers.google.com)\n5. *optional* If you require admin functionality, you will need to obtain client ID + client secret from OAuth2 credentials at https://console.developers.google.com. Once you have these, edit `config/services.php` and add the following lines:\n```\n'google'    =\u003e [\n        'client_id'     =\u003e 'your_client_id',\n        'client_secret' =\u003e 'your_client_secret',\n        'redirect'      =\u003e 'callback URL', // callback URL for OAuth authentication, e.g. http://example.com/login/google/callback\n    ],\n```\n\n## Customization / map style\nAll standard map tiles providers are supported.\n\nOpen `public/css/main.css` to customize layer markers or styles of paths etc. SVG properties (`fill`, `stroke` etc.) need to be used for styling paths/OSM ways, see https://css-tricks.com/svg-properties-and-css/.\n\nExample of path classes created from OpenStreetMap data (bicycle lane):\n```\nclass=\"path cycleway-left-lane cycleway-right-shared_lane foot-use_sidepath highway-residential lit-yes maxspeed-30 name-dunajska name-hu-dunautca surface-asphalt trolley_wire-yes\"\n```\n\nExample of marker classes created from OpenStreetMap data (bicycle parking):\n```\nclass=\"marker access-private amenity-bicycle_parking covered-yes surveillance-yes parking\"\n```\n\nAny combinations of keys / values can be easily styled for your purposes by using standardized CSS.\n\n## Automatic data update\nSetup cron to call refresh URLs daily (or other interval), e.g.:\n```\n15 0 * * * /usr/bin/curl --silent https://example.com/public/refresh/osm \u003e/dev/null 2\u003e\u00261\n```\nUpdate endpoints are:\n- OSM data: `/refresh/osm`\n- Bikeshare feed data: `/refresh/bikeshare`\n- Google sheets data: `/refresh/googlesheet`\n- Feed data: `/refresh/feed`\n\n## Examples\n\n### Markers created from photos automatically\n1. Upload photos/files to `public/photos` directory (create this directory, if it does not exist)\n2. run `php artisan db:seed --class=PhotosSeeder`\n3. Seeder will process all photos in the directory and extract their GPS coordinates from EXIF tags and add them to database as markers\n4. Enable photo layer by editing `config/map.php` (enabling clustering will help with large number of photos) and adding this code to `layers`:\n```\n1 =\u003e [\n    'type'  =\u003e 'marker',\n    'name'  =\u003e 'Your photos',\n    'class'   =\u003e 'photo',\n    'icon'    =\u003e 'filename',\n    'cluster' =\u003e true,\n    'options' =\u003e [\n        'disableClusteringAtZoom' =\u003e 17,\n    ],\n],\n```\n5. Refresh your map to see your photos\n\n### OpenStreeMap cycling paths and bike sharing stations\n1. Edit `config/map.php` and add a layer (change number `5` to suit your purposes) to the `layers`:\n```\n5 =\u003e [\n    'type'  =\u003e 'path',\n    'name'  =\u003e 'Cycling paths\u003cbr\u003e\u003cspan class=\"cycleway-lane\"\u003e━━━\u003c/span\u003e Segregated\u003cbr\u003e\u003cspan class=\"cycleway-shared_lane\"\u003e━━━\u003c/span\u003e Shared\u003cbr\u003e\u003cspan class=\"lcn-provisional\"\u003e• • • •\u003c/span\u003e Recommended\u003cbr\u003e\u003cspan class=\"highway-pedestrian\"\u003e━━━\u003c/span\u003e Pedestrianized\u003cbr\u003e\u003cspan class=\"mtb-scale\"\u003e━━━\u003c/span\u003e For mountain bikes',\n    'class' =\u003e 'ways',\n    'file'  =\u003e 'ways.json',\n],\n```\n2. Add OSM instructions for fetching data in `config/map.php`. Change `network` operator name to your city's one (e.g. `Slovnaft BAjk` for Bratislava):\n```\n// OSM data to fetch\n'osm_server'     =\u003e 'https://lz4.overpass-api.de/api/interpreter',\n'osm_data'       =\u003e [\n    [\n        'file' =\u003e 'ways.json',\n        'data' =\u003e '[out:json]; (way[cycleway]({{bbox}}); way[\"cycleway:left\"]({{bbox}}); way[\"cycleway:right\"]({{bbox}}); way[highway=pedestrian]({{bbox}}); way[highway=cycleway]({{bbox}}); way[bicycle=yes]({{bbox}}); way[bicycle=official]({{bbox}}); way[lcn]({{bbox}}); way[bicycle=designated]({{bbox}}); ); out body; \u003e; out skel qt;',\n    ],\n    [\n        'file' =\u003e 'bikeshare-sb.json',\n        'data' =\u003e '[out:json]; (node[network=\"Slovnaft BAjk\"]({{bbox}}); ); out body; \u003e; out skel qt;',\n    ],\n],\n```\n\n### Bicycle parking stands from OpenStreeMap\n1. Edit `config/map.php` and add a layer (change number `2` to suit your purposes) to the `layers`:\n```\n2   =\u003e [\n            'type'  =\u003e 'marker',\n            'name'  =\u003e 'Bicycle parking\u003cbr\u003e\u003cspan class=\"parking\"\u003e\u003c/span\u003e \u003cspan class=\"parking bicycle_parking-rack\"\u003e\u003c/span\u003e \u003cspan class=\"parking bicycle_parking-shed\"\u003e\u003c/span\u003e Safe\u003cbr\u003e\u003cspan class=\"parking bicycle_parking-anchors\"\u003e\u003c/span\u003e Unsuitable\u003cbr\u003e\u003cspan class=\"amenity-bicycle_repair_station\"\u003e\u003c/span\u003e Public pump and tools',\n            'class' =\u003e 'parking',\n            'file'  =\u003e 'parking.json',\n],\n```\n2. Add OSM instructions for fetching data in `config/map.php`:\n```\n'osm_server'     =\u003e 'https://lz4.overpass-api.de/api/interpreter',\n'osm_data'       =\u003e [\n    [\n        'file' =\u003e 'parking.json',\n        'data' =\u003e '[out:json]; (node[amenity=\"bicycle_parking\"]({{bbox}}); node[\"amenity\"=\"bicycle_repair_station\"]({{bbox}}); ); out body; \u003e; out skel qt;',\n    ],\n],\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnekromoff%2Fbicycle-master-plan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnekromoff%2Fbicycle-master-plan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnekromoff%2Fbicycle-master-plan/lists"}