## I. Before the workshop Download & install the back end, front end, and application components. The right arrow will take you through Mac OS X instructions. Use the down arrow once to access instructions for Ubuntu 14 instructions, twice to access instructions for Windows 8.1.
## Windows 8.1 Two options for working with Windows. You can run a Windows virtual machine (VM) via [VirtualBox](https://www.virtualbox.org/), or, if your machine already uses Windows as its operating system, you can install the components directly. Running a virtual machine adds one layer of complexity, but offers flexibility and one layer of safety. In the case of Windows, it may violate your license. Still, a slide or six on VirtualBox. windows | [next](#/9/2)

Xcode

Download and install Xcode from the Mac App Store. Xcode at the Apple App Store

VirtualBox

Download and install a VirtualBox platform package. VirtualBox download

prev | ubuntu | next

VirtualBox

Download and install a VirtualBox platform package. VirtualBox download

prev | windows | next

Homebrew

Installing this OS X package manager is a one-liner.

Homebrew home page

A Ubuntu disk image

Download Ubuntu. VirtualBox download

prev | ubuntu | next

A Windows disk image

Download Windows. You'll need your product key. VirtualBox download

prev | windows | next
## Create the VM, 1/3 1. start the VirtualBox Application 2. choose `Machine`, `New` 3. give the machine a `Name`, e.g., `Geostack` 4. set the `Type` to `Linux` 5. set the `Version` to `Ubuntu (64 bit)` 6. set `Memory size` 7. `Create a virtual hard drive now` as a `VDI`, `Dynamically allocated` [prev](#/10/1) | ubuntu | [next](#/12/1)
## Create the VM, 1/3 Microsoft provides a thorough [guide to Using VirtualBox](http://msdn.microsoft.com/en-us/library/windows/apps/jj945425.aspx). You won't need to install Visual Studio. 1. start the VirtualBox Application 2. choose `Machine`, `New` 3. give the machine a `Name`, e.g., `Geostack` 4. set the `Type` to `Microsoft Windows` 5. set the `Version` to `Windows 8.1 (64 bit)` 6. set the `Memory size` to at least `2 GB (2048 MB)`, `4 GB (4096 MB)` if you can 7. `Create a virtual hard drive now` as a `VDI`, `Dynamically allocated`, `40 GB` [prev](#/10/2) | windows | [next](#/12/2)

Yay Homebrew!

Image CC BY 2.0 Bernt Rostad, some rights reserved
## Create the VM, 2/3 8. select your new VM and click `Settings`, `Storage` 9. click `Empty` below `Controller: IDE` 10. use `CD/DVD Drive` to navigate to the file you downloaded from the Ubuntu site, e.g. `ubuntu-14.0401-desktop-amd64.iso` 11. click `Start` 12. choose `Install Ubuntu` 13. choose `Erase disk and install Ubuntu`; no fear, you're erasing a virtual disk 14. proceed with installation; if it seems to hang after `Stopping early crypto disks`, choose `Machine`, `Reset (Host+R)` [prev](#/11/1) | ubuntu | [next](#/13/1)
## Create the VM, 2/3 8. select your new VM and click `Settings`, `Storage` 9. select `Controller: IDE` 10. use `Add CD/DVD Device` to navigate to the .ISO file you downloaded from the Windows site 11. click `Start` 12. proceed with installation [prev](#/11/2) | windows | [next](#/13/2)
## Create the VM, 3/3 15. with the VM running, choose `Devices`, `Insert Guest Additions CD image` 16. navigate down the dock, select the CD/DVD icon, and `Run Software` 17. click the gear icon at top right, `Shut Down...`, and `Restart` 18. `Search your computer and online sources` for `Terminal`, drag `Terminal` to the dock 19. click on the `Terminal` icon to get started [prev](#/12/1) | ubuntu | [next](#/14/1)
## Create the VM, 3/3 13. with the VM running, choose `Devices`, `Insert Guest Additions CD image` 14. navigate to the CD Drive and run the `VirtualBox Guest Additions` [prev](#/12/2) | windows | [next](#/14/2)

Python

Download and install Python 2.7.X. Python download

prev | windows | next
## pip pip is the contemporary way to install Python packages. [Download it](https://bootstrap.pypa.io/get-pip.py), open a Windows PowerShell, execute it, then add the Scripts directory to your path. ``` Windows PowerShell Copyright (C) 2013 Microsoft Corporation. All rights reserved. PS C:\Users\erictheise> python Downloads\get-pip.py Downloading/unpacking pip Downloading/unpacking setuptools Installing collected packages: pip, setuptools Successfully installed pip setuptools Cleaning up... PS C:\Users\erictheise> setx PATH "%PATH%;C:\Python27\Scripts" ``` [Official instructions](https://pip.pypa.io/en/latest/installing.html) are available. [prev](#/14/2) | windows | [next](#/16/2)

TileMill

Download and install TileMill from MapBox. TileMill download

prev | ubuntu | next

NumPy

Download the installer. The filenames are very long, so make sure you get a python2.7 version.

NumPy download

prev | windows | next
## This slide intentionally left blank. [prev](#/16/1) | ubuntu | [next](#/21/1)

PostgreSQL

Download PostgreSQL 9.3.X. PostgreSQL download

prev | windows | next
## This slide intentionally left blank. [prev](#/16/1) | ubuntu | [next](#/21/1)

PostgreSQL & PostGIS

Run the installer. When it completes, use Stack Builder to install PostGIS 2.1

PostgreSQL download

prev | windows | next
## This slide intentionally left blank. [prev](#/16/1) | ubuntu | [next](#/21/1)

node.js

Download and install node.js.

node.js download

prev | windows | next
## This slide intentionally left blank. [prev](#/16/1) | ubuntu | [next](#/21/1)
## This slide intentionally left blank. [prev](#/18/2) | windows | [next](#/21/2)

TileMill

Download and install TileMill from MapBox. TileMill download

## This slide intentionally left blank. [prev](#/16/1) | ubuntu | [next](#/22)

TileMill

Download and install TileMill from MapBox. TileMill download

prev | windows | next

Okay, let's bake some tiles!

Image CC BY SA OpenStreetMap Wiki

OpenStreetMap schema

Four elements are central to OpenStreetMap's data model:

  • nodes: points. They may be actual points of interest or parts of ways.
  • ways: linear features (roads, rivers) and area boundaries ("closed ways": forests, buildings).
  • relations: multi-purpose data structures that document relationships between two or more elements.
  • tags: key/value pairs, ideally following community conventions.

osm2pgsql massages these elements to
create tables suitable for rendering.

osm2pgsql schema

  • planet_osm_point: contains all imported nodes with tags.
  • planet_osm_line: contains all imported ways
  • planet_osm_polygon: contains all imported polygons..
  • planet_osm_roads: contains a subset of planet_osm_line suitable for rendering at low zoom levels.

More detail is available.

Let's try and make this data visible.

TileMill

Launch TileMill and create a New Project.

TileMill - New Project

TileMill

Select the project, then pan and zoom to Portland. Add a PostGIS layer for lines. (Lines: immediate gratification.)

TileMill - Line Layer

Whoa. Interesting.

TileMill - Line Layer

Can I get a close-up of that?

Not very discriminating.

TileMill - Line Layer

Time to look at tags. SQL and tags. And MSS.

Tuning the SQL

Delete #planetosmline, add a PostGIS layer using:


( SELECT * FROM planet_osm_line
  WHERE highway IN ('motorway', 'primary', 'secondary', 'tertiary', 'service', 'residential')
) AS roads
            

TileMill - #roads.line Layer

Tuning the Map Style Sheet

TileMill - #roads.line Layer

You might say we're drinking from a firehose.

We need to be selective about what we suck down from our OSM database. Our basic dilemma is

  • which entities to show,
  • at what zoom level, and
  • how to style them

Roads.mss

Delete the #planetosmline & #roads blocks from style.mss. Click on the + to create a new Carto stylesheet, roads.mss. Into it, copy and paste this block:


@motorway:    #ff8c00;
@primary:     #ffd700;
@secondary:   #555555;
@tertiary:    #676767;
@service:     #888888;
@residential: #999999;

#roads.line {
  [highway = 'motorway'] {
    [zoom >=  9] { line-width:2; line-color:@motorway; }
    [zoom >= 10] { line-width:3; line-color:@motorway; }
    [zoom >= 11] { line-width:3.5; line-color:@motorway; }
    [zoom >= 12] { line-width:4; line-color:@motorway; }
    [zoom >= 13] { line-width:4.5; line-color:@motorway; }
    [zoom >= 14] { line-width:5; line-color:@motorway; }
    [zoom >= 15] { line-width:6; line-color:@motorway; }
    [zoom >= 16] { line-width:8; line-color:@motorway; }
    [zoom >= 17] { line-width:10; line-color:@motorway; }
    [zoom >= 18] { line-width:12; line-color:@motorway; }
  }

  [highway = 'primary'] {
    [zoom >=  9] { line-width:2; line-color:@primary; }
    [zoom >= 10] { line-width:3; line-color:@primary; }
    [zoom >= 11] { line-width:3.5; line-color:@primary; }
    [zoom >= 12] { line-width:4; line-color:@primary; }
    [zoom >= 13] { line-width:4.5; line-color:@primary; }
    [zoom >= 14] { line-width:5; line-color:@primary; }
    [zoom >= 15] { line-width:6; line-color:@primary; }
    [zoom >= 16] { line-width:8; line-color:@primary; }
    [zoom >= 17] { line-width:10; line-color:@primary; }
    [zoom >= 18] { line-width:12; line-color:@primary; }
  }

  [highway = 'secondary'] {
    [zoom >=  9] { line-width:0.75; line-color:@secondary; }
    [zoom >= 10] { line-width:2; line-color:@secondary; }
    [zoom >= 11] { line-width:2.5; line-color:@secondary; }
    [zoom >= 12] { line-width:3; line-color:@secondary; }
    [zoom >= 13] { line-width:3.5; line-color:@secondary; }
    [zoom >= 14] { line-width:4; line-color:@secondary; }
    [zoom >= 15] { line-width:5; line-color:@secondary; }
    [zoom >= 16] { line-width:6; line-color:@secondary; }
    [zoom >= 17] { line-width:7; line-color:@secondary; }
    [zoom >= 18] { line-width:9; line-color:@secondary; }
  }

  [highway = 'tertiary'] {
    [zoom >=  9] { line-width:0.25; line-color:@tertiary; }
    [zoom >= 10] { line-width:1; line-color:@tertiary; }
    [zoom >= 11] { line-width:1.5; line-color:@tertiary; }
    [zoom >= 12] { line-width:2; line-color:@tertiary; }
    [zoom >= 13] { line-width:2.5; line-color:@tertiary; }
    [zoom >= 14] { line-width:3; line-color:@tertiary; }
    [zoom >= 15] { line-width:4; line-color:@tertiary; }
    [zoom >= 16] { line-width:5; line-color:@tertiary; }
    [zoom >= 17] { line-width:6; line-color:@tertiary; }
    [zoom >= 18] { line-width:8; line-color:@tertiary; }
  }

  [highway = 'service'] {
    [zoom >= 10] { line-width:.5; line-color:@service; }
    [zoom >= 11] { line-width:.3; line-color:@service; }
    [zoom >= 12] { line-width:.4; line-color:@service; }
    [zoom >= 13] { line-width:.6; line-color:@service; }
    [zoom >= 14] { line-width:.8; line-color:@service; }
    [zoom >= 15] { line-width:1.2; line-color:@service; }
    [zoom >= 16] { line-width:2; line-color:@service; }
    [zoom >= 17] { line-width:4; line-color:@service; }
    [zoom >= 18] { line-width:6; line-color:@service; }
  }

  [highway = 'residential'] {
    [zoom >= 10] { line-width:.2; line-color:@residential; }
    [zoom >= 11] { line-width:.3; line-color:@residential; }
    [zoom >= 12] { line-width:.4; line-color:@residential; }
    [zoom >= 13] { line-width:.6; line-color:@residential; }
    [zoom >= 14] { line-width:.8; line-color:@residential; }
    [zoom >= 15] { line-width:1.2; line-color:@residential; }
    [zoom >= 16] { line-width:2; line-color:@residential; }
    [zoom >= 17] { line-width:4; line-color:@residential; }
    [zoom >= 18] { line-width:6; line-color:@residential; }
  }

}
          

MSS. Tags. Zoom.

TileMill - #roads.line Layer

Let's lay in the Willamette.

Water-area.

Add a PostGIS layer using:


( SELECT way, "natural", waterway, landuse, name
  FROM planet_osm_polygon
  WHERE waterway IN ('dock', 'riverbank', 'canal') OR
        landuse IN ('reservoir', 'basin') OR
        "natural" IN ('lake', 'water', 'land', 'glacier', 'mud')
) AS waterway
          
TileMill - #water-area Layer

Water.mss

Click on the + to create a new Carto stylesheet, water.mss. Into it, copy and paste this block:


@water-color: #16b;

#water-areas {
  [waterway = 'dock'],
  [waterway = 'canal'] {
    [zoom >= 9]::waterway {
      polygon-fill: @water-color;
    }
  }

  [landuse = 'basin'][zoom >= 7]::landuse {
    polygon-fill: @water-color;
  }

  [natural = 'lake']::natural,
  [natural = 'water']::natural,
  [landuse = 'reservoir']::landuse,
  [waterway = 'riverbank']::waterway {
    [zoom >= 6] {
      polygon-fill: @water-color;
    }
  }

}
        

Forty feet deep and rising.

Do drag the #water-areas layer below #roads.line.

TileMill - #water-area Layer

Let's pick up streams and creeks, too.

Water-lines.

Add a PostGIS layer using:


(SELECT way, waterway, lock, name, case WHEN tunnel IN ('yes','culvert') THEN 'yes' ELSE 'no' END AS int_tunnel, 'no' AS bridge
FROM planet_osm_line
WHERE waterway IN ('weir', 'river', 'canal', 'derelict_canal', 'stream', 'drain', 'ditch', 'wadi')
AND (bridge IS NULL OR bridge NOT IN ('yes','aqueduct'))
) AS water_lines
          
TileMill - #water.line Layer

Water.mss

Append this block into water.mss:


.water-lines {
  [waterway = 'weir'][zoom >= 15] {
    line-color: #aaa;
    line-width: 2;
    line-join: round;
    line-cap: round;
  }

  [waterway = 'canal'][zoom >= 12],
  [waterway = 'river'][zoom >= 12] {
    [bridge = 'yes'] {
      [zoom >= 14] {
        bridgecasing/line-color: black;
        bridgecasing/line-join: round;
        bridgecasing/line-width: 6;
        [zoom >= 15] { bridgecasing/line-width: 7; }
        [zoom >= 17] { bridgecasing/line-width: 11; }
        [zoom >= 18] { bridgecasing/line-width: 13; }
      }
    }

    line-color: @water-color;
    line-width: 2;
    [zoom >= 13] { line-width: 3; }
    [zoom >= 14] { line-width: 5; }
    [zoom >= 15] { line-width: 6; }
    [zoom >= 17] { line-width: 10; }
    [zoom >= 18] { line-width: 12; }
    line-cap: round;
    line-join: round;
    [int_tunnel = 'yes'] {
      line-dasharray: 4,2;
      line-cap: butt;
      line-join: miter;
      a/line-color: #f3f7f7;
      a/line-width: 1;
      [zoom >= 14] { a/line-width: 2; }
      [zoom >= 15] { a/line-width: 3; }
      [zoom >= 17] { a/line-width: 7; }
      [zoom >= 18] { a/line-width: 8; }
    }
  }

  [waterway = 'stream'],
  [waterway = 'ditch'],
  [waterway = 'drain'] {
    [zoom >= 13] {
      [bridge = 'yes'] {
        [zoom >= 14] {
        bridgecasing/line-color: black;
        bridgecasing/line-join: round;
        bridgecasing/line-width: 3;
        [waterway = 'stream'][zoom >= 15] { bridgecasing/line-width: 4; }
        bridgeglow/line-color: white;
        bridgeglow/line-join: round;
        bridgeglow/line-width: 2;
        [waterway = 'stream'][zoom >= 15] { bridgeglow/line-width: 3; }
        }
      }
      line-width: 1;
      line-color: @water-color;
      [waterway = 'stream'][zoom >= 15] {
        line-width: 2;
      }
      [int_tunnel = 'yes'][zoom >= 15] {
        line-width: 2.5;
        [waterway = 'stream'] { line-width: 3.5; }
        line-dasharray: 4,2;
        a/line-width: 1;
        [waterway = 'stream'] { a/line-width: 2; }
        a/line-color: #f3f7f7;
      }
    }
  }

}
          

Zoom in to see some creeks.

Drag the #water-lines layer below #water-areas.

TileMill - #water-lines Layer

Labels

Each of the planet_osm_ tables has a name field, and we need to tap into those to begin rendering labels.


( SELECT way, CASE WHEN SUBSTR(highway, length(highway)-3, 4) = 'link' THEN substr(highway,0,length(highway)-4) ELSE highway END, name
FROM planet_osm_line
WHERE highway IN ('motorway', 'motorway_link', 'trunk', 'trunk_link', 'primary', 'primary_link', 'secondary', 'secondary_link',
'tertiary', 'tertiary_link', 'residential', 'unclassified', 'road', 'service', 'pedestrian', 'raceway', 'living_street', 'construction', 'proposed')
AND name IS NOT NULL
) AS roads_text_name
          
TileMill - #roads-text-name Layer

Style.mss

Prepend this block into style.mss:


@book-fonts: "DejaVu Sans Book", "Arundina Sans Regular", "Padauk Regular", "Khmer OS Metal Chrieng Regular",
             "Mukti Narrow Regular", "gargi Medium", "TSCu_Paranar Regular", "Tibetan Machine Uni Regular", "Mallige Normal",
             "Droid Sans Fallback Regular", "Unifont Medium", "unifont Medium";
          

Road.mss

Append this block into road.mss:


#roads-text-name {
  [highway = 'motorway'],
  [highway = 'trunk'],
  [highway = 'primary'] {
    [zoom >= 13] {
      text-name: "[name]";
      text-size: 8;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-face-name: @book-fonts;
      text-halo-radius: 0;
    }
    [zoom >= 14] {
      text-size: 9;
    }
    [zoom >= 15] {
      text-size: 10;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }

  [highway = 'secondary'] {
    [zoom >= 13] {
    text-name: "[name]";
    text-size: 8;
    text-fill: black;
    text-spacing: 300;
    text-clip: false;
    text-placement: line;
    text-face-name: @book-fonts;
    text-halo-radius: 1;
    }
    [zoom >= 14] {
      text-size: 9;
    }
    [zoom >= 15] {
      text-size: 10;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }

  [highway = 'tertiary'],
  [highway = 'tertiary_link'] {
    [zoom >= 14] {
      text-name: "[name]";
      text-size: 9;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-face-name: @book-fonts;
      text-halo-radius: 1;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }

  [highway = 'proposed'],
  [highway = 'construction'] {
    [zoom >= 13] {
      text-name: "[name]";
      text-size: 9;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-halo-radius: 1;
      text-face-name: @book-fonts;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }

  [highway = 'residential'],
  [highway = 'unclassified'],
  [highway = 'road'] {
    [zoom >= 15] {
      text-name: "[name]";
      text-size: 8;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-halo-radius: 1;
      text-face-name: @book-fonts;
    }
    [zoom >= 16] {
      text-size: 9;
    }
    [zoom >= 17] {
      text-size: 11;
      text-spacing: 400;
    }
  }

  [highway = 'raceway'],
  [highway = 'service'] {
    [zoom >= 16] {
      text-name: "[name]";
      text-size: 9;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-halo-radius: 1;
    text-face-name: @book-fonts;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }

  [highway = 'living_street'],
  [highway = 'pedestrian'] {
    [zoom >= 15] {
      text-name: "[name]";
      text-size: 8;
      text-fill: black;
      text-spacing: 300;
      text-clip: false;
      text-placement: line;
      text-halo-radius: 1;
    text-face-name: @book-fonts;
    }
    [zoom >= 16] {
      text-size: 9;
    }
    [zoom >= 17] {
      text-size: 11;
    }
  }
}
        

static.html

Let's look at the contents of static.html.


<!DOCTYPE html>
<html>
<head>
  Static Data with Leaflet
  
  
  
  
</head>
<body>
<script> var tileUrl, map = L.map('map').setView([45.521969, -122.683424], 13); // @todo: Pick a tileserver by uncommenting one of these values for tileUrl: // // To use the MapBox example tiles: // tileUrl = 'https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png'; // // To use the Python SimpleHTTPServer you've set up on port 8887: // tileUrl = 'http://localhost:8887/tiles/{z}/{x}/{y}.png'; // // To use the TileStream server you've set up on port 8888: tileUrl = 'http://localhost:8888/v2/portland_from_osm/{z}/{x}/{y}.png'; // L.tileLayer(tileUrl, { minZoom: 10, maxZoom: 16, attribution: 'Map data © OpenStreetMap contributors, ' + 'Imagery © Mapbox', id: 'examples.map-i86knfo3' }).addTo(map); L.marker([45.521969, -122.683424]).addTo(map) .bindPopup( 'Ración
' + '1205 SW Washington St
Portland, OR' ); var popup = L.popup(); </script> </body> </html>

Open it in your browser.

dynamic.html

Let's look at the contents of dynamic.html.


<!DOCTYPE html>
<html>
<head>
  Dynamic Data with Leaflet
  
  
  
  
  
</head>
<body>
<script> function initMap() { var tileUrl; // Note: the [Wikipedia page on Downtown Portland Oregon](http://en.wikipedia.org/wiki/Downtown_Portland) // gives its location as 45.51935°N 122.67962°W. We'll center the map at that location with zoom level 12. // var map = L.map('map').setView([45.51935, -122.67962], 12); // @todo: Pick a tileserver by uncommenting one of these values for tileUrl: // // To use the MapBox example tiles: // tileUrl = 'https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png'; // // To use the Python SimpleHTTPServer you've set up on port 8887: // tileUrl = 'http://localhost:8887/tiles/{z}/{x}/{y}.png'; // // To use the TileStream server you've set up on port 8888: tileUrl = 'http://localhost:8888/v2/portland_from_osm/{z}/{x}/{y}.png'; // L.tileLayer(tileUrl, { minZoom: 10, maxZoom: 16, attribution: 'Map data © OpenStreetMap contributors, ' + 'Imagery © Mapbox', id: 'examples.map-i86knfo3' }).addTo(map); $.ajax({ url: 'http://localhost:3000/amenities', dataType: 'json', type: 'get' }).done(function (data) { var geojson = L.geoJson(data, { pointToLayer: function (feature, latlng) { var fillColor, fillOpacity = 0.75; // Note: as this is a quick & dirty demo, fill colors have been assigned arbitrarily // to the top 12 cuisines found tagged to restaurants in Sep 2014 OpenStreetMap // data for Portland, OR. Any other cuisine, or restaurants that have not been tagged // with a cuisine, get a dark gray fill color having low opacity. The color values // come from the 12 data class/qualitative nature/Set3 at http://colorbrewer2.org/ // // SELECT tags->'cuisine' AS cuisine, count(tags->'cuisine') // FROM planet_osm_point // WHERE amenity = 'restaurant' // GROUP BY tags->'cuisine' // ORDER BY count desc // LIMIT 12; // // cuisine | count //----------+------- // pizza | 47 // mexican | 42 // american | 32 // chinese | 23 // thai | 20 // japanese | 18 // italian | 17 // burger | 17 // sushi | 16 // asian | 11 // regional | 9 // sandwich | 8 // (12 rows) // switch (feature.properties.cuisine) { case 'pizza': fillColor = '#8dd3c7'; break; case 'mexican': fillColor = '#ffffb3'; break; case 'american': fillColor = '#bebada'; break; case 'chinese': fillColor = '#fb8072'; break; case 'thai': fillColor = '#80b1d3'; break; case 'japanese': fillColor = '#fdb462'; break; case 'italian': fillColor = '#b3de69'; break; case 'burger': fillColor = '#fccde5'; break; case 'sushi': fillColor = '#d9d9d9'; break; case 'asian': fillColor = '#bc80bd'; break; case 'regional': fillColor = '#ccebc5'; break; case 'sandwich': fillColor = '#ffed6f'; break; default: fillColor = '#222'; fillOpacity = 0.1; break; } var marker = L.circleMarker(latlng, { radius: 6, weight: 1, color: "#000", opacity: 1, fillColor: fillColor, fillOpacity: fillOpacity }); map.on('load zoomend', function () { var currentZoom = map.getZoom(); if (currentZoom < 13) { marker.setRadius(6); } else { if (currentZoom < 15) { marker.setRadius(9); } else { marker.setRadius(12); } } }); return marker; }, onEachFeature: function (feature, layer) { layer.bindPopup( '
' + ''+feature.properties.name+'' + '
' ); } }).addTo(map); }); } initMap(); </script> </body> </html>

Note the use of L.geoJson(), the switch statement to determine how to color each circleMarker, and the use of map.getZoom() to set the radius of each circleMarker.