Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve step-by-step tutorials #7254

Open
flywire opened this issue Sep 5, 2020 · 1 comment
Open

Improve step-by-step tutorials #7254

flywire opened this issue Sep 5, 2020 · 1 comment
Labels
docs Improvements or additions to documentation

Comments

@flywire
Copy link

flywire commented Sep 5, 2020

This comment specifically refers to Using GeoJSON with Leaflet but apply more generally.

A tutorial should work towards a solution step by step allowing opportunities for people to try what they are learning. The tutorial is frustrating, the examples aren't used in the demo example at the end and the files where the content occurs aren't referenced.

This tutorial could easily follow the Leaflet Quick Start Guide tutorial by adding geojson data step by step. This would ensure the preparation of the map is fully explained even though the basics are out of scope for this topic.

All of tutorial example code should come directly from the demo example but not all code in the demo example needs to be presented in more advanced tutorials.


Undoubtedly the API has changed since this tutorial was first written. It is time to review the tutorial to realign the tutorial examples and the demo example so the GeoJSON API is demonstrated. The coding style and constructs are very different between the tutorial and the demo example. For example, there are two tutorial style types and neither is a good match for the demo example.

I have changed the tutorial section below to use the demo example but it really demonstrates the demo example (including data) would be better to be reworked to demonstrate the tutorial examples:

Example improvements to the Using GeoJSON with Leaflet tutorial
---
layout: tutorial
title: Using GeoJSON with Leaflet
---

<h3>Using GeoJSON with Leaflet</h3>

<p>GeoJSON is becoming a very popular data format among many GIS technologies and services — it's simple, lightweight, straightforward, and Leaflet is quite good at handling it. In this example, you'll learn how to create and interact with map vectors created from <a href="http://geojson.org/">GeoJSON</a> objects.</p>

<div id="map" class="map" style="height: 250px"></div>

<script src="sample-geojson.js"></script>
<script>

	var map = L.map('map').setView([39.74739, -105], 13);

	L.tileLayer(MB_URL, {
		attribution: MB_ATTR,
		id: 'mapbox/light-v9',
		tileSize: 512,
		zoomOffset: -1
	}).addTo(map);

	var baseballIcon = L.icon({
		iconUrl: 'baseball-marker.png',
		iconSize: [32, 37],
		iconAnchor: [16, 37],
		popupAnchor: [0, -28]
	});

	function onEachFeature(feature, layer) {
		var popupContent = "<p>I started out as a GeoJSON " +
				feature.geometry.type + ", but now I'm a Leaflet vector!</p>";

		if (feature.properties && feature.properties.popupContent) {
			popupContent += feature.properties.popupContent;
		}

		layer.bindPopup(popupContent);
	}

	L.geoJson({features: [bicycleRental, campus]}, {

		style: function (feature) {
			return feature.properties && feature.properties.style;
		},

		onEachFeature: onEachFeature,

		pointToLayer: function (feature, latlng) {
			return L.circleMarker(latlng, {
				radius: 8,
				fillColor: "#ff7800",
				color: "#000",
				weight: 1,
				opacity: 1,
				fillOpacity: 0.8
			});
		}
	}).addTo(map);

	L.geoJson(freeBus, {

		filter: function (feature, layer) {
			if (feature.properties) {
				// If the property "underConstruction" exists and is true, return false (don't render features under construction)
				return feature.properties.underConstruction !== undefined ? !feature.properties.underConstruction : true;
			}
			return false;
		},

		onEachFeature: onEachFeature
	}).addTo(map);

	var coorsLayer = L.geoJson(null, {

		pointToLayer: function (feature, latlng) {
			return L.marker(latlng, {icon: baseballIcon});
		},

		onEachFeature: onEachFeature
	}).addTo(map);

	coorsLayer.addData(coorsField);

</script>

<p><a href="geojson-example.html">View example on a separate page &rarr;</a></p>

<h3>About GeoJSON</h3>

<p>According to <a href="http://geojson.org">http://geojson.org</a>:</p>

<blockquote>GeoJSON is a format for encoding a variety of geographic data structures. A GeoJSON object may represent a geometry, a feature, or a collection of features. GeoJSON supports the following geometry types: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection. Features in GeoJSON contain a geometry object and additional properties, and a feature collection represents a list of features.</blockquote>

<p>Leaflet supports all of the GeoJSON types above, but <a href="https://tools.ietf.org/html/rfc7946#section-3.2">Features</a> and <a href="https://tools.ietf.org/html/rfc7946#section-3.3">FeatureCollections</a> work best as they allow you to describe features with a set of properties. We can even use these properties to style our Leaflet vectors. Here's an example of a simple GeoJSON feature:</p>

<pre><code>var geojsonFeature = {
	"type": "Feature",
	"properties": {
		"name": "Coors Field",
		"amenity": "Baseball Stadium",
		"popupContent": "This is where the Rockies play!"
	},
	"geometry": {
		"type": "Point",
		"coordinates": [-104.99404, 39.75621]
	}
};
</code></pre>

Beware of the switched order of latitude and longitude in GeoJSON; as per definition in [RFC 7946](https://tools.ietf.org/html/rfc7946) GeoJSON uses coordinates in (lon,lat) order instead of (lat,lon) that Leaflet uses.

<h3>The GeoJSON layer</h3>

<p>GeoJSON objects are added to the map through a <a href="/reference.html#geojson">GeoJSON layer</a>. To create it and add it to a map, we can use the following code:</p>

<pre><code>L.geoJson(geojsonFeature).addTo(map);</code></pre>

<p>GeoJSON objects may also be passed as an array of valid GeoJSON objects.</p>

<p>This part needs extensive rework.</p>

<h4>pointToLayer</h4>

<p>Points are handled differently than polylines and polygons. By default simple markers are drawn for GeoJSON Points. We can alter this by passing a <code>pointToLayer</code> function in a <a href="/reference.html#geojson-options">GeoJSON options</a> object when creating the GeoJSON layer. This function is passed a <a href="/reference.html#latlng">LatLng</a> and should return an instance of ILayer, in this case likely a <a href="/reference.html#marker">Marker</a> or <a href="/reference.html#circlemarker">CircleMarker</a>.</p>

<p>Here we're using the <code>pointToLayer</code> option to create a CircleMarker:</p>

<pre><code>L.geoJson([bicycleRental, campus], {
	style: function (feature) {
		return feature.properties && feature.properties.style;
	},
	onEachFeature: onEachFeature,
	pointToLayer: function (feature, latlng) {
		return L.circleMarker(latlng, {
			radius: 8,
			fillColor: "#ff7800",
			color: "#000",
			weight: 1,
			opacity: 1,
			fillOpacity: 0.8
		});
	}
}).addTo(map);</code></pre>

<p>We could also set the <code>style</code> property in this example &mdash; Leaflet is smart enough to apply styles to GeoJSON points if you create a vector layer like circle inside the <code>pointToLayer</code> function.</p>

<h4>onEachFeature</h4>

<p>The <code>onEachFeature</code> option is a function that gets called on each feature before adding it to a GeoJSON layer. A common reason to use this option is to attach a popup to features when they are clicked.</p>

<pre><code>function onEachFeature(feature, layer) {
	var popupContent = "<p>I started out as a GeoJSON " +
			feature.geometry.type + ", but now I'm a Leaflet vector!</p>";
	if (feature.properties && feature.properties.popupContent) {
		popupContent += feature.properties.popupContent;
	}
	layer.bindPopup(popupContent);


var coorsField = {
    "type": "Feature",
    "properties": {
        "popupContent": "Coors Field"
    },
    "geometry": {
        "type": "Point",
        "coordinates": [-104.99404191970824, 39.756213909328125]
    }
};

var coorsLayer = L.geoJson(coorsField, {
	pointToLayer: function (feature, latlng) {
		return L.marker(latlng, {icon: baseballIcon});
	},
	onEachFeature: onEachFeature
}).addTo(map);</code></pre>

<h4>filter</h4>

<p>The <code>filter</code> option can be used to control the visibility of GeoJSON features. To accomplish this we pass a function as the <code>filter</code> option. This function gets called for each feature in your GeoJSON layer, and gets passed the <code>feature</code> and the <code>layer</code>. You can then utilise the values in the feature's properties to control the visibility by returning <code>true</code> or <code>false</code>.</p>

<p>In the example below the free bus line "underConstruction" will not be shown on the map.</p>

<pre><code>var freeBus = {
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [-105.00341892242432, 39.75383843460583],
                    [-105.0008225440979, 39.751891803969535]
                ]
            },
            "properties": {
                "popupContent": "This is a free bus line that will take you across downtown.",
                "underConstruction": false
            },
            "id": 1
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [-105.0008225440979, 39.751891803969535],
                    [-104.99820470809937, 39.74979664004068]
                ]
            },
            "properties": {
                "popupContent": "This is a free bus line that will take you across downtown.",
                "underConstruction": true
            },
            "id": 2
        }
    ]
};

L.geoJson(freeBus, {
	filter: function (feature, layer) {
		if (feature.properties) {
			// If the property "underConstruction" exists and is true, return false (don't render features under construction)
			return feature.properties.underConstruction !== undefined ? !feature.properties.underConstruction : true;
		}
		return false;
	},
	onEachFeature: onEachFeature
}).addTo(map);</code></pre>

<p>View the <a href="geojson-example.html">example page</a> to see in detail what is possible with the GeoJSON layer.</p>
@flywire
Copy link
Author

flywire commented Sep 5, 2020

The Leaflet Quick Start Guide, the most basic tutorial, doesn't even build the example step by step with code the users can post straight into the code development environment (eg https://www.tutorialspoint.com/leafletjs/leafletjs_getting_started.htm).

In the Leaflet Quick Start Guide not all necessary code is used/explained in the example

Code used/explained in the demo example is highlighted below with | in the margin:

    <!DOCTYPE html>
    <html>
    <head>
        
        <title>Quick Start - Leaflet</title>
    
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        
        <link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
    
|       <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
|       <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
    
    
        
    </head>
    <body>
    
    
    
?   <div id="mapid" style="width: 600px; height: 400px;"></div>
    <script>
    
|       var mymap = L.map('mapid').setView([51.505, -0.09], 13);
    
|       L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
|           maxZoom: 18,
|           attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' +
|               '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
|               'Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
|           id: 'mapbox/streets-v11',
|           tileSize: 512,
|           zoomOffset: -1
|       }).addTo(mymap);
    
?       L.marker([51.5, -0.09]).addTo(mymap)
            .bindPopup("<b>Hello world!</b><br />I am a popup.").openPopup();
    
?       L.circle([51.508, -0.11], 500, {
|           color: 'red',
|           fillColor: '#f03',
|           fillOpacity: 0.5
?       }).addTo(mymap).bindPopup("I am a circle.");
    
?       L.polygon([
|           [51.509, -0.08],
|           [51.503, -0.06],
|           [51.51, -0.047]
?       ]).addTo(mymap).bindPopup("I am a polygon.");
    
    
|       var popup = L.popup();
|   
|       function onMapClick(e) {
|           popup
|               .setLatLng(e.latlng)
|               .setContent("You clicked the map at " + e.latlng.toString())
|               .openOn(mymap);
|       }
|   
|       mymap.on('click', onMapClick);
    
    </script>
    
    
    
    </body>
    </html>

It should explain the html, js and css files work together and start with:

Here's an example of a simple HTML document:

<!DOCTYPE html>
<html>
   <head>
      <title>My first HTML document</title>
   </head>
   <body>
      <p>Hello world!
   </body>
</html>

@Falke-Design Falke-Design added the docs Improvements or additions to documentation label Oct 28, 2021
@Malvoz Malvoz changed the title Examples Should Relate to Tutorials - Using GeoJSON with Leaflet Examples Should Relate to Tutorials Jan 27, 2022
@Malvoz Malvoz changed the title Examples Should Relate to Tutorials Improve step-by-step tutorials Apr 21, 2022
@Malvoz Malvoz added this to To do in Tutorials refresh via automation Apr 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to documentation
Projects
Development

No branches or pull requests

2 participants