Add a WMS tutorial example.
Don't mock webGL in the tests; it needs to be done in the inner iframe.
Allow passing through a crossDomain setting of null.
Simpler tutorial titles.
Order tutorials.
MODIFY
src/imageTile.js
src/imageTile.js
@@ -43,8 +43,7 @@ module.exports = (function () {
spec.size = spec.size || {x: 256, y: 256};
this._image = null;
- // Cache the coordinate scaling
- this._cors = spec.crossDomain || 'anonymous';
+ this._cors = (spec.crossDomain || spec.crossDomain === null) ? spec.crossDomain : 'anonymous';
// Call superclass constructor
tile.call(this, spec);
@@ -62,13 +61,15 @@ module.exports = (function () {
/**
* Initiate the image request.
+ *
+ * @returns {this} The current tile class instance.
*/
this.fetch = function () {
var defer;
if (!this._image) {
this._image = new Image(this.size.x, this.size.y);
// Only set the crossOrigin parameter if this is going across origins.
- if (this._url.indexOf(':') >= 0 &&
+ if (this._cors && this._url.indexOf(':') >= 0 &&
this._url.indexOf('/') === this._url.indexOf(':') + 1) {
this._image.crossOrigin = this._cors;
}

MODIFY
src/osmLayer.js
src/osmLayer.js
@@ -8,7 +8,7 @@ module.exports = (function () {
var quadFeature = require('./quadFeature');
/**
- * Create a new instance of osmLayer
+ * Create a new instance of osmLayer.
*
* @class geo.osmLayer
* @extends geo.featureLayer
@@ -56,7 +56,8 @@ module.exports = (function () {
scale: this._options.tileScale,
url: this._options.url.call(
this, urlParams.x, urlParams.y, urlParams.level || 0,
- this._options.subdomains)
+ this._options.subdomains),
+ crossDomain: this._options.crossDomain
});
}.bind(this);
};

MODIFY
tests/tutorials.js
tests/tutorials.js
@@ -5,28 +5,20 @@ describe('tutorials', function () {
'use strict';
var $ = require('jquery');
- var geo = require('./test-utils').geo;
- var mockVGLRenderer = geo.util.mockVGLRenderer;
- var restoreVGLRenderer = geo.util.restoreVGLRenderer;
var imageTest = require('./image-test');
- beforeAll(function () {
- mockVGLRenderer();
+ beforeEach(function () {
imageTest.prepareIframeTest();
});
- afterEach(function () {
- restoreVGLRenderer();
- });
-
/* Test each tutorial */
tutorials.keys().forEach(function (tutorialPath) {
var tutorialName = tutorialPath.split('/')[1];
describe('Test ' + tutorialName, function () {
/* Load the tutorial in the test iframe */
beforeEach(function (done) {
- $('#map').on('load', done);
+ $('#map').one('load', done);
$('#map').attr('src', '/tutorials/' + tutorialName + '/index.html');
});
it('Run tutorial tests', function (done) {

MODIFY
tutorials/basic/tutorial.json
tutorials/basic/tutorial.json
{
- "title": "Tutorial - Simple Map",
+ "title": "Simple Map",
"hideNavbar": true,
+ "level": 0,
+ "order": 0,
"tutorialCss": [],
"tutorialJs": [],
"about": {

MODIFY
tutorials/build.js
tutorials/build.js
@@ -34,6 +34,23 @@ var tutorials = glob('tutorials/*/tutorial.json')
return json;
});
+/* Sort tutorials. Tutorials are sorted by level, order, title, and path.
+ * undefined or null levels are orders are sorted after defined values. level
+ * should be used for the approximate difficulty of the tutorial, and order for
+ * making specific tutorials appear sooner in the list. */
+tutorials.sort(function (a, b) {
+ if (a.level !== b.level) {
+ return a.level === undefined ? 1 : b.level === undefined ? -1 : a.level - b.level;
+ }
+ if (a.order !== b.order) {
+ return a.order === undefined ? 1 : b.order === undefined ? -1 : a.order - b.order;
+ }
+ if (a.title !== b.title) {
+ return a.title < b.title ? -1 : 1;
+ }
+ return a.path < b.path ? -1 : 1;
+});
+
// copy common files
fs.copySync('tutorials/common', 'dist/tutorials/common');

MODIFY
tutorials/common/index.pug
tutorials/common/index.pug
@@ -59,7 +59,7 @@ html(lang="en")
head
meta(charset="UTF-8")
link(rel="shortcut icon", href="data:image/x-icon;,", type="image/x-icon")
- title= title
+ title GeoJS Tutotial - #{title}
// include the main bundle
script(type='text/javascript', src=bundle, charset='UTF-8')
@@ -78,7 +78,7 @@ html(lang="en")
script(type="text/javascript").
$(start_tutorial);
- body
+ body(class=hideNavbar ? undefined : 'navbar')
if !hideNavbar
// Add a navbar
nav.navbar.navbar-default(role="navigation")

MODIFY
tutorials/common/tutorials.css
tutorials/common/tutorials.css
@@ -13,6 +13,9 @@ html, body {
height: 100%;
overflow: hidden;
}
+.navbar #maincontent {
+ height: calc(100% - 60px);
+}
#tutorial, #workframe {
width: 50%;
height: 100%;
@@ -49,7 +52,6 @@ html, body {
}
.codeblock .CodeMirror {
height: inherit;
- max-height: 16em;
border: 1px solid rgba(0, 0, 0, 0.5);
}
.codeblock_controls {

MODIFY
tutorials/common/tutorials.js
tutorials/common/tutorials.js
@@ -10,6 +10,9 @@ var processBlockInfo = {
defaultHTML: '<!DOCTYPE html>\n' +
'<html>\n' +
'<head>\n' +
+ /* We could also load from the CDN:
+ * https://cdnjs.cloudflare.com/ajax/libs/geojs/0.12.2/geo.min.js
+ * or the non-minified versions to make debug easier. */
' <script type="text/javascript" src="../../built/geo.min.js"></script>\n' +
' <script type="text/javascript" src="../../built/geo.ext.min.js"></script>\n' +
'</head>\n' +
@@ -76,7 +79,7 @@ function process_block(selector) {
target = elem.attr('target'),
targetelem = $('#' + target),
code = {},
- html, css, js, pos, jsurl;
+ html, css, js, pos, jsurl, webgl;
$('.codeblock[target="' + target + '"]').each(function () {
var block = $(this),
@@ -89,6 +92,7 @@ function process_block(selector) {
if (!block.hasClass('active')) {
return;
}
+ webgl = webgl || block.attr('webgl');
while (code[format].length <= step) {
code[format].push('');
}
@@ -104,8 +108,6 @@ function process_block(selector) {
});
code[key] = processed;
});
- /* We could also load from the CDN:
- * https://cdnjs.cloudflare.com/ajax/libs/geojs/0.12.2/geo.min.js */
html = code.html !== undefined ? code.html : processBlockInfo.defaultHTML;
css = code.css !== undefined ? code.css : processBlockInfo.defaultCSS;
js = code.javascript || '';
@@ -121,6 +123,12 @@ function process_block(selector) {
pos = html.length;
}
if (js) {
+ /* If any code block is marked as needing webgl and the current window's
+ * parent has a geojs element that reports that it doesn't support webgl,
+ * mock webgl. This is expected to only happen in the automated tests. */
+ if (webgl && window.parent && window.parent !== window && window.parent.geo && !window.parent.geo.gl.vglRenderer.supported()) {
+ js = 'geo.util.mockVGLRenderer();\n' + js;
+ }
html = html.substr(0, pos).replace(/\s+$/, '') + '\n<script type="text/javascript">\n' + js + '</script>\n' + html.substr(pos);
}
if (processBlockInfo.lastsrc !== html) {

MODIFY
tutorials/tile_source/tutorial.json
tutorials/tile_source/tutorial.json
{
- "title": "Tutorial - Changing Tile Source",
+ "title": "Changing Tile Source",
"hideNavbar": true,
+ "level": 0,
"tutorialCss": [],
"tutorialJs": [],
"about": {

ADD
tutorials/wms/index.pug
+extends ../common/index.pug
+
+block mainTutorial
+ :markdown-it
+ # Tutorial - Using a WMS Tile Source
+ When a tile layer is added to a map, the tiles can be loaded based on a templated URL or based on a URL returned from a function. When using a WMS tile server, often the request must be made in a specific coordinate system. The bounds of each tile can be requested, converted to the WMS coordinate system, and then sent as part of the URL.
+
+ +codeblock('javascript', 1, undefined, true).
+ // The default map projection is Web Mercator. We need to ask the WMS
+ // server to use this coordinate system.
+ var projection = 'EPSG:3857';
+
+ var map = geo.map({
+ node: "#map",
+ zoom: 4,
+ // start centered on the continental US
+ center: {x: -98.0, y: 39.5}
+ });
+ // place a base tile layer on the map
+ map.createLayer('osm');
+ // create a tile layer with the WMS data
+ map.createLayer('osm', {
+ attribution: null,
+ // the tiles are transparent, so don't keep the lower level ones; they
+ // would build up
+ keepLower: false,
+ // the map tile URL can be generated from a function
+ url: function (x, y, zoom) {
+ var service = 'NWS_Observations';
+ var observation = 'radar_base_reflectivity';
+ // get data from NOAA
+ var baseUrl = 'https://idpgis.ncep.noaa.gov/arcgis/rest/services/' + service + '/' + observation + '/MapServer/export';
+
+ // Get the bounding box of the tile in the desired projection
+ var bb = this.gcsTileBounds({x: x, y: y, level: zoom}, projection);
+ // make a string which the WMS server requires for the bounding box
+ var bbSpec = bb.left + ',' + bb.bottom + ',' + bb.right + ',' + bb.top;
+
+ // Set the WMS server parameters
+ var params = {
+ transparent: true,
+ format: 'png32',
+ bbox: bbSpec,
+ bboxSR: projection.split(':')[1],
+ imageSR: projection.split(':')[1],
+ size: '256,256',
+ layers: 'show:3',
+ f: 'image'
+ }
+ // construct the url for a tile. We can use jQuery's $.param function.
+ var url = baseUrl + '?' + $.param(params);
+ return url;
+ }
+ });
+ +codeblock_test('map has two osm layers, one from a WMS server', [
+ 'map.layers().length === 2',
+ 'map.layers()[0] instanceof geo.osmLayer',
+ 'map.layers()[1] instanceof geo.osmLayer',
+ 'geo.util.isFunction(map.layers()[1].url())'
+ ])

ADD
tutorials/wms/tutorial.json
+{
+ "title": "Using a WMS Tile Source",
+ "hideNavbar": true,
+ "tutorialCss": [],
+ "tutorialJs": [],
+ "level": 0,
+ "about": {
+ "text": "Make a map with a custom function for getting tiles from a WMS server. This also shows how to get tile bounds in different coordinates."
+ }
+}

Add a WMS tutorial example.

Don't mock webGL in the tests; it needs to be done in the inner iframe.

Allow passing through a crossDomain setting of null.

Simpler tutorial titles.

Order tutorials.