diff --git a/examples/index.js b/examples/index.js index 1780b29d32..74a593c646 100644 --- a/examples/index.js +++ b/examples/index.js @@ -11,6 +11,9 @@ require('codemirror/lib/codemirror.css'); require('codemirror/addon/lint/lint.css'); require('codemirror/addon/fold/foldgutter.css'); +// Colorbrewer +require('colorbrewer'); + require('./common/js/jsonlint'); require('codemirror'); require('codemirror/mode/javascript/javascript'); diff --git a/examples/sld/example.json b/examples/sld/example.json new file mode 100644 index 0000000000..304fa09860 --- /dev/null +++ b/examples/sld/example.json @@ -0,0 +1,9 @@ +{ + "path": "sld", + "title": "Adding styles to WMS raster layers", + "exampleCss": ["main.css"], + "exampleJs": ["main.js"], + "about": { + "text": "Rendering tiles from a WMS server by customizing the style." + } +} diff --git a/examples/sld/index.jade b/examples/sld/index.jade new file mode 100644 index 0000000000..aea4dd5e77 --- /dev/null +++ b/examples/sld/index.jade @@ -0,0 +1,26 @@ +extends ../common/templates/index.jade + +block append mainContent + div#controls + + .form-group(title="Select a color palette.") + label(for="palette") Color Palette + select#palette.mapparam(param-name="palette", placeholder="YlGn") + + .form-group(title="Select number of colors.") + label(for="color-count") Number of colors + select#color-count.mapparam(param-name="color-count") + + .form-group(title="Discrete or continuous colors.") + label(for="palette-type") Palette Type + select#palette-type.mapparam(param-name="palette-type", placeholder="continuous") + option(value="continuous") Continuous + option(value="discrete") Discrete + + .form-group(title="Minimum value for your data") + label(for="min") Minimum + input#min.mapparam(param-name="x", placeholder="0") + + .form-group(title="Maximum value for your data.") + label(for="max") Maximum + input#max.mapparam(param-name="x", placeholder="300") diff --git a/examples/sld/main.css b/examples/sld/main.css new file mode 100644 index 0000000000..1ffbf025ed --- /dev/null +++ b/examples/sld/main.css @@ -0,0 +1,29 @@ +#controls { + overflow-x: hidden; + overflow-y: auto; + position: absolute; + left: 10px; + top: 80px; + z-index: 10; + border-radius: 5px; + border: 1px solid grey; + box-shadow: 1px 1px 3px black; + opacity: 0.5; + transition: opacity 250ms ease; + background: #CCC; + color: black; + padding: 4px; + font-size: 14px; +} +#controls:hover { + opacity: 1; +} +#controls.no-controls { + display: none; +} +#controls .form-group { + margin-bottom: 0; +} +#controls label { + min-width: 90px; +} diff --git a/examples/sld/main.js b/examples/sld/main.js new file mode 100644 index 0000000000..40ac94d0f8 --- /dev/null +++ b/examples/sld/main.js @@ -0,0 +1,261 @@ +var colorbrewer = colorbrewer; + +var layer = { + // Default values + palette: 'YlGn', + selectedNum: '3', + type: 'continuous', + min: '0', + max: '300', + name: 'usgs:ned', + sld: '', + projection: 'EPSG:3785' +}; + +var layerViewer = { + renderPalettes: function () { + var paletteArray = Object.keys(colorbrewer); + utility.populateDropdown('#palette', paletteArray); + }, + renderWidget: function (layer) { + + // Populates the number of colors dropdown + $('#color-count') + .empty(); + var numberArray = Object.keys(colorbrewer[layer.palette]); + utility.populateDropdown('#color-count', numberArray); + + // Sets the count + $('#color-count') + .val(layer.selectedNum); + + // Sets the type + $('#palette-type') + .val(layer.type); + + // Sets the min and max values + $('#min') + .val(layer.min); + $('#max') + .val(layer.max); + } +}; + +var layerController = { + syncLayer: function (layer) { + // Sync the model with UI + layer.palette = $('#palette') + .val(); + + var items = Object.keys(colorbrewer[layer.palette]); + var maxNumber = parseInt(items[items.length - 1]); + + if (parseInt($('#color-count').val()) <= maxNumber) { + layer.selectedNum = $('#color-count').val(); + } else { + layer.selectedNum = String(maxNumber); + } + + layer.type = $('#palette-type') + .val(); + layer.min = $('#min') + .val(); + layer.max = $('#max') + .val(); + layer.sld = this.generateSld(layer); + }, + generateSld: function (layer) { + // Orchestrates the sld generation + var sequence = utility.generateSequence(layer.min, layer.max, layer.selectedNum); + var palette_array = utility.getPalette(layer.palette, layer.selectedNum); + var xml = utility.generateXml(layer.name, layer.selectedNum, sequence, + palette_array, layer.type); + return xml; + } +}; + +// Run after the DOM loads +$(function () { + 'use strict'; + + // Create a map object + var map = geo.map({ + node: '#map', + zoom: 8, + center: { + x: -76.0, + y: 39 + } + }); + // Add an OSM layer + map.createLayer('osm'); + + // Populate the palette dropdown + layerViewer.renderPalettes(); + + // Render the widget + layerViewer.renderWidget(layer); + + // Generate sld + layer.sld = layerController.generateSld(layer); + + // Add the wms layer + var wms = utility.createWMSLayer(map, layer.sld, layer.projection, layer.name); + + // If any of the input boxes changes regenerate sld again + $('#palette, #color-count, #min, #max, #palette-type') + .change(function () { + layerController.syncLayer(layer); + layerViewer.renderWidget(layer); + + map.deleteLayer(wms); + + wms = utility.createWMSLayer(map, layer.sld, layer.projection, + layer.name); + }); + +}); + +var utility = { + // Some utility functions + + populateDropdown: function (dropdown, array) { + // Populates the dropdown based on the array given + $.each(array, function () { + var option = document.createElement('option'); + $(dropdown) + .append($(option) + .attr('value', this) + .html(this)); + }); + }, + generateSequence: function (start, stop, count) { + // Generates a sequence of numbers with the given start, + // stop and count variables + var sequence = []; + var step = (stop - start) / (count - 1.0); + for (var i = 0; i < count; i++) { + sequence.push(parseFloat(start + i * step)); + } + return sequence; + }, + getPalette: function (name, count) { + // Gets the palette array with the given name and count parameters + return colorbrewer[name][count]; + }, + createMapEntry: function (xml, color, value, opacity) { + // Adds a color-quantity-opacity entry to the sld + $(xml) + .find('ColorMap') + .append($('', xml) + .attr({ + color: color, + quantity: value, + opacity: opacity + })); + }, + generateXml: function (name, count, values, palette, type) { + // Generates the xml (sld) file with the given parameters + var xml = $($.parseXML( + '' + )); + $('StyledLayerDescriptor', xml) + .attr({ + 'version': '1.0.0', + 'xsi:schemaLocation': 'http://www.opengis.net/sld StyledLayerDescriptor.xsd', + 'xmlns': 'http://www.opengis.net/sld', + 'xmlns:ogc': 'http://www.opengis.net/ogc', + 'xmlns:xlink': 'http://www.w3.org/1999/xlink', + 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance' + }); + $('StyledLayerDescriptor', xml) + .append($('', xml)); + $(xml) + .find('NamedLayer') + .append($('', xml)) + .append($( + '', xml)); + $(xml) + .find('Name') + .text(name); + $(xml) + .find('UserStyle') + .append($('', xml)) + .append($( + '<IsDefault>', xml)) + .append($('<FeatureTypeStyle>', xml)); + $(xml) + .find('Title') + .text('Custom Visualization'); + $(xml) + .find('IsDefault') + .text(1); + $(xml) + .find('FeatureTypeStyle') + .append($('<Rule>', xml)); + $(xml) + .find('Rule') + .append($('<RasterSymbolizer>', xml)); + $(xml) + .find('RasterSymbolizer') + .append($('<ColorMap>', xml)); + + if (type === 'discrete') { + $(xml) + .find('ColorMap') + .attr({ + 'type': 'intervals' + }); + } + + for (var i = 0; i < count; i++) { + this.createMapEntry(xml, palette[i], values[i], 1); + } + var xmlString = (new XMLSerializer()) + .serializeToString(xml.context); + + return xmlString; + }, + createWMSLayer: function (map, sld, projection, layer_name) { + + // Add an OSM layer with a WMS server as the source of its titles + var wms = map.createLayer('osm', { + keepLower: false, + attribution: null + }); + + wms.url(function (x, y, zoom) { + // Compute the bounding box + var bb = wms.gcsTileBounds({ + x: x, + y: y, + level: zoom + }, projection); + var bbox_mercator = bb.left + ',' + bb.bottom + ',' + + bb.right + ',' + bb.top; + // Set the WMS server parameters + var params = { + 'SERVICE': 'WMS', + 'VERSION': '1.3.0', + 'REQUEST': 'GetMap', + 'LAYERS': layer_name, // US Elevation + 'STYLES': '', + 'BBOX': bbox_mercator, + 'WIDTH': 256, //Use 256x256 tiles + 'HEIGHT': 256, + 'FORMAT': 'image/png', + 'TRANSPARENT': true, + 'SRS': projection, + 'TILED': true, + 'SLD_BODY': sld + }; + // OpenGeo Demo Web Map Service + var baseUrl = + 'http://demo.boundlessgeo.com/geoserver/ows'; + return baseUrl + '?' + $.param(params); + }); + + return wms; + + } +}; diff --git a/examples/sld/thumb.jpg b/examples/sld/thumb.jpg new file mode 100644 index 0000000000..a857689608 Binary files /dev/null and b/examples/sld/thumb.jpg differ diff --git a/package.json b/package.json index ec33746f08..8e771154f9 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "bootswatch": "^3.3.6", "codecov.io": "^0.1.6", "codemirror": "^5.15.2", + "colorbrewer": "^1.0.0", "css-loader": "^0.23.1", "docco": "^0.7.0", "earcut": "^2.1.1", diff --git a/webpack-examples.config.js b/webpack-examples.config.js index 672dbbc658..29ddade3e5 100644 --- a/webpack-examples.config.js +++ b/webpack-examples.config.js @@ -29,6 +29,9 @@ var loaders = base.module.loaders.concat([{ }, { test: /jsonlint\.js$/, loader: 'expose?jsonlint' +}, { + test: require.resolve('colorbrewer'), + loader: 'expose?colorbrewer' }]); loaders = loaders.concat(external.module.loaders);