├── map.css
├── LICENSE
├── geojson.php
├── index.html
├── multiple-map-layers-example.html
├── README.md
└── SC-CODES-README.md
/map.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0;
3 | margin: 0;
4 | }
5 | html, body, #map {
6 | height: 100%;
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2023 - HackGreenville Labs
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/geojson.php:
--------------------------------------------------------------------------------
1 | 'Unable to Change PHP Socket Timeout');
15 | $error = TRUE;
16 | } // end if, set socket timeout
17 |
18 | // if the opening the CSV file handler does not fail
19 | if ( !$error && (($handle = fopen($googleSpreadsheetUrl, "r")) !== FALSE) )
20 | {
21 | // while CSV has data, read up to 10000 rows
22 | while (($csvRow = fgetcsv($handle, 10000, ",")) !== FALSE)
23 | {
24 | $rowCount++;
25 |
26 | if ($rowCount == 1) { continue; } // skip the first/header row of the CSV
27 |
28 | $features[] = array(
29 | 'type' => 'Feature',
30 | 'geometry' => array(
31 | 'type' => 'Point',
32 | 'coordinates' => array(
33 | (float) $csvRow[1], // longitude, casted to type float
34 | (float) $csvRow[0] // latitude, casted to type float
35 | )
36 | ),
37 | 'properties' => array(
38 | 'title' => $csvRow[2],
39 | 'notes' => $csvRow[3],
40 | 'property3' => $csvRow[4]
41 | )
42 | );
43 | } // end while, loop through CSV data
44 |
45 | fclose($handle); // close the CSV file handler
46 |
47 | $output = array(
48 | 'type' => 'FeatureCollection',
49 | 'features' => $features
50 | );
51 | } // end if , read file handler opened
52 |
53 | // else, file didn't open for reading
54 | else
55 | {
56 | $output = array('error' => 'Problem Reading Google CSV');
57 | } // end else, file open fail
58 |
59 | // convert the PHP output array to JSON "pretty" format
60 | $jsonOutput = json_encode($output, JSON_PRETTY_PRINT);
61 |
62 | // render JSON and no cache headers
63 | header('Content-type: application/json; charset=utf-8');
64 | header('Cache-Control: no-cache, must-revalidate');
65 | header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
66 | header('Access-Control-Allow-Origin: *');
67 |
68 | print $jsonOutput;
69 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
14 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | See https://data.openupstate.org/map-layers for existing open Greenville map layers and GeoJSON data.
2 |
3 | ### Why?
4 | Go to Google Maps, zoom into Greenville, SC and type "bike racks" or "dog parks". If you're lucky you get decent info, but inevitably you'll be looking at incomplete or irrelavant results. You may even get advertisments.
5 |
6 | This is not to say we should make a public-version of Google Map. Actually, we already have that in OpenStreetMaps.
7 |
8 | Rather, lots of great public infomation is locked inside all maps, proprietary and open maps alike, in much the same way styling was locked inside of HTML before CSS.
9 |
10 | The current lock-in approach reduces:
11 | * accuracy of updates
12 | * the speed of changes
13 | * the scope of sharing
14 | * the ability to mix and match layers looking for new patterns (what do we see if we overlay tree planting data with census data?)
15 | * portability across applications (browser vs tablet app vs GIS tools)
16 | * depending on the source, oversight and fairness
17 |
18 |
19 | ### What's the Problem with Current Sharing Methods?
20 | Lets say you found park data on the city's GIS system and exported it out to a file. Now, you have the bright idea to build a Google Map with a layer for the parks, plus another layer for bathroom locations.
21 |
22 | You share it with friends and 5 of them love the idea. They make their own map with your city parks + their own interests (bike racks, breweries, dog parks, etc).
23 |
24 | Everyone goes about sharing and copying the layer data they like the most into their own personal maps.
25 |
26 | Cool, we have 6 interesting maps.
27 |
28 | One week later a dog park closes due to clown sightings. Spring comes and the city has a new park and 3 new breweries. Oh, and all the bike rakes were moved next to parking decks.
29 |
30 | Now what? Well, things get stale and maps die, that's what.
31 |
32 |
33 | ### How We Solve the Problem
34 | 1. A Google Docs / Drive Spreadsheet is used as a real-time data source that virtually anybody can help maintain.
35 | 2. A PHP script reads the published Google spreadsheet in real-time and converts that into a [GeoJSON format](http://geojson.org/geojson-spec.html)
36 | 3. The spreadsheet is now a public, sharable, standardized GeoJSON URL (via the geojson.php) which anybody can reference or edit in real-time.
37 | 4. Any tool which understands GeoJSON, like [LeafletJS](http://leafletjs.com/), can point at one or more map layer URLs and magically show fresh data.
38 |
39 |
40 | ### Start With a New Google Spreadsheet
41 | * Make a copy of the [base spreadsheet template](https://docs.google.com/spreadsheets/d/10eNXFh6mzFtii7B2PW90jmHtrQLJlRCrf3kkHU0HIH8/edit?usp=sharing) (File -> Make a Copy)
42 | * Rename your copy and start adding real "point" data, including geographic coordinates (longitude, latitude) and other properties
43 |
44 | ### Publish Your Google Spreadsheet
45 | * Go to (File -> Advanced -> Publish to Web).
46 | * There are two drop-down boxes near the top of the box that opens.
47 | * For the left drop-down box select "Entire Document"
48 | * For the right drop-down box select "Comma-seperated values (.csv)
49 | * Now click the Publish button
50 | * A URL will be provided. It looks like https://docs.google.com/spreadsheets/d/{a-bunch-of-random-numbers-and-letters}/pub?output=csv
51 |
52 | Additions or changes to the spreadsheet will appear on a refreshed map somewhere between immediately and a few minutes later.
53 |
54 | You should plan on only having 1 tab in the spreadsheet. However, if for some reason you want to point at the data in the second tab within the spreadsheet then open that tab in you browser and look in the URL for *gid=##########*. You would need to append ``&gid={your-tabs-gid-here}&single=true`` to the end of the CSV URL above to target that tab.
55 |
56 | ### Allowing Other People to Help Curate the Data
57 |
58 | Use Google's *Share* function to give *Edit* permissions. You should consider allowing "Anyone with the link" to edit the data and then all you need to do is share the edit URL with trusted people.
59 |
60 | ### Using PHP to Convert a Google Sheets CSV to a GeoJSON File
61 | The "CSV data source URL" to be used in the geojson.php PHP script is the URL from the "File -> Publish to Web" step
62 |
63 | ex. ``https://docs.google.com/spreadsheets/d/{a-bunch-of-random-numbers-and-letters}/pub?output=csv``
64 |
65 | Insert that URL in the $googleSpreadsheetUrl variable near the top of geojson.php file in your editor / workspace.
66 |
67 | The column values are used in this example to generate the GeoJSON are hard-coded to include 4 fields: longitude, latitude, title, and notes.
68 |
69 | If you need more columns / fields to your spreadsheet then you can add additional *properties* in your geojson.php under the ``$features['properties']`` array section and then include them in the map pop-up bubble by modifying the ``popuphtml`` variable within index.html
70 |
71 | You'd be best not to change order of the latitude and longitude columns in the spreadsheet. If you must change the order then you'd need to modify the corresponding code in geojson.php and index.html
72 |
73 | ### Getting Longitude and Latitude
74 |
75 | Non-programming / manual ways to get latitude and longitude numbers
76 | * (Easiest) Go to MapQuest, zoom in and center the position you want in the middle of the map and right click on the spot you want. The pop-up will show the lat and long.
77 | * (OK) In Google Maps zoom into a point. The URL in your browser will contain the center point's latitude and longitude (in that order) ex: 34.8509174,-82.3987371
78 | * (Hard, but good for looking up lots of data) If you know how to do custom functions in Google Sheets then you can [convert an address into latitude and longitude](https://ctrlq.org/code/19992-google-maps-functions-for-google-script) with some customization.
79 |
80 | ### Rendering a Leaflet Map Showing the GeoJSON Data
81 | The index.html is where you look to see the actual map. There is already a line of code to load the geojson.php into a ``$geoJsonData`` Javascript variable and render that to the Leaflet map. So, all you have to do is open the index.html in your browser.
82 |
83 | [Leaflet JS can use many different providers of map tiles](https://github.com/leaflet-extras/leaflet-providers). Though, each provider has it's own pricing and limitations. This project initially used MapQuest raster tiles, but the current version uses a free OpenStreetMap tile server (for very low volume use cases).
84 |
85 | Please read each provider's documentation and pricing. Many have a free tier, but they may only be allowed for non-commercial use.
86 |
87 | Also, each provider should have different documentation for applying their map tiles, so this example code may not work without notable changes to the section of the Javascript the connects the map tiles to the map.
88 |
89 | ### Testing
90 | The geojson.php file will need a running "server" in order to process. You can use a locally running server PHP server, including [PHP's built-in server](https://www.php.net/manual/en/features.commandline.webserver.php). Or, any web server, like Apache or Nginx, can be configured to process PHP files.
91 |
92 | ### Multiple Layers and Advanced Leaflet
93 | [Leaflet has a bunch of other plug-ins and options](https://leafletjs.com/index.html), so the maps can be tweaked in all sorts of ways.
94 |
95 | ----
96 | Original concept from [OpenData Day 2014 in Greenville SC](https://github.com/OpenUpstate/OpenDataDay2014)
97 |
--------------------------------------------------------------------------------
/SC-CODES-README.md:
--------------------------------------------------------------------------------
1 | # SC Codes Project Goals
2 |
3 | ## Getting Started
4 | 1. Setup a Cloud9 account using the invite sent via email by Pamela (if it asks for a credit card contact Pamela)
5 | 1. Connect your GitHub account to Cloud9 [via Connected Services](https://c9.io/account/services)
6 | 1. Understand the [broader purpose of this open data project](https://github.com/hackgvl/leaflet-google-sheets-template/blob/master/README.md)
7 | 1. Start thinking about ideas for open/public data you'd like to maintain or co-maintain.
8 |
9 | ### Ideally, your data set should:
10 | * already be publicly discoverable
11 | * consist of [longitude, latitude] "point data" (shapes or paths will be too advanced and won't work with the sample documentation)
12 | * be relatively static, meaning not changing more than once every couple months
13 | * be something commercial maps (GPS, Google) don't already do accurately and completely
14 | * be more than 5 lat/long points, but not so huge it's hard to gather or maintain
15 | * be something you could help keep up to date in your free time, even after SC Codes wraps up
16 | * be specific to Greenville city, Greenville county, or the Upstate (at the broadest)
17 | * be complete (if you can't gather all / most of the possible points then consider something else)
18 |
19 | Non-profits, the City, and the County would be ideal examples of places to find existing public data that's trapped. In other cases, you may need to do the research / leg work to create the data.
20 |
21 | Good example: The [City has a map of parking decks](http://www.greenvillesc.gov/513/Parking) but the data was stuck inside of the embedded Google map at the bottom. You can't easily take that map and pull it into your own map. Wouldn't it be great if it was a real-time GeoJSON-based data layer? (Answer: yes)
22 |
23 | Bad Examples: Things like Greenville restaurants, potholes, or vape shops. These change frequently and/or are already done well enough by other services.
24 |
25 | ### Map Data Ideas and Staking Your Claim
26 |
27 | The [first two classes of SC Codes implemented many ideas](https://data.openupstate.org/map-layers). As such, your class has the option of creating a new, original idea OR helping co-maintain an existing map layer.
28 |
29 | If you opt to [co-maintain an existing map layer](https://data.openupstate.org/map-layers) then click on the map title and then click the "Contribute Link" under that maps details. It should open a Google Spreadsheet and you may see a blue "View only" button. Click that View only button and you can request edit access from the original owner of the spreadsheet. If you try to access an existing spreadsheet and get an error then contact Jim and Pamela on Slack.
30 |
31 | Once you've settled on an idea, whether new or co-maintaining, add your name and the idea to [the list](https://docs.google.com/spreadsheets/d/1IWsFT1p0ZY-DInfMOFq_gmqpGuKyl5wyBb9VoyoEjRs/edit#gid=1517769371) to stake your claim.
32 |
33 | ## Putting it All Together
34 | 1. Fork the GitHub Project
35 | 1. Setup a Cloud9 Workspace that talks with your GitHub fork
36 | 1. Do a basic Git command and push to GitHub
37 | 1. Get the map and spreadsheet working together
38 |
39 | ### Steps on GitHub
40 | 1. Decide on which data set you want to build
41 | 1. Go to [our template GitHub project/repo](https://github.com/hackgvl/leaflet-google-sheets-template) and click "Fork" in the top-left
42 | 1. We are [forking](https://help.github.com/articles/fork-a-repo/) with the intention of creating a starting point for your project. We will not get into "pull requests", but understand that people also fork projects as a way to create a branch/copy and then contribute back to the original project.
43 | 1. If you see a pop-up that says "Where should we fork this repository?" then select your personal user name.
44 | 1. GitHub will create a copy of the template repo. You'll see this under your GitHub account, like yourusername/leaflet-google-sheets-template
45 | 1. On your fork select the "Settings" (tab with the gear icon).
46 | 1. In the "Repository name" box enter a name relevant to your data set. It should be 35 characters or less. Ex. ``gville-map-layer-parking-decks``
47 | 1. Click the "Rename" button
48 |
49 | ### Steps to Setup a C9.io Workspace that Uses Your GitHub Fork
50 | 1. Go back to [your Cloud9 repositories](https://c9.io/account/repos) and refresh the page.
51 | 1. The forked repository you created over at GitHub should be listed as an option.
52 | 1. Click the "Clone to Edit" button associated with your fork repo.
53 | 1. For the "Workspace name" enter the same repo name you used on GitHub. The name should be less than 35 characters. Ex. ``gville-map-layer-parking-decks``
54 | 1. Leave the "Team" set to "SC Codes", leave the "Hosted Workspace" set to "Public" and the "template" as "HTML 5".
55 | 1. Click "Create workspace".
56 | 1. The end result is that you've forked a project / repo in GitHub and now have a full copy of all the sample code. This means you can now start changing code and pushing your changes back up to your own GitHub fork.
57 |
58 | ### Steps to Start Programming and using Git in Your Fancy C9 Workspace
59 | 1. When your new workspace opens you'll see the terminal tab (the bottom-left tab) at the bottom of the screen. Drag the top line to make the terminal tab area larger and easier to see.
60 | 1. This is a real Linux-style bash shell. You can type Linux commands in here, including Git commands.
61 | 1. Type ``git config -l`` (lower case L, not #1) and press Enter. This will show various bits of info about the Git configuration of the active C9 workspace. Notice the "remote.origin.url" which should be pointing to your GitHub fork URL.
62 | 1. We'll run a basic git command as an example of using Git, GitHub, and the Cloud9 workspace.
63 | 1. In the terminal tab type ``ls`` and you'll see a list of all the files in the current directory
64 | 1. The ``SC-CODES-README.md`` file isn't necessary in your fork, since you already did all these steps. Let's delete it with Git and make that change reflect on your GitHub project
65 | 1. In the terminal run ``git rm SC-CODES-README.md`` and press Enter. (Pro Tip: You could also type ``git rm SC`` and then hit tab key. The terminal shell will autocomplete the rest of the file name for you)
66 | 1. Press the up arrow until you see the ``ls`` (lowercase LS) command again. When you see it press Enter. This shows you two things: 1) the SC-CODES-README.md is now gone and 2) you can use the up / down arrows to scroll through recent commands
67 | 1. Run ``git status`` and you'll see that git is ready for you to permanently commit your changes (1 deleted file).
68 | 1. Run ``git commit -m 'Deleted an SC Codes related README file'``
69 | 1. To push this commit up to the GitHub fork repository you now need to run ``git push``
70 | 1. Go back to your fork page on GitHub and refresh the page. You should no longer see the deleted file and you should notice your commit message.
71 |
72 | ### Steps to Get the Map and Spreadsheet Working
73 | 1. Now, read the main [README.md](https://github.com/hackgvl/leaflet-google-sheets-template/blob/master/README.md) file for details on how to create a public spreadsheet and a Leaflet map.
74 | 1. Side note: You can ignore messages like "This branch is 1 commit ahead, 1 commit behind hackgvl:master." on your GitHub fork page. This means changes were made to the project you forked. You're on your own now and don't need to keep in sync with the fork's origin.
75 |
76 | ## Wrapping Up
77 | 1. Add your project specific URLs to the [open data list](https://docs.google.com/spreadsheets/d/1IWsFT1p0ZY-DInfMOFq_gmqpGuKyl5wyBb9VoyoEjRs/edit#gid=1517769371)
78 | 1. Git add, commit, and push your changes from Cloud 9 up to GitHub.
79 |
80 | ### Share Your Work
81 | Please share your Google Spreadsheet link, spreadsheet CSV link, GeoJSON link, Map Preview link, and GitHub link in the [open data list](https://docs.google.com/spreadsheets/d/1IWsFT1p0ZY-DInfMOFq_gmqpGuKyl5wyBb9VoyoEjRs/edit#gid=1517769371) in the yellow highlighted boxes.
82 |
83 | Through the power of GitHub, we'll clone your map and GeoJSON to https://data.openupstate.org/map-layers where it will be publically available to the broader community.
84 |
85 | ### Git Commit and Push
86 | Earlier, you changed the index.html and geojson.php. Let's add and commit these chages to version control using Git.
87 |
88 | Go to your Cloud 9 workspace and click in the terminal tab. It should be the bottom-left tab that says "bash".
89 |
90 | In the terminal, run ``git status`` and Git will tell you about the modified files. These are "Changes not staged for commit".
91 |
92 | You can also run ``git diff`` to see a "differential" of what's modified but not yet "staged" . You can use the arrow up/down or page up/down keys to scroll through the diff. There are + and - symbols showing which lines in the code are new or removed. If you see a colon (:) at the bottom of the terminal then you probably need to type the letter q (for quit) to escape out and back to the main terminal.
93 |
94 | Run ``git add index.html`` and then ``git add geojson.php`` to stage the files for commiting.
95 |
96 | Run ``git status`` again and it will show the files under "Changes to be committed"
97 |
98 | Commit the staged changes using ``git commit -m 'Customized Leaflet and PHP GeoJSON files to pull from my own Google Spreadsheet and get the map working'``
99 |
100 | Now let your GitHub repo know about your commit by running ``git push origin master`` or the shortcut ``git push``
101 | The repo on GitHub is configured as your git remote "origin" and you only have a single "master" branch, hence pushing to origin master sends changes from your master branch up to GitHub.
102 |
103 | ## Goals for Week 5
104 | 1. Add a Git upstream, merge in changes from the upstream, resolve a simple merge conflict
105 | 1. (Optional) Choose your adventure if you want to flex your coding skills on more advanced mapping concepts.
106 |
107 | ### Git Upstream, Merge, Conflict Resolution
108 | When you fork a GitHub repository it's often, but not always, that you want to merge changes from original project back into your copy. In this case the original repo is called the "upstream".
109 |
110 | As a simple example, the README.md and SC-CODES-README.md have been changed in the upstream since you forked. Let's merge in the changes.
111 |
112 | Right now you have a single master branch and a link to the remote origin master (on GitHub). You can see this by listing all the branches Git knows about.
113 |
114 | In the terminal, run ``git branch -a``
115 |
116 | We're going to tell Git where to find the ["remote upstream"](https://help.github.com/articles/configuring-a-remote-for-a-fork/) pointing at our [original template project](https://github.com/hackgvl/leaflet-google-sheets-template) by running ``git remote add upstream https://github.com/hackgvl/leaflet-google-sheets-template.git``
117 |
118 | We want to grab a copy of the [remote upstream](https://help.github.com/articles/syncing-a-fork/).
119 |
120 | ``git fetch upstream``
121 |
122 | Run ``git branch -a`` again and you'll see a new branch "remotes/upstream/master"
123 |
124 | This fetched the latest upstream code into a branch within your Cloud 9 environment. Note, you won't see anything change within Cloud 9, but your local Git has a copy of the upstream master branch at your disposal.
125 |
126 | Now merge the upstream code into your code.
127 | ``git merge upstream/master``
128 |
129 | You'll see a message like "CONFLICT (modify/delete): SC-CODES-README.md deleted in HEAD and modified in upstream/master. Version upstream/master of SC-CODES-README.md left in tree. Automatic merge failed; fix conflicts and then commit the result."
130 |
131 | Run ``git status`` to see more information. It will show a ["merge conflict"](https://help.github.com/articles/resolving-a-merge-conflict-from-the-command-line/). On the one hand, you deleted (in week 9) the SC-CODES-README.md file while the upstream still has a copy which was also edited since you forked it.
132 |
133 | Let's tell Git that we still don't care need a copy of SC-CODES-README.md in our fork by removing the merged in changes ``git rm SC-CODES-README.md``
134 |
135 | You'll also see that the upstream changed the README.md. You can compare what changed between your fork and the upstream README.md by running ``git diff README.md``
136 |
137 | We want to keep these merged README.md changes, so tell git to add them ``git add README.md``
138 |
139 | Instruct git to commit these changes
140 | ``git commit -m 'Merge in the README.md from the remote upstream'``
141 |
142 | Push the commit up to the GitHub remote copy
143 | ``git push origin master``
144 |
145 | Congratulations, you've done your first "merge" and resolved a "merge conflict".
146 |
147 | Side note, knowing how to use a remote upstream is one of the steps for creating a ["pull request"](https://help.github.com/articles/creating-a-pull-request/). A pull request is how GitHub allows you to propose a merge into someone's GitHub repository. For instance, if you wanted to contribute a fix to an open-source project that hosts its code on GitHub.
148 |
149 | ### Optional Advanced Map Challenges
150 | Here are sample ideas. Be aware of cross-origin issues if you're trying to load GeoJSON across domains using javascript.
151 |
152 | 1. Create a new layers.html file in your C9 workspace and add another student's geojson.php as a second Leaflet layer
153 | 1. Add [custom map icons in Leaflet](http://leafletjs.com/examples/custom-icons/)
154 | 1. Build an equivalent [Google Map by loading your geojson.php as a layer](https://developers.google.com/maps/documentation/javascript/datalayer#load_geojson) by creating a googlemap.html in Cloud9.
155 | 1. Test out other [Leaflet functions](http://leafletjs.com/reference-1.0.2.html)
156 |
--------------------------------------------------------------------------------