This is an attempt to create the most basic example of a web map for crowdsourcing... anything that users can draw on a map (points, squares, circles, lines, polygons...). It uses Leaflet.draw (demo), an extension of the Leaflet javascript mapping library to enable users to draw shapes on a map and then inserts them in a Carto table. The webmap is hosted on gh-pages, which allows you to host free static websites on github, the codesharing website that you're reading this on currently. The only permitted behaviour is for users to view previously submitted input and to submit their own input. Editing or deleting input is not permitted. Examples of possible implementations include:
Create a new Carto dataset. Find datasets by navigating to the top left, click on Maps
and then Your Datasets
. On that page click the NEW DATASET
button on the right and then click CREATE EMPTY DATASET
on the right.
The default dataset comes with the following fields: {cartodb_id, the_geom, description, name}
Each row represents one submission from the map with the first field a unique id assigned by Carto to each geometry. the_geom
is the geographic object. description
is the user input description of the shape, and name
is the user's name. Click on untitled_table
to give your table a unique name. Remember this name, though the rest of this tutorial uses crowdmap_basic
as the tablename.
In the bottom left corner, slide the circle from METADATA
to SQL
to enable writing arbitrary SQL. SQL stands for Structured Query Language and it's a standardized programming language for databases that comes in a number of flavours for different database engines (Oracle, Microsoft SQL Server). Carto uses PostgreSQL under the hood.
Copy and paste the contents of insert_function.sql
(located here) into the SQL pane (it has SELECT * FROM untitled_table
in the image :point_up:), and then modify the name of the table to be inserted (if you changed the table name) in step 2 of this section:
_the_table TEXT := 'crowdmap_basic';
This function allows you to send data from the map to the Carto using a publicly accessible URL while limiting what functions the public can perform on the data (for example, the public can't modify or delete existing data). This function takes the drawn shape as a GeoJSON, the description, and the username. It converts the GeoJSON to a PostGIS geometry object and then inserts a new row in the table with the geometry, and the other two user-input values. Since it isn't easy to view saved functions in Carto, I recommend saving the function in a text file.
If you have multiple tables (because you're creating multiple maps, or you're teaching this workshop multiple times) see below for more information on keeping track of multiple files. Else go down to Edit HTML
Multiple tables: you need to create a unique function for each, it's probably a good idea to save each function as a separate file so you can recall what is on your Carto account. Alternatively you can see which functions have been created with the following sql
query (source):
SELECT proname, proargnames, prosrc
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p
ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
AND p.proowner <> 10
You can download the contents of this repository as a folder (called cloning) so you can modify and test it on your personal machine rather than displaying it on the web. If you're interested in that I'd recommend starting by downloading GitHub Desktop.
However, the goal of this tutorial is to entirely use web tools, so we'll modify things in your browser. index.html
is the lobby for the webpage for this project. This is a combination of html, CSS, and JavaScript code that tells your browser how to render the map prettily and where to send your data.
index.html
filename above :point_up: and then clicking on the :pencil: icon in the top right. Modify the following variables in index.html
(search for "TODO"):
cartoDBusername
to your Carto usernamecartoDBinsertfunction
to the name of your insert function (if you changed it)cartoDBtablename
to the name of your table in Carto (if you changed it)What to do and modify on your map once it's working. Have a look at the different parameters in the config
variable in index.html
to get a sense of what you can modify:
mapcenter
parameterdrawOptions
parameters. See more Leaflet.Draw options here
var CartoDB_Positron = L.tileLayer(...)
, you can test a number herecss/style.css
. See this quirky frog-based tutorial as an introduction to Cascading Style Sheetsinsert_function
(and then re-executing this in Carto)<div id="dialog" title="Tell us About this Drawing">
in index.html
setData()
function in index.html
Once you've collected data from users, have a look at the tutorials at Carto for what kinds of maps you can make from your data.
This section details the modifications made from the excellent tutorial by Mike Foster (@mjfoster83). If this is your first introduction to leaflet, you should probably go through the entire webmapping workshop
Modify the setData()
function to construct the SQL query which calls the function to insert the data to Carto.
//Convert the drawing to a GeoJSON to pass to the Carto sql database
var drawing = "'"+JSON.stringify(layer.toGeoJSON().geometry)+"'";
//Construct the SQL query to insert data from the three parameters: the drawing,
//the input username, and the input description of the drawn shape
var sql = "SELECT insert_crowd_mapping_data(";
sql += drawing;
sql += ","+enteredDescription;
sql += ","+enteredUsername;
sql += ");";
And then add the sql query to an AJAX call in order to pass the data to your Carto table
//TODO: Change to your username
var cartoDBusername = "raphaeld"
//Sending the data
$.ajax({
type: 'POST',
url: 'https://'+cartousername+'.cartodb.com/api/v2/sql',
crossDomain: true,
data: {"q":sql},
dataType: 'json',
success: function(responseData, textStatus, jqXHR) {
console.log("Data saved");
},
error: function (responseData, textStatus, errorThrown) {
console.log("Problem saving the data");
}
});
After each new drawing is inserted, the data from the drawnItems
layer is passed to the cartoData
layer without re-querying the database. This does mean that a user won't see others' edits to the map after load. See Mike Foster's tutorial for the easy fix to reload the data from Carto after every draw.
// Transfer drawing to the Carto layer
var newData = layer.toGeoJSON();
newData.properties.description = description.value;
newData.properties.name = username.value;
cartoDBData.addData(newData);