Leaflet – leafletjs.com

Web Maps with Leaflet
Presented at the Annual Conference of the
Organization of Fish and Wildlife Information Managers
September 30, 2014 • Flagstaff, Arizona
Dyanne Fry Cortez • Texas Parks and Wildlife Department
dyanne.cortez@tpwd.texas.gov
Maps to Browse
Texas State Parks - www.tpwd.texas.gov/state-parks/
Texas Lake Finder - www.tpwd.texas.gov/fishboat/fish/recreational/lakes/
Where ShareLunkers Come From www.tpwd.texas.gov/spdest/visitorcenters/tffc/sharelunker/lunkerlocations.phtml
Demonstration map of Austin Public Libraries - www.sarasafavi.com/maps/libraries.html
Loads GeoJSON layer made from shape file downloaded from a public data server
Helpful Links
Leaflet – leafletjs.com
GeoJSON tutorial - leafletjs.com/examples/geojson.html
GeoJSON specs - http://geojson.org/geojson-spec.html
OpenStreetMaps tile usage http://wiki.openstreetmap.org/wiki/Tile_usage_policy and
http://wiki.openstreetmap.org/wiki/Mapquest#MapQuest-hosted_map_tiles
Sample Code from Demonstration Maps
Lakes of the South Texas Plains
www.tpwd.texas.gov/fishboat/fish/recreational/lakes/southtex_demo.phtml
In HTML <head> section:
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css" />
In <body>:
<div id="map" style="width: 90%; height: 520px; border: 1px solid black;"> </div>
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script>
var map = L.map('map').setView([28.0242,-98.769], 7);
L.tileLayer('http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png',
{attribution: 'Tiles Courtesy of <a href="http://www.mapquest.com/"
target="_blank">MapQuest</a>, &copy; <a
href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>
contributors.'}).addTo(map);
var lakeIcon = L.icon({
iconUrl: '/fishboat/fish/recreational/lakes/images/icons/leaflet_marker_grey.png',
iconSize: [20, 32], // size of the icon
iconAnchor: [10, 31], // point of the icon which will correspond to marker's location
popupAnchor: [0, -35] // point from which the popup should open relative to the iconAnchor
});
// add points
L.marker([28.7789, -99.8281], {icon: lakeIcon, title: 'Averhoff'}).addTo(map)
.bindPopup("<b>Averhoff Reservoir</b><br /><a
href=\"/fishboat/fish/recreational/lakes/averhoff\">Fishing Information</a>");
L.marker([29.2425, -98.3752], {icon: lakeIcon, title: 'Braunig'}).addTo(map)
.bindPopup("<b>Braunig Lake</b><br /><a
href=\"/fishboat/fish/recreational/lakes/braunig\">Fishing Information</a>");
L.marker([29.2794, -98.3036], {icon: lakeIcon, title: 'Calaveras'}).addTo(map)
.bindPopup("<b>Calaveras Lake</b><br /><a
href=\"/fishboat/fish/recreational/lakes/calaveras\">Fishing Information</a>");
L.marker([27.534, -99.4469], {icon: lakeIcon, title: 'Casa Blanca'}).addTo(map)
.bindPopup("<b>Casa Blanca International</b><br /><a
href=\"/fishboat/fish/recreational/lakes/casa_blanca\">Fishing Information</a>");
L.marker([28.4825, -98.2435], {icon: lakeIcon, title: 'Choke Canyon'}).addTo(map)
.bindPopup("<b>Choke Canyon Reservoir</b><br /><a
href=\"/fishboat/fish/recreational/lakes/choke_canyon\">Fishing Information</a>");
L.marker([26.5579, -99.1598], {icon: lakeIcon, title: 'Falcon'}).addTo(map)
.bindPopup("<b>Falcon Reservoir</b><br /><a
href=\"/fishboat/fish/recreational/lakes/falcon\">Fishing Information");
L.marker([27.7907, -98.061], {icon: lakeIcon, title: 'Findley'}).addTo(map)
.bindPopup("<b>Lake Findley</b><br /><a
href=\"/fishboat/fish/recreational/lakes/findley\">Fishing Information</a>");
// Create overlay image
var imageUrl = '/fishboat/fish/recreational/lakes/images/statemaps/stregion.gif',
imageBounds = [[25.90123692658776, -100.7645521644355], [29.86978299705952,
-97.13094330328251]];
L.imageOverlay(imageUrl, imageBounds, {opacity: 0.3}).addTo(map);
</script>
Community Fishing Lakes, South Texas
Script to generate data file
<?php
[connect to database]
// Function to convert odd characters
function parseToJSON($htmlStr) {
$myStr=str_replace('<','&lt;',$htmlStr);
$myStr=str_replace('>','&gt;',$myStr);
$myStr=str_replace('"','&quot;',$myStr);
$myStr=str_replace("'",'&#39;',$myStr);
$myStr=str_replace("&",'&amp;',$myStr);
return $myStr;
}
// Select Community Fishing Lakes
$query = "SELECT w.Lake_Code, c.County_Name, w.Water_Name, w.Area, w.Longitude, w.Latitude
FROM waterbodies w, counties c
WHERE w.County_Code = c.County_ID
AND c.TRegion_ID = '5'
AND w.CFL = 'Yes'
AND w.Longitude IS NOT NULL
AND w.Longitude != '0'
ORDER BY County_Name, Water_Name";
$result = mysql_query($query);
if (!$result) {
die('Invalid query: ' . mysql_error());
}
header("Content-type: text/javascript");
// Start file, echo parent node
$myJSON = 'var cfls = {"type":"FeatureCollection","features":[';
// Iterate through the rows, printing a line for each
while ($row = @mysql_fetch_array($result)){
$myJSON .= '{"type":"Feature",';
$myJSON .= '"properties":';
$myJSON .= '{"ID":"'. $row[0] . '", ';
$myJSON .= '"name":"' . parseToJSON($row[2]) . '", ';
$myJSON .= '"county":"' . $row[1] . '",';
$myJSON .= '"size":"' . $row[3] . '"},';
$myJSON .= '"geometry":{"type":"Point","coordinates":[' . $row[4] . ',' . $row[5] . ']}';
$myJSON .= '},';
}
// End file
$myJSON .= ']}';
$myJSON = substr_replace($myJSON, "]}" ,-3);
print $myJSON;
?>
Run above script and save output to cfl5.js
Here’s a snippet:
var cfls = {"type":"FeatureCollection","features":[
{"type":"Feature","properties":{"ID":"1974", "name":"Brackenridge Park",
"county":"Bexar","size":"10.00"},
"geometry":{"type":"Point","coordinates":[-98.4724,29.4617]}},
{"type":"Feature","properties":{"ID":"2602", "name":"Converse North Park City Lake",
"county":"Bexar","size":"20.00"},
"geometry":{"type":"Point","coordinates":[-98.3129,29.5082]}},
{"type":"Feature","properties":{"ID":"1880", "name":"Earl Scott Pond", "county":"Bexar","size":"2.10"},
"geometry":{"type":"Point","coordinates":[-98.6289,29.5547]}},
{"type":"Feature","properties":{"ID":"0271", "name":"Elmendorf", "county":"Bexar","size":"30.00"},
"geometry":{"type":"Point","coordinates":[-98.5377,29.4252]}},
{"type":"Feature","properties":{"ID":"0232", "name":"Espada", "county":"Bexar","size":"19.00"},
"geometry":{"type":"Point","coordinates":[-98.4663,29.3463]}}
]}
Page to display map
www.tpwd.texas.gov/fishboat/fish/recreational/lakes/small_lakes_demo.phtml
In HTML <head> section:
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
In <body>:
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
// load data file
<script type="text/javascript" src="/fishboat/fish/recreational/lakes/media/cfl5.js"></script>
// start map code
<script>
var map = L.map('map').setView([28.0242,-98.769], 7);
L.tileLayer('http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {
attribution: 'Tiles Courtesy of <a href="http://www.mapquest.com/"
target="_blank">MapQuest</a>, &copy; <a
href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.'
}).addTo(map);
var imageUrl = '/fishboat/fish/recreational/lakes/images/statemaps/stregion.gif',
imageBounds = [[25.90123692658776, -100.7645521644355], [29.86978299705952, 97.13094330328251]];
L.imageOverlay(imageUrl, imageBounds, {opacity: 0.3}).addTo(map);
var lakeIcon = L.icon({
iconUrl: '/fishboat/fish/recreational/lakes/images/icons/0access_marker.png',
iconSize: [10, 10], // size of the icon
iconAnchor: [0, 0], // point of the icon which will correspond to marker's location
popupAnchor: [5, 5], // point from which the popup should open
});
function onEachFeature(feature, layer) {
var popupContent = "<p><b>" + feature.properties.name + "</b><br>" +
feature.properties.county + " County<br>" + feature.properties.size + " acres</p>";
layer.bindPopup(popupContent);
}
L.geoJson(cfls, {
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {icon: lakeIcon, title: feature.properties.name});
}
}).addTo(map);
</script>
ShareLunker Locations
www.tpwd.state.tx.us/spdest/visitorcenters/tffc/sharelunker/lunkerlocations.phtml
Data generator script
$query = "SELECT lunker_fish.WB_code, waterbodies.Water_Name, waterbodies.Longitude,
waterbodies.Latitude, COUNT(lunker_fish.ID)
FROM lunker_fish, waterbodies
WHERE lunker_fish.WB_code = waterbodies.Lake_Code
AND waterbodies.Longitude IS NOT NULL
AND waterbodies.Longitude != '0'
GROUP BY waterbodies.Water_Name";
$result = mysql_query($query);
if (!$result) {
die('Invalid query: ' . mysql_error());
}
// Start JSON statement
$myJSON = 'var lunkerlakes = {"type":"FeatureCollection","features":[';
// Iterate through the rows, printing a line for each
while ($row = mysql_fetch_array($result)){
$myJSON .= '{"type":"Feature",';
$myJSON .= '"properties":';
$myJSON .= '{"ID":"'. $row[0] . '", ';
$myJSON .= '"name":"' . parseToJSON($row[1]) . '", ';
$myJSON .= '"fishcount":"' . $row[4] . '"},';
$myJSON .= '"geometry":{"type":"Point","coordinates":[' . $row[2] . ',' . $row[3] . ']}';
$myJSON .= '},';
}
// End file
$myJSON .= ']}';
$myJSON = substr_replace($myJSON, "]}" ,-3);
print $myJSON;
?>
Snippet of code
var lunkerlakes = {"type":"FeatureCollection","features":[
{"type":"Feature","properties":{"ID":"0006", "name":"Alan Henry", "fishcount":"25"},
"geometry":{"type":"Point","coordinates":[-101.042,33.0602]}},
{"type":"Feature","properties":{"ID":"0014", "name":"Amistad", "fishcount":"12"},
"geometry":{"type":"Point","coordinates":[-101.057,29.4503]}},
{"type":"Feature","properties":{"ID":"0015", "name":"Amon G. Carter", "fishcount":"2"},
"geometry":{"type":"Point","coordinates":[-97.8576,33.4587]}},
{"type":"Feature","properties":{"ID":"0023", "name":"Arlington", "fishcount":"1"},
"geometry":{"type":"Point","coordinates":[-97.1994,32.7211]}},
{"type":"Feature","properties":{"ID":"0763", "name":"Waco", "fishcount":"1"},
"geometry":{"type":"Point","coordinates":[-97.1995,31.5811]}},
{"type":"Feature","properties":{"ID":"0781", "name":"White River Reservoir", "fishcount":"1"}
,"geometry":{"type":"Point","coordinates":[-101.084,33.4577]}}
]}
Page to display map
<div id="map" style="width: 98%; height: 700px; border: 1px solid black;"> </div>
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script type="text/javascript"
src="/spdest/visitorcenters/tffc/sharelunker/include/sharelunkers.js"></script>
<script>
var map = L.map('map').setView([31,-100], 6);
L.tileLayer('http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {attribution: 'Tiles Courtesy of <a
href="http://www.mapquest.com/" target="_blank">MapQuest</a>, &copy; <a
href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.'
}).addTo(map);
var lunkerIcon = L.icon({
iconUrl: '/fishboat/fish/recreational/lakes/images/icons/leaflet_marker.png',
iconSize: [20, 32], // size of the icon
iconAnchor: [10, 31], // point of the icon which will correspond to marker's location
popupAnchor: [0, -35] // point from which the popup should open relative to the iconAnchor
});
function onEachFeature(feature, layer) {
// conditional statement for plural vs. singular fishcount
if (feature.properties.fishcount > 1) {
var popupContent = "<b>" + feature.properties.name + "</b><br>"+
feature.properties.fishcount + " ShareLunkers (<a href='archives/index.phtml?wcode=" +
feature.properties.ID + "'>see list</a>)";
}
else {
var popupContent = "<b>" + feature.properties.name + "</b><br>" +
feature.properties.fishcount + " ShareLunker (<a href='archives/index.phtml?wcode=" +
feature.properties.ID + "'>details</a>)";
}
layer.bindPopup(popupContent);
}
L.geoJson(lunkerlakes, {
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {icon: lunkerIcon, title: feature.properties.name + " ("
+ feature.properties.fishcount + ")"});
}
}).addTo(map);
// Two river locations do not have lats and longs in the database and need to be added individually:
L.marker([28.5442, -99.7891], {icon: lunkerIcon, title: 'Nueces River (1)'}).addTo(map)
.bindPopup("<b>Nueces River</b><br />1 Sharelunker (<a
href=\"archives/index.phtml?wcode=1422\">details</a>)");
L.marker([29.9335, -94.4827], {icon: lunkerIcon, title: 'Spindletop Bayou (1)'}).addTo(map)
.bindPopup("<b>Spindletop Bayou</b><br />1 Sharelunker (<a
href=\"archives/index.phtml?wcode=1563\">details</a>)");
</script>