├── .editorconfig ├── .github └── workflows │ ├── nodejs.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── LICENSE.md ├── README.md ├── bin └── wmscapabilities ├── example ├── css │ ├── prism.css │ ├── style.css │ ├── topcoat-desktop-dark.css │ └── topcoat-desktop-light.css ├── font │ ├── LICENSE.txt │ ├── SourceCodePro-Black.otf │ ├── SourceCodePro-Bold.otf │ ├── SourceCodePro-ExtraLight.otf │ ├── SourceCodePro-Light.otf │ ├── SourceCodePro-Regular.otf │ ├── SourceCodePro-Semibold.otf │ ├── SourceSansPro-Black.otf │ ├── SourceSansPro-BlackIt.otf │ ├── SourceSansPro-Bold.otf │ ├── SourceSansPro-BoldIt.otf │ ├── SourceSansPro-ExtraLight.otf │ ├── SourceSansPro-ExtraLightIt.otf │ ├── SourceSansPro-It.otf │ ├── SourceSansPro-Light.otf │ ├── SourceSansPro-LightIt.otf │ ├── SourceSansPro-Regular.otf │ ├── SourceSansPro-Semibold.otf │ └── SourceSansPro-SemiboldIt.otf ├── img │ └── dropdown.svg ├── index.html ├── js │ ├── app.js │ ├── bundle.js │ ├── json-format.js │ ├── prism.js │ └── xml-format.js └── less │ ├── style.less │ └── topcoat-select.less ├── index.d.ts ├── index.html ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── externs.js ├── index.js ├── node_types.js ├── parsers.js ├── utils │ ├── isdef.js │ ├── setifundefined.js │ └── string.js ├── xlink.js ├── xml_parser.js └── xsd.js └── test ├── fixtures ├── analyses.xml ├── dmsp.xml ├── forecasts.xml ├── obs.xml └── wwa.xml └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | 10 | [*{.scss,.less}] 11 | indent_style = tab 12 | 13 | [*.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.x, 12.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm ci 28 | - run: npm run build --if-present 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*.*.*' 5 | 6 | jobs: 7 | publish: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | - run: npm ci 17 | - run: npm run build 18 | - run: npm test 19 | - uses: JS-DevTools/npm-publish@v1 20 | with: 21 | token: ${{ secrets.NPM_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Commenting this out is preferred by some people, see 24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 25 | node_modules 26 | 27 | # Users Environment Variables 28 | .lock-wscript 29 | 30 | .reify-cache 31 | dist 32 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example 2 | test 3 | .reify-cache 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2005-2016 OpenLayers Contributors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY OPENLAYERS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 14 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 15 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 16 | SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 18 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 20 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 21 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of OpenLayers Contributors. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WMS `GetCapabilities` parser 2 | [![npm version](https://badge.fury.io/js/wms-capabilities.svg)](http://badge.fury.io/js/wms-capabilities) 3 | 4 | Parses [WMS](http://en.wikipedia.org/wiki/Web_Map_Service) capabilities XML format to JSON. This is a simplified excerpt from [OpenLayers](https://github.com/openlayers/ol3) code to be used separately from its large codebase. 5 | 6 | ## [Demo](https://w8r.github.io/wms-capabilities) 7 | 8 | ## Usage 9 | 10 | ### ES 11 | ``` 12 | npm install wms-capabilities --save 13 | ``` 14 | ```js 15 | import WMSCapabilities from 'wms-capabilities'; 16 | ... 17 | new WMSCapabilities().parse(xmlString); 18 | //or 19 | new WMSCapabilities(xmlString).toJSON(); 20 | // or 21 | new WMSCapabilities().readFromDocument(xmldoc); 22 | ``` 23 | ### Browser 24 | ```html 25 | 26 | ... 27 | new WMSCapabilities().parse(xmlString); 28 | ``` 29 | 30 | ### Node 31 | 32 | Requires `xmldom` to traverse XML 33 | ```sh 34 | $npm install --save xmldom 35 | ``` 36 | then 37 | ```js 38 | import xmldom from 'xmldom'; // 'xmldom' doesn't 'export' the DOMParser 39 | import WMSCapabilities from 'wms-capabilities'; 40 | ... 41 | new WMSCapabilities(xmlString, xmldom.DOMParser).toJSON(); 42 | ``` 43 | 44 | ### Command-line 45 | 46 | ```sh 47 | $ npm install -g wms-capabilities 48 | $ cat capabilities.xml | wmscapabilities > out.json 49 | $ # or 50 | $ wmscapabilities capabilities.json > out.json 51 | ``` 52 | -------------------------------------------------------------------------------- /bin/wmscapabilities: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var xmldom = require('xmldom'); 4 | var WMSCapabilites = require('../dist/wms-capabilities.min.js'); 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | 8 | var pkg = require('../package.json'); 9 | var xml = ''; 10 | 11 | var args = process.argv.slice(2); 12 | var arg = args[0]; 13 | 14 | var stream = process.stdin; 15 | 16 | if (arg === '--version') { 17 | console.log(pkg.version) 18 | process.exit(0) 19 | } else if (arg === '--help') { 20 | console.log('\n WMS Capabilities converter', pkg.version, '\n'); 21 | console.log(' $ cat capabilities.xml | wmscapabilities > out.json'); 22 | console.log(' $ wmscapabilities capabilities.xml > out.json\n'); 23 | process.exit(0); 24 | } else if (arg) { 25 | stream = fs.createReadStream(path.join(process.cwd(), arg)); 26 | } 27 | 28 | stream.on('data', function (data) { 29 | xml += data; 30 | }); 31 | 32 | stream.resume(); 33 | 34 | stream.on('end', function () { 35 | var json = new WMSCapabilites(xml, xmldom.DOMParser).toJSON(); 36 | process.stdout.write(JSON.stringify(json, 0, 2) + '\n'); 37 | }); 38 | 39 | -------------------------------------------------------------------------------- /example/css/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-twilight&languages=markup+twig+clike+javascript&plugins=line-numbers */ 2 | /** 3 | * prism.js Twilight theme 4 | * Based (more or less) on the Twilight theme originally of Textmate fame. 5 | * @author Remy Bach 6 | */ 7 | code[class*="language-"], 8 | pre[class*="language-"] { 9 | color: white; 10 | direction: ltr; 11 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 12 | text-align: left; 13 | text-shadow: 0 -.1em .2em black; 14 | white-space: pre; 15 | word-spacing: normal; 16 | word-break: normal; 17 | line-height: 1.5; 18 | 19 | -moz-tab-size: 4; 20 | -o-tab-size: 4; 21 | tab-size: 4; 22 | 23 | -webkit-hyphens: none; 24 | -moz-hyphens: none; 25 | -ms-hyphens: none; 26 | hyphens: none; 27 | } 28 | 29 | pre[class*="language-"], 30 | :not(pre) > code[class*="language-"] { 31 | background: hsl(0, 0%, 8%); /* #141414 */ 32 | } 33 | 34 | /* Code blocks */ 35 | pre[class*="language-"] { 36 | border-radius: .5em; 37 | border: .3em solid hsl(0, 0%, 33%); /* #282A2B */ 38 | box-shadow: 1px 1px .5em black inset; 39 | margin: .5em 0; 40 | overflow: auto; 41 | padding: 1em; 42 | } 43 | 44 | pre[class*="language-"]::selection { 45 | /* Safari */ 46 | background: hsl(200, 4%, 16%); /* #282A2B */ 47 | } 48 | 49 | pre[class*="language-"]::selection { 50 | /* Firefox */ 51 | background: hsl(200, 4%, 16%); /* #282A2B */ 52 | } 53 | 54 | /* Text Selection colour */ 55 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 56 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 57 | text-shadow: none; 58 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */ 59 | } 60 | 61 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 62 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 63 | text-shadow: none; 64 | background: hsla(0, 0%, 93%, 0.15); /* #EDEDED */ 65 | } 66 | 67 | /* Inline code */ 68 | :not(pre) > code[class*="language-"] { 69 | border-radius: .3em; 70 | border: .13em solid hsl(0, 0%, 33%); /* #545454 */ 71 | box-shadow: 1px 1px .3em -.1em black inset; 72 | padding: .15em .2em .05em; 73 | } 74 | 75 | .token.comment, 76 | .token.prolog, 77 | .token.doctype, 78 | .token.cdata { 79 | color: hsl(0, 0%, 47%); /* #777777 */ 80 | } 81 | 82 | .token.punctuation { 83 | opacity: .7; 84 | } 85 | 86 | .namespace { 87 | opacity: .7; 88 | } 89 | 90 | .token.tag, 91 | .token.boolean, 92 | .token.number, 93 | .token.deleted { 94 | color: hsl(14, 58%, 55%); /* #CF6A4C */ 95 | } 96 | 97 | .token.keyword, 98 | .token.property, 99 | .token.selector, 100 | .token.constant, 101 | .token.symbol, 102 | .token.builtin { 103 | color: hsl(53, 89%, 79%); /* #F9EE98 */ 104 | } 105 | 106 | .token.attr-name, 107 | .token.attr-value, 108 | .token.string, 109 | .token.char, 110 | .token.operator, 111 | .token.entity, 112 | .token.url, 113 | .language-css .token.string, 114 | .style .token.string, 115 | .token.variable, 116 | .token.inserted { 117 | color: hsl(76, 21%, 52%); /* #8F9D6A */ 118 | } 119 | 120 | .token.atrule { 121 | color: hsl(218, 22%, 55%); /* #7587A6 */ 122 | } 123 | 124 | .token.regex, 125 | .token.important { 126 | color: hsl(42, 75%, 65%); /* #E9C062 */ 127 | } 128 | 129 | .token.important, 130 | .token.bold { 131 | font-weight: bold; 132 | } 133 | .token.italic { 134 | font-style: italic; 135 | } 136 | 137 | .token.entity { 138 | cursor: help; 139 | } 140 | 141 | pre[data-line] { 142 | padding: 1em 0 1em 3em; 143 | position: relative; 144 | } 145 | 146 | /* Markup */ 147 | .language-markup .token.tag, 148 | .language-markup .token.attr-name, 149 | .language-markup .token.punctuation { 150 | color: hsl(33, 33%, 52%); /* #AC885B */ 151 | } 152 | 153 | /* Make the tokens sit above the line highlight so the colours don't look faded. */ 154 | .token { 155 | position: relative; 156 | z-index: 1; 157 | } 158 | 159 | .line-highlight { 160 | background: -moz-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */ 161 | background: -o-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */ 162 | background: -webkit-linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */ 163 | background: hsla(0, 0%, 33%, 0.25); /* #545454 */ 164 | background: linear-gradient(left, hsla(0, 0%, 33%, .1) 70%, hsla(0, 0%, 33%, 0)); /* #545454 */ 165 | border-bottom: 1px dashed hsl(0, 0%, 33%); /* #545454 */ 166 | border-top: 1px dashed hsl(0, 0%, 33%); /* #545454 */ 167 | left: 0; 168 | line-height: inherit; 169 | margin-top: 0.75em; /* Same as .prism’s padding-top */ 170 | padding: inherit 0; 171 | pointer-events: none; 172 | position: absolute; 173 | right: 0; 174 | white-space: pre; 175 | z-index: 0; 176 | } 177 | 178 | .line-highlight:before, 179 | .line-highlight[data-end]:after { 180 | background-color: hsl(215, 15%, 59%); /* #8794A6 */ 181 | border-radius: 999px; 182 | box-shadow: 0 1px white; 183 | color: hsl(24, 20%, 95%); /* #F5F2F0 */ 184 | content: attr(data-start); 185 | font: bold 65%/1.5 sans-serif; 186 | left: .6em; 187 | min-width: 1em; 188 | padding: 0 .5em; 189 | position: absolute; 190 | text-align: center; 191 | text-shadow: none; 192 | top: .4em; 193 | vertical-align: .3em; 194 | } 195 | 196 | .line-highlight[data-end]:after { 197 | bottom: .4em; 198 | content: attr(data-end); 199 | top: auto; 200 | } 201 | 202 | pre.line-numbers { 203 | position: relative; 204 | padding-left: 3.8em; 205 | counter-reset: linenumber; 206 | } 207 | 208 | pre.line-numbers > code { 209 | position: relative; 210 | } 211 | 212 | .line-numbers .line-numbers-rows { 213 | position: absolute; 214 | pointer-events: none; 215 | top: 0; 216 | font-size: 100%; 217 | left: -3.8em; 218 | width: 3em; /* works for line-numbers below 1000 lines */ 219 | letter-spacing: -1px; 220 | border-right: 1px solid #999; 221 | 222 | -webkit-user-select: none; 223 | -moz-user-select: none; 224 | -ms-user-select: none; 225 | user-select: none; 226 | 227 | } 228 | 229 | .line-numbers-rows > span { 230 | pointer-events: none; 231 | display: block; 232 | counter-increment: linenumber; 233 | } 234 | 235 | .line-numbers-rows > span:before { 236 | content: counter(linenumber); 237 | color: #999; 238 | display: block; 239 | padding-right: 0.8em; 240 | text-align: right; 241 | } 242 | -------------------------------------------------------------------------------- /example/css/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | color: #F0F1F1; 6 | background: #4A4D4E; 7 | } 8 | .topcoat-select { 9 | padding: 0; 10 | margin: 0; 11 | font: inherit; 12 | color: inherit; 13 | background: transparent; 14 | border: none; 15 | } 16 | .topcoat-select { 17 | vertical-align: top; 18 | outline: none; 19 | } 20 | .topcoat-select { 21 | cursor: default; 22 | -webkit-user-select: none; 23 | -moz-user-select: none; 24 | -ms-user-select: none; 25 | user-select: none; 26 | } 27 | .topcoat-select { 28 | -moz-box-sizing: border-box; 29 | box-sizing: border-box; 30 | background-clip: padding-box; 31 | } 32 | .topcoat-select { 33 | position: relative; 34 | display: inline-block; 35 | vertical-align: top; 36 | } 37 | .topcoat-select:disabled { 38 | opacity: 0.3; 39 | cursor: default; 40 | pointer-events: none; 41 | } 42 | .topcoat-select { 43 | -moz-appearance: none; 44 | -webkit-appearance: none; 45 | } 46 | .topcoat-select::-ms-expand { 47 | display: none; 48 | } 49 | /* topdoc 50 | name: Topcoat Select 51 | description: A component that lets you select things 52 | modifiers: 53 | :disabled: Disabled state 54 | :focus: Focused 55 | :invalid: Hover state 56 | markup: 57 | 63 | 69 | tags: 70 | - desktop 71 | - mobile 72 | - text 73 | - input 74 | */ 75 | .topcoat-select { 76 | -webkit-user-select: none; 77 | -moz-user-select: none; 78 | -ms-user-select: none; 79 | user-select: none; 80 | cursor: pointer; 81 | appearance: button; 82 | text-indent: 0.01px; 83 | text-overflow: ''; 84 | padding: 0.7rem 1.3rem 0.7rem 1rem; 85 | font-size: 16px; 86 | font-weight: 400; 87 | height: 3rem; 88 | letter-spacing: 1px; 89 | color: hsl(180, 2%, 78%); 90 | text-shadow: 0 -1px hsla(0, 0%, 0%, 0.69); 91 | border-radius: 6px; 92 | background-color: hsl(180, 1%, 35%); 93 | box-shadow: inset 0 1px hsl(0, 0%, 45%); 94 | border: 1px solid hsl(180, 1%, 20%); 95 | background-image: url('../img/dropdown.svg'); 96 | background-repeat: no-repeat; 97 | background-position: center right; 98 | } 99 | .topcoat-select:hover { 100 | background-color: hsl(200, 2%, 39%); 101 | } 102 | .topcoat-select:active { 103 | background-color: hsl(210, 2%, 25%); 104 | box-shadow: inset 0 1px hsla(0, 0%, 0%, 0.05); 105 | } 106 | .topcoat-select:focus { 107 | border: 1px solid hsl(227, 100%, 50%); 108 | box-shadow: 0 0 0 2px hsl(208, 82%, 69%); 109 | outline: 0; 110 | } 111 | h2, 112 | h3 { 113 | font-weight: 300; 114 | } 115 | .container { 116 | margin: 0 auto; 117 | } 118 | .container a { 119 | color: #288edf; 120 | text-decoration: none; 121 | } 122 | .container a:hover { 123 | text-decoration: underline; 124 | } 125 | .container header { 126 | position: relative; 127 | } 128 | .container header:before { 129 | content: ''; 130 | width: 100%; 131 | display: block; 132 | position: absolute; 133 | left: 0; 134 | top: 23px; 135 | } 136 | .container .wrapper { 137 | padding: 30px; 138 | } 139 | .container .github-button { 140 | color: transparent; 141 | } 142 | .container .code, 143 | .container .code:focus { 144 | max-height: 350px; 145 | margin-right: 20px; 146 | border-radius: 0.5em; 147 | border: 0.3em solid hsl(0, 0%, 33%); 148 | box-shadow: 1px 1px 0.5em black inset; 149 | margin: 0; 150 | overflow: auto; 151 | padding: 1em; 152 | background: hsl(0, 0%, 8%); 153 | color: white; 154 | direction: ltr; 155 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 156 | text-align: left; 157 | text-shadow: 0 -0.1em 0.2em black; 158 | white-space: pre; 159 | word-spacing: normal; 160 | word-break: normal; 161 | line-height: 1.5; 162 | -moz-tab-size: 4; 163 | -o-tab-size: 4; 164 | tab-size: 4; 165 | -webkit-hyphens: none; 166 | -moz-hyphens: none; 167 | -ms-hyphens: none; 168 | hyphens: none; 169 | width: 95%; 170 | } 171 | .container #input-area { 172 | display: none; 173 | } 174 | .container .outputs { 175 | clear: both; 176 | padding-bottom: 40px; 177 | } 178 | .container .output { 179 | width: 49%; 180 | vertical-align: top; 181 | display: inline-block; 182 | min-width: 600px; 183 | } 184 | @media (min-width: 900px) { 185 | #map { 186 | width: 60%; 187 | height: 100%; 188 | } 189 | #controls { 190 | width: 40%; 191 | } 192 | #controls .control.range { 193 | display: inline-block; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /example/font/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /example/font/SourceCodePro-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-Black.otf -------------------------------------------------------------------------------- /example/font/SourceCodePro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-Bold.otf -------------------------------------------------------------------------------- /example/font/SourceCodePro-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-ExtraLight.otf -------------------------------------------------------------------------------- /example/font/SourceCodePro-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-Light.otf -------------------------------------------------------------------------------- /example/font/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /example/font/SourceCodePro-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceCodePro-Semibold.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-Black.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-BlackIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-BlackIt.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-Bold.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-BoldIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-BoldIt.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-ExtraLight.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-ExtraLight.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-ExtraLightIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-ExtraLightIt.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-It.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-It.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-Light.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-LightIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-LightIt.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-Semibold.otf -------------------------------------------------------------------------------- /example/font/SourceSansPro-SemiboldIt.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w8r/wms-capabilities/f6ade93d2dd6c357047b940f787f9425b524d21c/example/font/SourceSansPro-SemiboldIt.otf -------------------------------------------------------------------------------- /example/img/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WMSCapabilities 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

WMS Service GetCapabilities Reader

21 | Star 24 | Fork 27 | 28 |
29 |

Select service or copy your XML into the input

30 | 45 |
46 | 47 |
48 |
49 |

XML

50 |

52 |           
55 |         
56 | 57 |
58 |

JSON

59 |

61 |         
62 |
63 | 64 | 71 | 72 |
73 |
74 | 75 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /example/js/app.js: -------------------------------------------------------------------------------- 1 | import jsonFormat from './json-format'; 2 | import xmlFormat from './xml-format'; 3 | import WMSCapabilities from '../../dist/wms-capabilities.min'; 4 | //import Spinner from 'spin.js'; 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | var serviceSelect = document.getElementById('service'); 8 | var xml = document.getElementById('xml'); 9 | var json = document.getElementById('json'); 10 | var input = document.getElementById('input-area'); 11 | 12 | // the only open CORS proxy I could find 13 | var parser = new WMSCapabilities(); 14 | 15 | function showInput() { 16 | xml.style.display = 'none'; 17 | input.style.display = 'inline-block'; 18 | } 19 | 20 | function hideInput() { 21 | xml.style.display = 'inline-block'; 22 | input.style.display = 'none'; 23 | } 24 | 25 | function update(xmlString) { 26 | xml.textContent = xmlFormat(xmlString); 27 | Prism.highlightElement(xml); 28 | 29 | json.textContent = jsonFormat(JSON.stringify(parser.parse(xmlString))); 30 | Prism.highlightElement(json); 31 | } 32 | 33 | serviceSelect.addEventListener('change', function() { 34 | if (serviceSelect.value !== '') { 35 | hideInput(); 36 | 37 | fetch(serviceSelect.value) 38 | .then(response => response.text()) 39 | .then(xmlString => update(xmlString)); 40 | } 41 | }, false); 42 | 43 | xml.addEventListener('click', showInput, false); 44 | 45 | input.addEventListener('paste', function() { 46 | setTimeout(function() { 47 | update(input.value); 48 | hideInput(); 49 | }, 50); 50 | }, false); 51 | -------------------------------------------------------------------------------- /example/js/bundle.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | /* 5 | json-format v.1.1 6 | http://github.com/phoboslab/json-format 7 | 8 | Released under MIT license: 9 | http://www.opensource.org/licenses/mit-license.php 10 | */ 11 | 12 | var p = [], 13 | push = function(m) { 14 | return '\\' + p.push(m) + '\\'; 15 | }, 16 | pop = function(m, i) { 17 | return p[i - 1] 18 | }, 19 | tabs = function(count) { 20 | return new Array(count + 1).join('\t'); 21 | }; 22 | 23 | function jsonFormat(json) { 24 | p = []; 25 | var out = "", 26 | indent = 0; 27 | 28 | // Extract backslashes and strings 29 | json = json 30 | .replace(/\\./g, push) 31 | .replace(/(".*?"|'.*?')/g, push) 32 | .replace(/\s+/, ''); 33 | 34 | // Indent and insert newlines 35 | for (var i = 0; i < json.length; i++) { 36 | var c = json.charAt(i); 37 | 38 | switch (c) { 39 | case '{': 40 | out += c + "\n" + tabs(++indent); 41 | break; 42 | case '[': 43 | out += c + "\n" + tabs(++indent); 44 | break; 45 | case ']': 46 | out += "\n" + tabs(--indent) + c; 47 | break; 48 | case '}': 49 | out += "\n" + tabs(--indent) + c; 50 | break; 51 | case ',': 52 | if (/\d/.test(json.charAt(i - 1))) { 53 | out += ", "; 54 | } else { 55 | out += ",\n" + tabs(indent); 56 | } 57 | break; 58 | case ':': 59 | out += ": "; 60 | break; 61 | default: 62 | out += c; 63 | break; 64 | } 65 | } 66 | 67 | // Strip whitespace from numeric arrays and put backslashes 68 | // and strings back in 69 | out = out 70 | .replace(/\[[\d,\s]+?\]/g, function(m) { 71 | return m.replace(/\s/g, ''); 72 | }) 73 | // number arrays 74 | .replace(/\[\s*(\d)/g, function(a, b) { 75 | return '[' + b; 76 | }) 77 | .replace(/(\d)\s*\]/g, function(a, b) { 78 | return b + ']'; 79 | }) 80 | .replace(/\{\s*\}/g, '{}') // empty objects 81 | .replace(/\\(\d+)\\/g, pop) // strings 82 | .replace(/\\(\d+)\\/g, pop); // backslashes in strings 83 | 84 | return out; 85 | } 86 | 87 | function xmlFormat(xml) { 88 | var formatted = ''; 89 | var reg = /(>)(<)(\/*)/g; 90 | xml = xml.replace(reg, '$1\r\n$2$3'); 91 | var pad = 0; 92 | 93 | xml.split('\r\n').forEach(function(node, index) { 94 | var indent = 0; 95 | if (node.match(/.+<\/\w[^>]*>$/)) { 96 | indent = 0; 97 | } else if (node.match(/^<\/\w/)) { 98 | if (pad != 0) { 99 | pad -= 1; 100 | } 101 | } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { 102 | indent = 1; 103 | } else { 104 | indent = 0; 105 | } 106 | 107 | var padding = ''; 108 | for (var i = 0; i < pad; i++) { 109 | padding += ' '; 110 | } 111 | 112 | formatted += padding + node + '\r\n'; 113 | pad += indent; 114 | }); 115 | 116 | return formatted; 117 | } 118 | 119 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; 120 | 121 | function createCommonjsModule(fn) { 122 | var module = { exports: {} }; 123 | return fn(module, module.exports), module.exports; 124 | } 125 | 126 | /* 127 | BSD-2-Clause 128 | @preserve 129 | */ 130 | 131 | var wmsCapabilities_min = createCommonjsModule(function (module, exports) { 132 | (function(f,m){module.exports=m();})(commonjsGlobal,function(){function f(a){return void 0!==a}function m(a,c,b){if(a.nodeType===u.CDATA_SECTION||a.nodeType===u.TEXT){ c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue); }else { for(a=a.firstChild;a;a=a.nextSibling){ m(a,c,b); } }return b}function F(a){for(a= 133 | a.nextElementSibling||a.nextSibling;a&&a.nodeType!==u.ELEMENT;){ a=a.nextSibling; }return a}function h(a,c,b){b=f(b)?b:{};var d;var n=0;for(d=a.length;ne.length)break e;if(!(d instanceof a)){g.lastIndex=0;var m=g.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),O=[p,1];b&&O.push(b);var N=new a(l,u?t.tokenize(m,u):m,h);O.push(N),w&&O.push(w),Array.prototype.splice.apply(r,O)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; 4 | Prism.languages.twig={comment:/\{#[\s\S]*?#\}/g,tag:{pattern:/(\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\})/g,inside:{ld:{pattern:/^(\{\{\-?|\{%\-?\s*\w+)/,inside:{punctuation:/^(\{\{|\{%)\-?/,keyword:/\w+/}},rd:{pattern:/\-?(%\}|\}\})$/,inside:{punctuation:/.*/}},string:{pattern:/("|')(\\?.)*?\1/g,inside:{punctuation:/^('|")|('|")$/g}},keyword:/\b(if)\b/g,"boolean":/\b(true|false|null)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/==|=|!=|<|>|>=|<=|\+|\-|~|\*|\/|\/\/|%|\*\*|\|/g,"space-operator":{pattern:/(\s)(\b(not|b\-and|b\-xor|b\-or|and|or|in|matches|starts with|ends with|is)\b|\?|:|\?:)(?=\s)/g,lookbehind:!0,inside:{operator:/.*/}},property:/\b[a-zA-Z_][a-zA-Z0-9_]*\b/g,punctuation:/\(|\)|\[\]|\[|\]|\{|\}|:|\.|,/g}},other:{pattern:/[\s\S]*/,inside:Prism.languages.markup}};; 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/g,"function":/(?!\d)[a-z0-9_$]+(?=\()/gi}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});; 7 | Prism.hooks.add("after-highlight",function(e){var n=e.element.parentNode;if(n&&/pre/i.test(n.nodeName)&&-1!==n.className.indexOf("line-numbers")){var t,a=1+e.code.split("\n").length;lines=new Array(a),lines=lines.join(""),t=document.createElement("span"),t.className="line-numbers-rows",t.innerHTML=lines,n.hasAttribute("data-start")&&(n.style.counterReset="linenumber "+(parseInt(n.getAttribute("data-start"),10)-1)),e.element.appendChild(t)}});; 8 | -------------------------------------------------------------------------------- /example/js/xml-format.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export default function(xml) { 4 | var formatted = ''; 5 | var reg = /(>)(<)(\/*)/g; 6 | xml = xml.replace(reg, '$1\r\n$2$3'); 7 | var pad = 0; 8 | 9 | xml.split('\r\n').forEach(function(node, index) { 10 | var indent = 0; 11 | if (node.match(/.+<\/\w[^>]*>$/)) { 12 | indent = 0; 13 | } else if (node.match(/^<\/\w/)) { 14 | if (pad != 0) { 15 | pad -= 1; 16 | } 17 | } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { 18 | indent = 1; 19 | } else { 20 | indent = 0; 21 | } 22 | 23 | var padding = ''; 24 | for (var i = 0; i < pad; i++) { 25 | padding += ' '; 26 | } 27 | 28 | formatted += padding + node + '\r\n'; 29 | pad += indent; 30 | }); 31 | 32 | return formatted; 33 | } 34 | -------------------------------------------------------------------------------- /example/less/style.less: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | color: #F0F1F1; 5 | background: #4A4D4E; 6 | } 7 | 8 | @import "example/less/topcoat-select.less"; 9 | 10 | h2, h3 { 11 | font-weight: 300; 12 | } 13 | 14 | .container { 15 | //max-width: 700px; 16 | margin: 0 auto; 17 | 18 | a { 19 | color: #288edf; 20 | text-decoration: none; 21 | 22 | &:hover { 23 | text-decoration: underline; 24 | } 25 | } 26 | 27 | header { 28 | position: relative; 29 | 30 | &:before { 31 | content: ''; 32 | width: 100%; 33 | display: block; 34 | position: absolute; 35 | left: 0; 36 | top: 23px; 37 | } 38 | } 39 | 40 | .wrapper { 41 | padding: 30px; 42 | } 43 | 44 | .github-button { color: transparent; } 45 | 46 | .code, .code:focus { 47 | max-height: 350px; 48 | margin-right: 20px; 49 | 50 | border-radius: .5em; 51 | border: .3em solid hsl(0, 0%, 33%); 52 | box-shadow: 1px 1px .5em black inset; 53 | margin: 0; 54 | overflow: auto; 55 | padding: 1em; 56 | background: hsl(0, 0%, 8%); 57 | color: white; 58 | direction: ltr; 59 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 60 | text-align: left; 61 | text-shadow: 0 -.1em .2em black; 62 | white-space: pre; 63 | word-spacing: normal; 64 | word-break: normal; 65 | line-height: 1.5; 66 | -moz-tab-size: 4; 67 | -o-tab-size: 4; 68 | tab-size: 4; 69 | -webkit-hyphens: none; 70 | -moz-hyphens: none; 71 | -ms-hyphens: none; 72 | hyphens: none; 73 | width: 95%; 74 | } 75 | 76 | #input-area { 77 | display: none; 78 | } 79 | 80 | .outputs { 81 | clear: both; 82 | padding-bottom: 40px; 83 | } 84 | 85 | .output { 86 | width: 49%; 87 | vertical-align: top; 88 | display: inline-block; 89 | min-width: 600px; 90 | } 91 | 92 | } 93 | 94 | @media (min-width:900px) { 95 | #map { 96 | width: 60%; 97 | height: 100%; 98 | } 99 | 100 | #controls { 101 | width: 40%; 102 | } 103 | 104 | #controls .control.range { 105 | display: inline-block; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /example/less/topcoat-select.less: -------------------------------------------------------------------------------- 1 | .topcoat-select { 2 | padding: 0; 3 | margin: 0; 4 | font: inherit; 5 | color: inherit; 6 | background: transparent; 7 | border: none; 8 | } 9 | 10 | .topcoat-select { 11 | vertical-align: top; 12 | outline: none; 13 | } 14 | 15 | .topcoat-select { 16 | cursor: default; 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -ms-user-select: none; 20 | user-select: none; 21 | } 22 | 23 | .topcoat-select { 24 | -moz-box-sizing: border-box; 25 | box-sizing: border-box; 26 | background-clip: padding-box; 27 | } 28 | 29 | .topcoat-select { 30 | position: relative; 31 | display: inline-block; 32 | vertical-align: top; 33 | } 34 | 35 | .topcoat-select:disabled { 36 | opacity: 0.3; 37 | cursor: default; 38 | pointer-events: none; 39 | } 40 | 41 | .topcoat-select { 42 | -moz-appearance: none; 43 | -webkit-appearance: none; 44 | } 45 | 46 | .topcoat-select::-ms-expand { 47 | display: none; 48 | } 49 | 50 | /* topdoc 51 | name: Topcoat Select 52 | description: A component that lets you select things 53 | modifiers: 54 | :disabled: Disabled state 55 | :focus: Focused 56 | :invalid: Hover state 57 | markup: 58 | 64 | 70 | tags: 71 | - desktop 72 | - mobile 73 | - text 74 | - input 75 | */ 76 | 77 | .topcoat-select { 78 | -webkit-user-select: none; 79 | -moz-user-select: none; 80 | -ms-user-select: none; 81 | user-select: none; 82 | cursor: pointer; 83 | appearance: button; 84 | text-indent: 0.01px; 85 | text-overflow: ''; 86 | padding: 0.7rem 1.3rem 0.7rem 1rem; 87 | font-size: 16px; 88 | font-weight: 400; 89 | height: 3rem; 90 | letter-spacing: 1px; 91 | color: hsla(180, 2%, 78%, 1); 92 | text-shadow: 0 -1px hsla(0, 0%, 0%, 0.69); 93 | border-radius: 6px; 94 | background-color: hsla(180, 1%, 35%, 1); 95 | box-shadow: inset 0 1px hsla(0, 0%, 45%, 1); 96 | border: 1px solid hsla(180, 1%, 20%, 1); 97 | background-image: url('../img/dropdown.svg'); 98 | background-repeat: no-repeat; 99 | background-position: center right; 100 | } 101 | 102 | .topcoat-select:hover { 103 | background-color: hsla(200, 2%, 39%, 1); 104 | } 105 | 106 | .topcoat-select:active { 107 | background-color: hsla(210, 2%, 25%, 1); 108 | box-shadow: inset 0 1px hsla(0, 0%, 0%, 0.05); 109 | } 110 | 111 | .topcoat-select:focus { 112 | border: 1px solid hsla(227, 100%, 50%, 1); 113 | box-shadow: 0 0 0 2px hsla(208, 82%, 69%, 1); 114 | outline: 0; 115 | } 116 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export interface WMSCapabilitiesJSON { 2 | version: string; 3 | Service: Service; 4 | Capability: Capability; 5 | } 6 | 7 | export interface Service { 8 | Name: string; 9 | Title: string; 10 | Abstract: string; 11 | KeywordList: string[]; 12 | OnlineResource: string; 13 | ContactInformation: ContactInformation; 14 | Fees: string; 15 | AccessConstraints: string; 16 | MaxWidth: number; 17 | MaxHeight: number; 18 | } 19 | 20 | export interface ContactInformation { 21 | ContactPersonPrimary: ContactPersonPrimary; 22 | ContactPosition: string; 23 | ContactAddress: ContactAddress; 24 | ContactVoiceTelephone: string; 25 | ContactFacsimileTelephone: string; 26 | ContactElectronicMailAddress: string; 27 | } 28 | 29 | export interface ContactPersonPrimary { 30 | ContactPerson: string; 31 | ContactOrganization: string; 32 | } 33 | 34 | export interface ContactAddress { 35 | AddressType: string; 36 | Address: string; 37 | City: string; 38 | StateOrProvince: string; 39 | PostCode: string; 40 | Country: string; 41 | } 42 | 43 | export interface Capability { 44 | Request: Request; 45 | Exception: string[]; 46 | Layer: Layer; 47 | } 48 | 49 | export interface Request { 50 | GetCapabilities: GetCapabilities; 51 | GetMap: GetMap; 52 | GetFeatureInfo: GetFeatureInfo; 53 | } 54 | 55 | export interface GetCapabilities { 56 | Format: string[]; 57 | DCPType: Dcptype[]; 58 | } 59 | 60 | export interface Dcptype { 61 | HTTP: Http; 62 | } 63 | 64 | export interface Http { 65 | Get: Get; 66 | } 67 | 68 | export interface Get { 69 | OnlineResource: string; 70 | } 71 | 72 | export interface GetMap { 73 | Format: string[]; 74 | DCPType: Dcptype2[]; 75 | } 76 | 77 | export interface Dcptype2 { 78 | HTTP: Http2; 79 | } 80 | 81 | export interface Http2 { 82 | Get: Get2; 83 | } 84 | 85 | export interface Get2 { 86 | OnlineResource: string; 87 | } 88 | 89 | export interface GetFeatureInfo { 90 | Format: string[]; 91 | DCPType: Dcptype3[]; 92 | } 93 | 94 | export interface Dcptype3 { 95 | HTTP: Http3; 96 | } 97 | 98 | export interface Http3 { 99 | Get: Get3; 100 | } 101 | 102 | export interface Get3 { 103 | OnlineResource: string; 104 | } 105 | 106 | export interface Layer { 107 | queryable: boolean; 108 | Title: string; 109 | CRS: string[]; 110 | EX_GeographicBoundingBox: number[]; 111 | BoundingBox: BoundingBox[]; 112 | Layer: Layer2[]; 113 | } 114 | 115 | export interface BoundingBox { 116 | crs: string; 117 | extent: number[]; 118 | res: any[]; 119 | } 120 | 121 | export interface Layer2 { 122 | Title: string; 123 | Abstract: string; 124 | CRS: string[]; 125 | EX_GeographicBoundingBox: number[]; 126 | BoundingBox: BoundingBox2[]; 127 | Dimension: Dimension[]; 128 | Layer: Layer3[]; 129 | queryable: boolean; 130 | opaque: boolean; 131 | noSubsets: boolean; 132 | } 133 | 134 | export interface BoundingBox2 { 135 | crs: string; 136 | extent: number[]; 137 | res: any[]; 138 | } 139 | 140 | export interface Dimension { 141 | name: string; 142 | units: string; 143 | unitSymbol: any; 144 | default: any; 145 | current: boolean; 146 | values: string; 147 | } 148 | 149 | export interface Layer3 { 150 | Name: string; 151 | Title: string; 152 | Abstract: string; 153 | CRS: string[]; 154 | EX_GeographicBoundingBox: number[]; 155 | BoundingBox: BoundingBox3[]; 156 | Style: Style[]; 157 | queryable: boolean; 158 | opaque: boolean; 159 | noSubsets: boolean; 160 | Dimension: Dimension2[]; 161 | } 162 | 163 | export interface BoundingBox3 { 164 | crs: string; 165 | extent: number[]; 166 | res: any[]; 167 | } 168 | 169 | export interface Style { 170 | Name: string; 171 | Title: string; 172 | LegendURL: LegendUrl[]; 173 | } 174 | 175 | export interface LegendUrl { 176 | Format: string; 177 | OnlineResource: string; 178 | size: number[]; 179 | } 180 | 181 | export interface Dimension2 { 182 | name: string; 183 | units: string; 184 | unitSymbol: any; 185 | default: any; 186 | current: boolean; 187 | values: string; 188 | } 189 | 190 | export default class WMS { 191 | /** 192 | * WMS Capabilities parser 193 | * 194 | * @param {String=} xmlString 195 | * @constructor 196 | */ 197 | constructor(xmlString?: string | undefined, DOMParser: any); 198 | /** 199 | * @type {String} 200 | */ 201 | version: string; 202 | /** 203 | * @type {XMLParser} 204 | */ 205 | _parser: XMLParser; 206 | /** 207 | * @type {String=} 208 | */ 209 | _data: string | undefined; 210 | /** 211 | * @param {String} xmlString 212 | * @return {WMS} 213 | */ 214 | data(xmlString: string): WMS; 215 | /** 216 | * @param {String=} xmlString 217 | * @return {Object|null} 218 | */ 219 | toJSON(xmlString?: string | undefined): WMSCapabilitiesJSON | null; 220 | /** 221 | * @return {String} xml 222 | * @return {Object|null} 223 | */ 224 | parse(xmlString: any): WMSCapabilitiesJSON; 225 | /** 226 | * @param {Document} doc 227 | * @return {Object|null} 228 | */ 229 | readFromDocument(doc: Document): WMSCapabilitiesJSON | null; 230 | /** 231 | * @param {DOMNode} node 232 | * @return {Object} 233 | */ 234 | readFromNode(node: DOMNode): WMSCapabilitiesJSON; 235 | } 236 | 237 | class XMLParser { 238 | /** 239 | * XML DOM parser 240 | * @constructor 241 | * @param {DOMParser} DOMParser 242 | */ 243 | constructor(DOMParser: DOMParser); 244 | /** 245 | * @type {DOMParser} 246 | */ 247 | _parser: DOMParser; 248 | /** 249 | * @param {String} xmlstring 250 | * @return {Document} 251 | */ 252 | toDocument(xmlstring: string): Document; 253 | /** 254 | * Recursively grab all text content of child nodes into a single string. 255 | * @param {Node} node Node. 256 | * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line 257 | * breaks. 258 | * @return {string} All text content. 259 | * @api 260 | */ 261 | getAllTextContent(node: Node, normalizeWhitespace: boolean): string; 262 | } 263 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wms-capabilities", 3 | "version": "0.6.0", 4 | "description": "WMS service Capabilities > JSON, based on openlayers ", 5 | "main": "dist/wms-capabilities.min.js", 6 | "module": "dist/wms-capabilities.mjs", 7 | "types": "index.d.ts", 8 | "bin": { 9 | "wmscapabilities": "bin/wmscapabilities" 10 | }, 11 | "scripts": { 12 | "test": "node -r reify test/index.js | tap-spec", 13 | "start": "npm run watch-js & npm run watch-css", 14 | "watch-css": "catw -c 'lessc -' 'example/less/*.less' -o example/css/style.css -v", 15 | "watch-js": "rollup -cw", 16 | "build-less": "lessc example/less/style.less > example/css/style.css", 17 | "build-css": "npm run build-less", 18 | "build-js": "rollup -c", 19 | "build": "npm run build-js && npm run build-css" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/w8r/wms-capabilities.git" 24 | }, 25 | "devDependencies": { 26 | "@ampproject/rollup-plugin-closure-compiler": "^0.26.0", 27 | "@rollup/plugin-buble": "^0.21.3", 28 | "@rollup/plugin-commonjs": "^18.0.0", 29 | "@rollup/plugin-node-resolve": "^11.2.1", 30 | "lessc": "^1.0.2", 31 | "reify": "^0.20.12", 32 | "rollup": "^2.45.2", 33 | "rollup-plugin-browsersync": "^1.3.1", 34 | "tap-spec": "^5.0.0", 35 | "tape": "^5.2.2", 36 | "xmldom": "^0.6.0" 37 | }, 38 | "keywords": [ 39 | "gis", 40 | "wms", 41 | "getcapabilities", 42 | "xml", 43 | "json" 44 | ], 45 | "author": "Alexander Milevski ", 46 | "license": "BSD-2-Clause", 47 | "bugs": { 48 | "url": "https://github.com/w8r/wms-capabilities/issues" 49 | }, 50 | "homepage": "https://github.com/w8r/wms-capabilities" 51 | } 52 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from "@rollup/plugin-buble"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import nodeResolve from "@rollup/plugin-node-resolve"; 4 | import compiler from "@ampproject/rollup-plugin-closure-compiler"; 5 | import browsersync from "rollup-plugin-browsersync"; 6 | import { name, description, license, version } from "./package.json"; 7 | 8 | const banner = ` 9 | /** 10 | * ${name} @${version} 11 | * @description ${description} 12 | * @license ${license} 13 | * @preserve 14 | */ 15 | `; 16 | 17 | export default [ 18 | { 19 | input: "src/index.js", 20 | output: { 21 | file: "dist/wms-capabilities.js", 22 | format: "umd", 23 | name: "WMSCapabilities", 24 | sourcemap: true, 25 | banner, 26 | }, 27 | plugins: [buble()], 28 | }, 29 | { 30 | input: "src/index.js", 31 | output: { 32 | file: "dist/wms-capabilities.mjs", 33 | format: "esm", 34 | name: "WMSCapabilities", 35 | sourcemap: true, 36 | banner, 37 | }, 38 | }, 39 | { 40 | input: "src/index.js", 41 | output: { 42 | file: "dist/wms-capabilities.min.js", 43 | format: "umd", 44 | name: "WMSCapabilities", 45 | sourcemap: true, 46 | banner, 47 | }, 48 | plugins: [buble(), compiler({})], 49 | }, 50 | { 51 | input: "example/js/app.js", 52 | output: { 53 | file: "example/js/bundle.js", 54 | format: "iife", 55 | }, 56 | plugins: [ 57 | process.argv[2].indexOf("w") !== -1 58 | ? browsersync({ 59 | server: ".", 60 | port: 3002, 61 | }) 62 | : null, 63 | nodeResolve(), 64 | commonjs(), 65 | buble(), 66 | ], 67 | }, 68 | ]; 69 | -------------------------------------------------------------------------------- /src/externs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Externs file for google closure compiler 3 | */ 4 | 5 | // this makes GCC play with browserify 6 | 7 | /** 8 | * @param {*=}o 9 | * @param {*=}u 10 | */ 11 | window.require = function(o, u) {}; 12 | 13 | /** 14 | * @type {Object} 15 | */ 16 | window.module = { 17 | exports: {} 18 | }; 19 | 20 | window['WMSCapabilities'] = function () {}; 21 | 22 | /** 23 | * @class WMSCapabilities 24 | * @constructor 25 | */ 26 | window['WMSCapabilities'] = function() {}; 27 | 28 | /** 29 | * @param {String} xmlString 30 | * @return {WMSCapabilities} 31 | */ 32 | window['WMSCapabilities'].prototype.data = function(xmlString) {}; 33 | 34 | /** 35 | * @param {String=} xmlString 36 | * @return {Object} 37 | */ 38 | window['WMSCapabilities'].prototype.toJSON = function(xmlString) {}; 39 | 40 | /** 41 | * @param {String} xmlString 42 | * @return {Object} 43 | */ 44 | window['WMSCapabilities'].prototype.parse = function(xmlString) {}; 45 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import XMLParser, { pushParseAndPop } from './xml_parser'; 2 | import nodeTypes from './node_types'; 3 | import { PARSERS } from './parsers'; 4 | 5 | export default class WMS { 6 | /** 7 | * WMS Capabilities parser 8 | * 9 | * @param {String=} xmlString 10 | * @constructor 11 | */ 12 | constructor(xmlString, DOMParser) { 13 | if (!DOMParser && typeof window !== 'undefined') { 14 | DOMParser = window.DOMParser; 15 | } 16 | 17 | /** 18 | * @type {String} 19 | */ 20 | this.version = undefined; 21 | 22 | /** 23 | * @type {XMLParser} 24 | */ 25 | this._parser = new XMLParser(DOMParser); 26 | 27 | /** 28 | * @type {String=} 29 | */ 30 | this._data = xmlString; 31 | } 32 | 33 | 34 | /** 35 | * @param {String} xmlString 36 | * @return {WMS} 37 | */ 38 | data (xmlString) { 39 | this._data = xmlString; 40 | return this; 41 | } 42 | 43 | /** 44 | * @param {String=} xmlString 45 | * @return {Object|null} 46 | */ 47 | toJSON (xmlString) { 48 | xmlString = xmlString || this._data; 49 | return this.parse(xmlString); 50 | } 51 | 52 | /** 53 | * @return {String} xml 54 | * @return {Object|null} 55 | */ 56 | parse (xmlString) { 57 | return this.readFromDocument(this._parser.toDocument(xmlString)); 58 | } 59 | 60 | /** 61 | * @param {Document} doc 62 | * @return {Object|null} 63 | */ 64 | readFromDocument (doc) { 65 | for (let node = doc.firstChild; node; node = node.nextSibling) { 66 | if (node.nodeType == nodeTypes.ELEMENT) { 67 | return this.readFromNode(node); 68 | } 69 | } 70 | return null; 71 | } 72 | 73 | /** 74 | * @param {DOMNode} node 75 | * @return {Object} 76 | */ 77 | readFromNode (node) { 78 | this.version = node.getAttribute('version'); 79 | const wmsCapabilityObject = pushParseAndPop({ 80 | 'version': this.version 81 | }, PARSERS, node, []); 82 | 83 | return wmsCapabilityObject || null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/node_types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @enum {Number} 3 | */ 4 | const NODE_TYPES = { 5 | ELEMENT: 1, 6 | ATTRIBUTE: 2, 7 | TEXT: 3, 8 | CDATA_SECTION: 4, 9 | ENTITY_REFERENCE: 5, 10 | ENTITY: 6, 11 | PROCESSING_INSTRUCTION: 7, 12 | COMMENT: 8, 13 | DOCUMENT: 9, 14 | DOCUMENT_TYPE: 10, 15 | DOCUMENT_FRAGMENT: 11, 16 | NOTATION: 12 17 | }; 18 | 19 | export default NODE_TYPES; 20 | -------------------------------------------------------------------------------- /src/parsers.js: -------------------------------------------------------------------------------- 1 | import XMLParser, { 2 | makeObjectPropertySetter, 3 | makeObjectPropertyPusher, 4 | makeParsersNS, 5 | pushParseAndPop, 6 | makeArrayPusher 7 | } from './xml_parser'; 8 | import { 9 | readString, 10 | readDecimalString, 11 | readBooleanString, 12 | readNonNegativeIntegerString, 13 | readNonNegativeInteger, 14 | readDecimal 15 | } from './xsd'; 16 | import { readHref } from './xlink'; 17 | import setIfUndefined from './utils/setifundefined'; 18 | import isDef from './utils/isdef'; 19 | 20 | /** 21 | * @param {Node} node Node. 22 | * @param {Array.<*>} objectStack Object stack. 23 | * @return {Object|undefined} Attribution object. 24 | */ 25 | function readAttribution(node, objectStack) { 26 | return pushParseAndPop({}, ATTRIBUTION_PARSERS, node, objectStack); 27 | } 28 | 29 | 30 | /** 31 | * @private 32 | * @param {Node} node Node. 33 | * @return {ol.Extent} Bounding box object. 34 | */ 35 | function readBoundingBoxExtent (node) { 36 | return [ 37 | readDecimalString(node.getAttribute('minx')), 38 | readDecimalString(node.getAttribute('miny')), 39 | readDecimalString(node.getAttribute('maxx')), 40 | readDecimalString(node.getAttribute('maxy')) 41 | ]; 42 | } 43 | 44 | /** 45 | * @private 46 | * @param {Node} node Node. 47 | * @param {Array.<*>} objectStack Object stack. 48 | * @return {Object} Bounding box object. 49 | */ 50 | function readBoundingBox (node, objectStack) { 51 | const extent = readBoundingBoxExtent(node); 52 | const resolutions = [ 53 | readDecimalString(node.getAttribute('resx')), 54 | readDecimalString(node.getAttribute('resy')) 55 | ]; 56 | 57 | return { 58 | 'crs': node.getAttribute('CRS') || node.getAttribute('SRS'), 59 | extent, res: resolutions 60 | }; 61 | } 62 | 63 | /** 64 | * @private 65 | * @param {Node} node Node. 66 | * @param {Array.<*>} objectStack Object stack. 67 | * @return {ol.Extent|undefined} Bounding box object. 68 | */ 69 | function readLatLonBoundingBox (node, objectStack) { 70 | const extent = readBoundingBoxExtent(node); 71 | 72 | if (!isDef(extent[0]) || !isDef(extent[1]) || 73 | !isDef(extent[2]) || !isDef(extent[3])) { 74 | return undefined; 75 | } 76 | 77 | return extent; 78 | } 79 | 80 | 81 | /** 82 | * @privat 83 | * @param {Node} node Node 84 | * @param {Arra.} objectStack Object stack 85 | * @return {Object} 86 | */ 87 | function readScaleHint (node, objectStack) { 88 | const min = parseFloat(node.getAttribute('min')); 89 | const max = parseFloat(node.getAttribute('max')); 90 | 91 | return { min, max }; 92 | } 93 | 94 | 95 | /** 96 | * @private 97 | * @param {Node} node Node. 98 | * @param {Array.<*>} objectStack Object stack. 99 | * @return {ol.Extent|undefined} Bounding box object. 100 | */ 101 | function readEXGeographicBoundingBox (node, objectStack) { 102 | const geographicBoundingBox = pushParseAndPop({}, 103 | EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS, 104 | node, objectStack); 105 | if (!isDef(geographicBoundingBox)) return undefined; 106 | 107 | const westBoundLongitude = /** @type {number|undefined} */ 108 | (geographicBoundingBox['westBoundLongitude']); 109 | const southBoundLatitude = /** @type {number|undefined} */ 110 | (geographicBoundingBox['southBoundLatitude']); 111 | const eastBoundLongitude = /** @type {number|undefined} */ 112 | (geographicBoundingBox['eastBoundLongitude']); 113 | const northBoundLatitude = /** @type {number|undefined} */ 114 | (geographicBoundingBox['northBoundLatitude']); 115 | 116 | if (!isDef(westBoundLongitude) || !isDef(southBoundLatitude) || 117 | !isDef(eastBoundLongitude) || !isDef(northBoundLatitude)) { 118 | return undefined; 119 | } 120 | 121 | return [ 122 | westBoundLongitude, southBoundLatitude, 123 | eastBoundLongitude, northBoundLatitude 124 | ]; 125 | } 126 | 127 | 128 | /** 129 | * @param {Node} node Node. 130 | * @param {Array.<*>} objectStack Object stack. 131 | * @private 132 | * @return {Object|undefined} Capability object. 133 | */ 134 | function readCapability (node, objectStack) { 135 | return pushParseAndPop({}, CAPABILITY_PARSERS, node, objectStack); 136 | } 137 | 138 | 139 | /** 140 | * @param {Node} node Node. 141 | * @param {Array.<*>} objectStack Object stack. 142 | * @private 143 | * @return {Object|undefined} Service object. 144 | */ 145 | function readService (node, objectStack) { 146 | return pushParseAndPop({}, SERVICE_PARSERS, node, objectStack); 147 | } 148 | 149 | 150 | /** 151 | * @param {Node} node Node. 152 | * @param {Array.<*>} objectStack Object stack. 153 | * @private 154 | * @return {Object|undefined} Contact information object. 155 | */ 156 | function readContactInformation (node, objectStack) { 157 | return pushParseAndPop({}, CONTACT_INFORMATION_PARSERS, 158 | node, objectStack); 159 | } 160 | 161 | 162 | /** 163 | * @param {Node} node Node. 164 | * @param {Array.<*>} objectStack Object stack. 165 | * @private 166 | * @return {Object|undefined} Contact person object. 167 | */ 168 | function readContactPersonPrimary (node, objectStack) { 169 | return pushParseAndPop({}, CONTACT_PERSON_PARSERS, 170 | node, objectStack); 171 | } 172 | 173 | 174 | /** 175 | * @param {Node} node Node. 176 | * @param {Array.<*>} objectStack Object stack. 177 | * @private 178 | * @return {Object|undefined} Contact address object. 179 | */ 180 | function readContactAddress (node, objectStack) { 181 | return pushParseAndPop({}, CONTACT_ADDRESS_PARSERS, 182 | node, objectStack); 183 | } 184 | 185 | 186 | /** 187 | * @param {Node} node Node. 188 | * @param {Array.<*>} objectStack Object stack. 189 | * @private 190 | * @return {Array.|undefined} Format array. 191 | */ 192 | function readException (node, objectStack) { 193 | return pushParseAndPop( 194 | [], EXCEPTION_PARSERS, node, objectStack); 195 | } 196 | 197 | 198 | /** 199 | * @param {Node} node Node. 200 | * @param {Array.<*>} objectStack Object stack. 201 | * @private 202 | * @return {Object|undefined} Layer object. 203 | */ 204 | function readCapabilityLayer (node, objectStack) { 205 | const queryable = readBooleanString(node.getAttribute('queryable')); 206 | return pushParseAndPop({ 207 | queryable: isDef(queryable) ? queryable : false }, 208 | LAYER_PARSERS, node, objectStack); 209 | } 210 | 211 | 212 | /** 213 | * @private 214 | * @param {Node} node Node. 215 | * @param {Array.<*>} objectStack Object stack. 216 | * @return {Object|undefined} Layer object. 217 | */ 218 | function readLayer (node, objectStack) { 219 | var parentLayerObject = /** @type {Object.} */ 220 | (objectStack[objectStack.length - 1]); 221 | 222 | const layerObject = /** @type {Object.} */ 223 | (pushParseAndPop({}, LAYER_PARSERS, 224 | node, objectStack)); 225 | 226 | if (!isDef(layerObject)) return undefined; 227 | 228 | let queryable = readBooleanString(node.getAttribute('queryable')); 229 | if (!isDef(queryable)) { 230 | queryable = parentLayerObject['queryable']; 231 | } 232 | layerObject['queryable'] = isDef(queryable) ? queryable : false; 233 | 234 | let cascaded = readNonNegativeIntegerString(node.getAttribute('cascaded')); 235 | if (!isDef(cascaded)) { 236 | cascaded = parentLayerObject['cascaded']; 237 | } 238 | layerObject['cascaded'] = cascaded; 239 | 240 | let opaque = readBooleanString(node.getAttribute('opaque')); 241 | if (!isDef(opaque)) { 242 | opaque = parentLayerObject['opaque']; 243 | } 244 | layerObject['opaque'] = isDef(opaque) ? opaque : false; 245 | 246 | let noSubsets = readBooleanString(node.getAttribute('noSubsets')); 247 | if (!isDef(noSubsets)) { 248 | noSubsets = parentLayerObject['noSubsets']; 249 | } 250 | layerObject['noSubsets'] = isDef(noSubsets) ? noSubsets : false; 251 | 252 | let fixedWidth = readDecimalString(node.getAttribute('fixedWidth')); 253 | if (!isDef(fixedWidth)) { 254 | fixedWidth = parentLayerObject['fixedWidth']; 255 | } 256 | layerObject['fixedWidth'] = fixedWidth; 257 | 258 | let fixedHeight = readDecimalString(node.getAttribute('fixedHeight')); 259 | if (!isDef(fixedHeight)) { 260 | fixedHeight = parentLayerObject['fixedHeight']; 261 | } 262 | layerObject['fixedHeight'] = fixedHeight; 263 | 264 | // See 7.2.4.8 265 | const addKeys = ['Style', 'CRS', 'AuthorityURL']; 266 | for (let i = 0, len = addKeys.length; i < len; i++) { 267 | const key = addKeys[i]; 268 | const parentValue = parentLayerObject[key]; 269 | if (isDef(parentValue)) { 270 | let childValue = setIfUndefined(layerObject, key, []); 271 | childValue = childValue.concat(parentValue); 272 | layerObject[key] = childValue; 273 | } 274 | } 275 | 276 | const replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension', 277 | 'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator' 278 | ]; 279 | for (let i = 0, len = replaceKeys.length; i < len; i++) { 280 | const key = replaceKeys[i]; 281 | const childValue = layerObject[key]; 282 | if (!isDef(childValue)) { 283 | const parentValue = parentLayerObject[key]; 284 | layerObject[key] = parentValue; 285 | } 286 | } 287 | 288 | return layerObject; 289 | } 290 | 291 | 292 | /** 293 | * @private 294 | * @param {Node} node Node. 295 | * @param {Array.<*>} objectStack Object stack. 296 | * @return {Object} Dimension object. 297 | */ 298 | function readDimension (node, objectStack) { 299 | return { 300 | 'name': node.getAttribute('name'), 301 | 'units': node.getAttribute('units'), 302 | 'unitSymbol': node.getAttribute('unitSymbol'), 303 | 'default': node.getAttribute('default'), 304 | 'multipleValues': readBooleanString(node.getAttribute('multipleValues')), 305 | 'nearestValue': readBooleanString(node.getAttribute('nearestValue')), 306 | 'current': readBooleanString(node.getAttribute('current')), 307 | 'values': readString(node) 308 | }; 309 | } 310 | 311 | 312 | /** 313 | * @private 314 | * @param {Node} node Node. 315 | * @param {Array.<*>} objectStack Object stack. 316 | * @return {Object|undefined} Online resource object. 317 | */ 318 | function readFormatOnlineresource (node, objectStack) { 319 | return pushParseAndPop({}, FORMAT_ONLINERESOURCE_PARSERS, 320 | node, objectStack); 321 | } 322 | 323 | 324 | /** 325 | * @private 326 | * @param {Node} node Node. 327 | * @param {Array.<*>} objectStack Object stack. 328 | * @return {Object|undefined} Request object. 329 | */ 330 | function readRequest (node, objectStack) { 331 | return pushParseAndPop({}, REQUEST_PARSERS, node, objectStack); 332 | } 333 | 334 | 335 | /** 336 | * @private 337 | * @param {Node} node Node. 338 | * @param {Array.<*>} objectStack Object stack. 339 | * @return {Object|undefined} DCP type object. 340 | */ 341 | function readDCPType (node, objectStack) { 342 | return pushParseAndPop({}, DCPTYPE_PARSERS, node, objectStack); 343 | } 344 | 345 | 346 | /** 347 | * @private 348 | * @param {Node} node Node. 349 | * @param {Array.<*>} objectStack Object stack. 350 | * @return {Object|undefined} HTTP object. 351 | */ 352 | function readHTTP (node, objectStack) { 353 | return pushParseAndPop({}, HTTP_PARSERS, node, objectStack); 354 | } 355 | 356 | 357 | /** 358 | * @private 359 | * @param {Node} node Node. 360 | * @param {Array.<*>} objectStack Object stack. 361 | * @return {Object|undefined} Operation type object. 362 | */ 363 | function readOperationType (node, objectStack) { 364 | return pushParseAndPop({}, OPERATIONTYPE_PARSERS, node, objectStack); 365 | } 366 | 367 | 368 | /** 369 | * @private 370 | * @param {Node} node Node. 371 | * @param {Array.<*>} objectStack Object stack. 372 | * @return {Object|undefined} Online resource object. 373 | */ 374 | function readSizedFormatOnlineresource (node, objectStack) { 375 | var formatOnlineresource = readFormatOnlineresource(node, objectStack); 376 | if (isDef(formatOnlineresource)) { 377 | const size = [ 378 | readNonNegativeIntegerString(node.getAttribute('width')), 379 | readNonNegativeIntegerString(node.getAttribute('height')) 380 | ]; 381 | formatOnlineresource['size'] = size; 382 | return formatOnlineresource; 383 | } 384 | return undefined; 385 | } 386 | 387 | 388 | /** 389 | * @private 390 | * @param {Node} node Node. 391 | * @param {Array.<*>} objectStack Object stack. 392 | * @return {Object|undefined} Authority URL object. 393 | */ 394 | function readAuthorityURL (node, objectStack) { 395 | var authorityObject = readFormatOnlineresource(node, objectStack); 396 | if (isDef(authorityObject)) { 397 | authorityObject['name'] = node.getAttribute('name'); 398 | return authorityObject; 399 | } 400 | return undefined; 401 | } 402 | 403 | 404 | /** 405 | * @private 406 | * @param {Node} node Node. 407 | * @param {Array.<*>} objectStack Object stack. 408 | * @return {Object|undefined} Metadata URL object. 409 | */ 410 | function readMetadataURL (node, objectStack) { 411 | var metadataObject = readFormatOnlineresource(node, objectStack); 412 | if (isDef(metadataObject)) { 413 | metadataObject['type'] = node.getAttribute('type'); 414 | return metadataObject; 415 | } 416 | return undefined; 417 | } 418 | 419 | 420 | /** 421 | * @private 422 | * @param {Node} node Node. 423 | * @param {Array.<*>} objectStack Object stack. 424 | * @return {Object|undefined} Style object. 425 | */ 426 | function readStyle (node, objectStack) { 427 | return pushParseAndPop({}, STYLE_PARSERS, node, objectStack); 428 | } 429 | 430 | 431 | /** 432 | * @private 433 | * @param {Node} node Node. 434 | * @param {Array.<*>} objectStack Object stack. 435 | * @return {Array.|undefined} Keyword list. 436 | */ 437 | function readKeywordList (node, objectStack) { 438 | return pushParseAndPop( 439 | [], KEYWORDLIST_PARSERS, node, objectStack); 440 | } 441 | 442 | 443 | /** 444 | * @const 445 | * @type {Array.} 446 | */ 447 | const NAMESPACE_URIS = [ 448 | null, 449 | 'http://www.opengis.net/wms' 450 | ]; 451 | 452 | /** 453 | * @const 454 | * @type {Object.>} 455 | * @private 456 | */ 457 | export const PARSERS = makeParsersNS( 458 | NAMESPACE_URIS, { 459 | 'Service': makeObjectPropertySetter(readService), 460 | 'Capability': makeObjectPropertySetter(readCapability) 461 | }); 462 | 463 | /** 464 | * @const 465 | * @type {Object.>} 466 | * @private 467 | */ 468 | const CAPABILITY_PARSERS = makeParsersNS( 469 | NAMESPACE_URIS, { 470 | 'Request': makeObjectPropertySetter(readRequest), 471 | 'Exception': makeObjectPropertySetter(readException), 472 | 'Layer': makeObjectPropertySetter(readCapabilityLayer) 473 | }); 474 | 475 | /** 476 | * @const 477 | * @type {Object.>} 478 | * @private 479 | */ 480 | const SERVICE_PARSERS = makeParsersNS( 481 | NAMESPACE_URIS, { 482 | 'Name': makeObjectPropertySetter(readString), 483 | 'Title': makeObjectPropertySetter(readString), 484 | 'Abstract': makeObjectPropertySetter(readString), 485 | 'KeywordList': makeObjectPropertySetter(readKeywordList), 486 | 'OnlineResource': makeObjectPropertySetter(readHref), 487 | 'ContactInformation': makeObjectPropertySetter(readContactInformation), 488 | 'Fees': makeObjectPropertySetter(readString), 489 | 'AccessConstraints': makeObjectPropertySetter(readString), 490 | 'LayerLimit': makeObjectPropertySetter(readNonNegativeInteger), 491 | 'MaxWidth': makeObjectPropertySetter(readNonNegativeInteger), 492 | 'MaxHeight': makeObjectPropertySetter(readNonNegativeInteger) 493 | }); 494 | 495 | /** 496 | * @const 497 | * @type {Object.>} 498 | * @private 499 | */ 500 | const CONTACT_INFORMATION_PARSERS = makeParsersNS( 501 | NAMESPACE_URIS, { 502 | 'ContactPersonPrimary': makeObjectPropertySetter(readContactPersonPrimary), 503 | 'ContactPosition': makeObjectPropertySetter(readString), 504 | 'ContactAddress': makeObjectPropertySetter(readContactAddress), 505 | 'ContactVoiceTelephone': makeObjectPropertySetter(readString), 506 | 'ContactFacsimileTelephone': makeObjectPropertySetter(readString), 507 | 'ContactElectronicMailAddress': makeObjectPropertySetter(readString) 508 | }); 509 | 510 | /** 511 | * @const 512 | * @type {Object.>} 513 | * @private 514 | */ 515 | const CONTACT_PERSON_PARSERS = makeParsersNS( 516 | NAMESPACE_URIS, { 517 | 'ContactPerson': makeObjectPropertySetter(readString), 518 | 'ContactOrganization': makeObjectPropertySetter(readString) 519 | }); 520 | 521 | 522 | /** 523 | * @const 524 | * @type {Object.>} 525 | * @private 526 | */ 527 | const CONTACT_ADDRESS_PARSERS = makeParsersNS( 528 | NAMESPACE_URIS, { 529 | 'AddressType': makeObjectPropertySetter(readString), 530 | 'Address': makeObjectPropertySetter(readString), 531 | 'City': makeObjectPropertySetter(readString), 532 | 'StateOrProvince': makeObjectPropertySetter(readString), 533 | 'PostCode': makeObjectPropertySetter(readString), 534 | 'Country': makeObjectPropertySetter(readString) 535 | }); 536 | 537 | 538 | /** 539 | * @const 540 | * @type {Object.>} 541 | * @private 542 | */ 543 | const EXCEPTION_PARSERS = makeParsersNS( 544 | NAMESPACE_URIS, { 545 | 'Format': makeArrayPusher(readString) 546 | }); 547 | 548 | 549 | /** 550 | * @const 551 | * @type {Object.>} 552 | * @private 553 | */ 554 | const LAYER_PARSERS = makeParsersNS( 555 | NAMESPACE_URIS, { 556 | 'Name': makeObjectPropertySetter(readString), 557 | 'Title': makeObjectPropertySetter(readString), 558 | 'Abstract': makeObjectPropertySetter(readString), 559 | 'KeywordList': makeObjectPropertySetter(readKeywordList), 560 | 'CRS': makeObjectPropertyPusher(readString), 561 | 'SRS': makeObjectPropertyPusher(readString), 562 | 'EX_GeographicBoundingBox': makeObjectPropertySetter(readEXGeographicBoundingBox), 563 | 'LatLonBoundingBox': makeObjectPropertySetter(readLatLonBoundingBox), 564 | 'BoundingBox': makeObjectPropertyPusher(readBoundingBox), 565 | 'Dimension': makeObjectPropertyPusher(readDimension), 566 | 'Attribution': makeObjectPropertySetter(readAttribution), 567 | 'AuthorityURL': makeObjectPropertyPusher(readAuthorityURL), 568 | 'Identifier': makeObjectPropertyPusher(readString), 569 | 'MetadataURL': makeObjectPropertyPusher(readMetadataURL), 570 | 'DataURL': makeObjectPropertyPusher(readFormatOnlineresource), 571 | 'FeatureListURL': makeObjectPropertyPusher(readFormatOnlineresource), 572 | 'Style': makeObjectPropertyPusher(readStyle), 573 | 'MinScaleDenominator': makeObjectPropertySetter(readDecimal), 574 | 'MaxScaleDenominator': makeObjectPropertySetter(readDecimal), 575 | 'ScaleHint': makeObjectPropertySetter(readScaleHint), 576 | 'Layer': makeObjectPropertyPusher(readLayer) 577 | }); 578 | 579 | 580 | /** 581 | * @const 582 | * @type {Object.>} 583 | * @private 584 | */ 585 | const ATTRIBUTION_PARSERS = makeParsersNS( 586 | NAMESPACE_URIS, { 587 | 'Title': makeObjectPropertySetter(readString), 588 | 'OnlineResource': makeObjectPropertySetter(readHref), 589 | 'LogoURL': makeObjectPropertySetter(readSizedFormatOnlineresource) 590 | }); 591 | 592 | 593 | /** 594 | * @const 595 | * @type {Object.>} 596 | * @private 597 | */ 598 | const EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS = 599 | makeParsersNS(NAMESPACE_URIS, { 600 | 'westBoundLongitude': makeObjectPropertySetter(readDecimal), 601 | 'eastBoundLongitude': makeObjectPropertySetter(readDecimal), 602 | 'southBoundLatitude': makeObjectPropertySetter(readDecimal), 603 | 'northBoundLatitude': makeObjectPropertySetter(readDecimal) 604 | }); 605 | 606 | 607 | /** 608 | * @const 609 | * @type {Object.>} 610 | * @private 611 | */ 612 | const REQUEST_PARSERS = makeParsersNS( 613 | NAMESPACE_URIS, { 614 | 'GetCapabilities': makeObjectPropertySetter( 615 | readOperationType), 616 | 'GetMap': makeObjectPropertySetter( 617 | readOperationType), 618 | 'GetFeatureInfo': makeObjectPropertySetter( 619 | readOperationType) 620 | }); 621 | 622 | 623 | /** 624 | * @const 625 | * @type {Object.>} 626 | * @private 627 | */ 628 | const OPERATIONTYPE_PARSERS = makeParsersNS( 629 | NAMESPACE_URIS, { 630 | 'Format': makeObjectPropertyPusher(readString), 631 | 'DCPType': makeObjectPropertyPusher( 632 | readDCPType) 633 | }); 634 | 635 | 636 | /** 637 | * @const 638 | * @type {Object.>} 639 | * @private 640 | */ 641 | const DCPTYPE_PARSERS = makeParsersNS( 642 | NAMESPACE_URIS, { 643 | 'HTTP': makeObjectPropertySetter( 644 | readHTTP) 645 | }); 646 | 647 | 648 | /** 649 | * @const 650 | * @type {Object.>} 651 | * @private 652 | */ 653 | const HTTP_PARSERS = makeParsersNS( 654 | NAMESPACE_URIS, { 655 | 'Get': makeObjectPropertySetter( 656 | readFormatOnlineresource), 657 | 'Post': makeObjectPropertySetter( 658 | readFormatOnlineresource) 659 | }); 660 | 661 | 662 | /** 663 | * @const 664 | * @type {Object.>} 665 | * @private 666 | */ 667 | const STYLE_PARSERS = makeParsersNS( 668 | NAMESPACE_URIS, { 669 | 'Name': makeObjectPropertySetter(readString), 670 | 'Title': makeObjectPropertySetter(readString), 671 | 'Abstract': makeObjectPropertySetter(readString), 672 | 'LegendURL': makeObjectPropertyPusher(readSizedFormatOnlineresource), 673 | 'StyleSheetURL': makeObjectPropertySetter(readFormatOnlineresource), 674 | 'StyleURL': makeObjectPropertySetter(readFormatOnlineresource) 675 | }); 676 | 677 | 678 | /** 679 | * @const 680 | * @type {Object.>} 681 | * @private 682 | */ 683 | const FORMAT_ONLINERESOURCE_PARSERS = makeParsersNS( 684 | NAMESPACE_URIS, { 685 | 'Format': makeObjectPropertySetter(readString), 686 | 'OnlineResource': makeObjectPropertySetter(readHref) 687 | }); 688 | 689 | 690 | /** 691 | * @const 692 | * @type {Object.>} 693 | * @private 694 | */ 695 | const KEYWORDLIST_PARSERS = makeParsersNS( 696 | NAMESPACE_URIS, { 697 | 'Keyword': makeArrayPusher(readString) 698 | }); 699 | -------------------------------------------------------------------------------- /src/utils/isdef.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns true if the specified value is not undefined. 3 | * 4 | * @param {?} val Variable to test. 5 | * @return {Boolean} Whether variable is defined. 6 | */ 7 | export default (val) => val !== void 0; 8 | 9 | -------------------------------------------------------------------------------- /src/utils/setifundefined.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds a key-value pair to the object/map/hash if it doesn't exist yet. 3 | * 4 | * @param {Object.} obj The object to which to add the key-value pair. 5 | * @param {String} key The key to add. 6 | * @param {V} value The value to add if the key wasn't present. 7 | * @return {V} The value of the entry at the end of the function. 8 | * @template K,V 9 | */ 10 | export default (obj, key, value) => key in obj ? obj[key] : (obj[key] = value); 11 | -------------------------------------------------------------------------------- /src/utils/string.js: -------------------------------------------------------------------------------- 1 | import isDef from './isdef'; 2 | 3 | /** 4 | * Make sure we trim BOM and NBSP 5 | * @type {RegExp} 6 | */ 7 | const TRIM_RE = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; 8 | 9 | /** 10 | * Repeats a string n times. 11 | * @param {String} string The string to repeat. 12 | * @param {Number} length The number of times to repeat. 13 | * @return {String} A string containing {@code length} repetitions of 14 | * {@code string}. 15 | */ 16 | function repeat(string, length) { 17 | return new Array(length + 1).join(string); 18 | } 19 | 20 | /** 21 | * @param {String} str 22 | * @return {String} 23 | */ 24 | export function trim (str) { 25 | return str.replace(TRIM_RE, ''); 26 | } 27 | 28 | /** 29 | * Pads number to given length and optionally rounds it to a given precision. 30 | * For example: 31 | *
padNumber(1.25, 2, 3) -> '01.250'
32 |  * padNumber(1.25, 2) -> '01.25'
33 |  * padNumber(1.25, 2, 1) -> '01.3'
34 |  * padNumber(1.25, 0) -> '1.25'
35 | * 36 | * @param {Number} num The number to pad. 37 | * @param {Number} length The desired length. 38 | * @param {Number=} opt_precision The desired precision. 39 | * @return {String} {@code num} as a string with the given options. 40 | */ 41 | export function padNumber (num, length, opt_precision) { 42 | var s = isDef(opt_precision) ? num.toFixed(opt_precision) : String(num); 43 | var index = s.indexOf('.'); 44 | if (index == -1) { 45 | index = s.length; 46 | } 47 | return repeat('0', Math.max(0, length - index)) + s; 48 | } 49 | -------------------------------------------------------------------------------- /src/xlink.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @const 3 | * @type {string} 4 | */ 5 | const NAMESPACE_URI = 'http://www.w3.org/1999/xlink'; 6 | 7 | /** 8 | * @param {Node} node Node. 9 | * @return {Boolean|undefined} Boolean. 10 | */ 11 | export function readHref (node) { 12 | return node.getAttributeNS(NAMESPACE_URI, 'href'); 13 | } 14 | -------------------------------------------------------------------------------- /src/xml_parser.js: -------------------------------------------------------------------------------- 1 | import isDef from './utils/isdef'; 2 | import setIfUndefined from'./utils/setifundefined'; 3 | import nodeTypes from './node_types'; 4 | 5 | export default class XMLParser { 6 | /** 7 | * XML DOM parser 8 | * @constructor 9 | * @param {DOMParser} DOMParser 10 | */ 11 | constructor (DOMParser) { 12 | /** 13 | * @type {DOMParser} 14 | */ 15 | this._parser = new DOMParser(); 16 | }; 17 | 18 | /** 19 | * @param {String} xmlstring 20 | * @return {Document} 21 | */ 22 | toDocument (xmlstring) { 23 | return this._parser.parseFromString(xmlstring, 'application/xml'); 24 | } 25 | 26 | /** 27 | * Recursively grab all text content of child nodes into a single string. 28 | * @param {Node} node Node. 29 | * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line 30 | * breaks. 31 | * @return {string} All text content. 32 | * @api 33 | */ 34 | getAllTextContent (node, normalizeWhitespace) { 35 | return getAllTextContent(node, normalizeWhitespace, []).join(''); 36 | } 37 | } 38 | 39 | 40 | /** 41 | * Recursively grab all text content of child nodes into a single string. 42 | * @param {Node} node Node. 43 | * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line 44 | * breaks. 45 | * @return {string} All text content. 46 | * @api 47 | */ 48 | export function getAllTextContent (node, normalizeWhitespace) { 49 | return getAllTextContentInternal(node, normalizeWhitespace, []).join(''); 50 | } 51 | 52 | 53 | /** 54 | * @param {Node} node Node. 55 | * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line 56 | * breaks. 57 | * @param {Array.} accumulator Accumulator. 58 | * @private 59 | * @return {Array.} Accumulator. 60 | */ 61 | export function getAllTextContentInternal (node, normalizeWhitespace, accumulator) { 62 | if (node.nodeType === nodeTypes.CDATA_SECTION || 63 | node.nodeType === nodeTypes.TEXT) { 64 | if (normalizeWhitespace) { 65 | // FIXME understand why goog.dom.getTextContent_ uses String here 66 | accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, '')); 67 | } else { 68 | accumulator.push(node.nodeValue); 69 | } 70 | } else { 71 | var n; 72 | for (n = node.firstChild; n; n = n.nextSibling) { 73 | getAllTextContentInternal(n, normalizeWhitespace, accumulator); 74 | } 75 | } 76 | return accumulator; 77 | } 78 | 79 | /** 80 | * @param {Object.>} parsersNS 81 | * Parsers by namespace. 82 | * @param {Node} node Node. 83 | * @param {Array.<*>} objectStack Object stack. 84 | * @param {*=} bind The object to use as `this`. 85 | */ 86 | export function parseNode (parsersNS, node, objectStack, bind) { 87 | for (var n = firstElementChild(node); n; n = nextElementSibling(n)) { 88 | var namespaceURI = n.namespaceURI || null; 89 | var parsers = parsersNS[namespaceURI]; 90 | if (isDef(parsers)) { 91 | var parser = parsers[n.localName]; 92 | if (isDef(parser)) { 93 | parser.call(bind, n, objectStack); 94 | } 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Mostly for node.js 101 | * @param {Node} node 102 | * @return {Node} 103 | */ 104 | export function firstElementChild (node) { 105 | let firstElementChild = node.firstElementChild || node.firstChild; 106 | while (firstElementChild && firstElementChild.nodeType !== nodeTypes.ELEMENT) { 107 | firstElementChild = firstElementChild.nextSibling; 108 | } 109 | return firstElementChild; 110 | } 111 | 112 | /** 113 | * Mostly for node.js 114 | * @param {Node} node 115 | * @return {Node} 116 | */ 117 | function nextElementSibling (node) { 118 | let nextSibling = node.nextElementSibling || node.nextSibling; 119 | while (nextSibling && nextSibling.nodeType !== nodeTypes.ELEMENT) { 120 | nextSibling = nextSibling.nextSibling; 121 | } 122 | return nextSibling; 123 | } 124 | 125 | /** 126 | * @param {Array.} namespaceURIs Namespace URIs. 127 | * @param {Object.} parsers Parsers. 128 | * @param {Object.>=} opt_parsersNS 129 | * ParsersNS. 130 | * @return {Object.>} Parsers NS. 131 | */ 132 | export function makeParsersNS (namespaceURIs, parsers, opt_parsersNS) { 133 | return /** @type {Object.>} */ ( 134 | makeStructureNS(namespaceURIs, parsers, opt_parsersNS)); 135 | } 136 | 137 | /** 138 | * Creates a namespaced structure, using the same values for each namespace. 139 | * This can be used as a starting point for versioned parsers, when only a few 140 | * values are version specific. 141 | * @param {Array.} namespaceURIs Namespace URIs. 142 | * @param {T} structure Structure. 143 | * @param {Object.=} opt_structureNS Namespaced structure to add to. 144 | * @return {Object.} Namespaced structure. 145 | * @template T 146 | */ 147 | export function makeStructureNS (namespaceURIs, structure, opt_structureNS) { 148 | /** 149 | * @type {Object.} 150 | */ 151 | var structureNS = isDef(opt_structureNS) ? opt_structureNS : {}; 152 | var i, ii; 153 | for (i = 0, ii = namespaceURIs.length; i < ii; ++i) { 154 | structureNS[namespaceURIs[i]] = structure; 155 | } 156 | return structureNS; 157 | } 158 | 159 | 160 | /** 161 | * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. 162 | * @param {T=} opt_this The object to use as `this` in `valueReader`. 163 | * @return {Function} Parser. 164 | * @template T 165 | */ 166 | export function makeArrayPusher (valueReader, opt_this) { 167 | return ( 168 | /** 169 | * @param {Node} node Node. 170 | * @param {Array.<*>} objectStack Object stack. 171 | */ 172 | function(node, objectStack) { 173 | var value = valueReader.call(isDef(opt_this) ? opt_this : this, 174 | node, objectStack); 175 | if (isDef(value)) { 176 | var array = objectStack[objectStack.length - 1]; 177 | array.push(value); 178 | } 179 | }); 180 | } 181 | 182 | /** 183 | * @param {Object} object Object. 184 | * @param {Object.>} parsersNS Parsers by namespace. 185 | * @param {Node} node Node. 186 | * @param {Array.<*>} objectStack Object stack. 187 | * @param {*=} bind The object to use as `this`. 188 | * @return {Object|undefined} Object. 189 | */ 190 | export function pushParseAndPop (object, parsersNS, node, objectStack, bind) { 191 | objectStack.push(object); 192 | parseNode(parsersNS, node, objectStack, bind); 193 | return objectStack.pop(); 194 | } 195 | 196 | 197 | /** 198 | * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. 199 | * @param {string=} opt_property Property. 200 | * @param {T=} opt_this The object to use as `this` in `valueReader`. 201 | * @return {XMLParser.Parser} Parser. 202 | * @template T 203 | */ 204 | export function makeObjectPropertySetter (valueReader, opt_property, opt_this) { 205 | return ( 206 | /** 207 | * @param {Node} node Node. 208 | * @param {Array.<*>} objectStack Object stack. 209 | */ 210 | function(node, objectStack) { 211 | let value = valueReader.call(isDef(opt_this) ? opt_this : this, 212 | node, objectStack); 213 | if (isDef(value)) { 214 | var object = /** @type {Object} */ (objectStack[objectStack.length - 1]); 215 | var property = isDef(opt_property) ? opt_property : node.localName; 216 | object[property] = value; 217 | } 218 | }); 219 | } 220 | 221 | 222 | /** 223 | * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. 224 | * @param {string=} opt_property Property. 225 | * @param {T=} opt_this The object to use as `this` in `valueReader`. 226 | * @return {Function} Parser. 227 | * @template T 228 | */ 229 | export function makeObjectPropertyPusher (valueReader, opt_property, opt_this) { 230 | return ( 231 | /** 232 | * @param {Node} node Node. 233 | * @param {Array.<*>} objectStack Object stack. 234 | */ 235 | function(node, objectStack) { 236 | var value = valueReader.call(isDef(opt_this) ? opt_this : this, 237 | node, objectStack); 238 | 239 | if (isDef(value)) { 240 | var object = /** @type {Object} */ (objectStack[objectStack.length - 1]); 241 | var property = isDef(opt_property) ? opt_property : node.localName; 242 | var array = setIfUndefined(object, property, []); 243 | array.push(value); 244 | } 245 | }); 246 | } 247 | -------------------------------------------------------------------------------- /src/xsd.js: -------------------------------------------------------------------------------- 1 | import isDef from './utils/isdef'; 2 | import { padNumber, trim } from './utils/string'; 3 | import XMLParser, { getAllTextContent } from './xml_parser'; 4 | 5 | /** 6 | * @const 7 | * @type {string} 8 | */ 9 | export const NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema'; 10 | 11 | /** 12 | * @param {Node} node Node. 13 | * @return {boolean|undefined} Boolean. 14 | */ 15 | export function readBoolean (node) { 16 | const s = getAllTextContent(node, false); 17 | return readBooleanString(s); 18 | } 19 | 20 | /** 21 | * @param {string} string String. 22 | * @return {boolean|undefined} Boolean. 23 | */ 24 | export function readBooleanString (string) { 25 | const m = /^\s*(true|1)|(false|0)\s*$/.exec(string); 26 | if (m) { 27 | return isDef(m[1]) || false; 28 | } else { 29 | return undefined; 30 | } 31 | } 32 | 33 | 34 | /** 35 | * @param {Node} node Node. 36 | * @return {number|undefined} DateTime in seconds. 37 | */ 38 | export function readDateTime (node) { 39 | const s = getAllTextContent(node, false); 40 | const re = /^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?))\s*$/; 41 | const m = re.exec(s); 42 | if (m) { 43 | const year = parseInt(m[1], 10); 44 | const month = parseInt(m[2], 10) - 1; 45 | const day = parseInt(m[3], 10); 46 | const hour = parseInt(m[4], 10); 47 | const minute = parseInt(m[5], 10); 48 | const second = parseInt(m[6], 10); 49 | let dateTime = Date.UTC(year, month, day, hour, minute, second) / 1000; 50 | if (m[7] != 'Z') { 51 | const sign = m[8] == '-' ? -1 : 1; 52 | dateTime += sign * 60 * parseInt(m[9], 10); 53 | if (isDef(m[10])) { 54 | dateTime += sign * 60 * 60 * parseInt(m[10], 10); 55 | } 56 | } 57 | return dateTime; 58 | } else { 59 | return undefined; 60 | } 61 | } 62 | 63 | 64 | /** 65 | * @param {Node} node Node. 66 | * @return {number|undefined} Decimal. 67 | */ 68 | export function readDecimal (node) { 69 | return readDecimalString(getAllTextContent(node, false)); 70 | } 71 | 72 | 73 | /** 74 | * @param {string} string String. 75 | * @return {number|undefined} Decimal. 76 | */ 77 | export function readDecimalString (string) { 78 | // FIXME check spec 79 | const m = /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(string); 80 | if (m) { 81 | return parseFloat(m[1]); 82 | } else { 83 | return undefined; 84 | } 85 | } 86 | 87 | 88 | /** 89 | * @param {Node} node Node. 90 | * @return {number|undefined} Non negative integer. 91 | */ 92 | export function readNonNegativeInteger (node) { 93 | return readNonNegativeIntegerString(getAllTextContent(node, false)); 94 | } 95 | 96 | 97 | /** 98 | * @param {string} string String. 99 | * @return {number|undefined} Non negative integer. 100 | */ 101 | export function readNonNegativeIntegerString (string) { 102 | const m = /^\s*(\d+)\s*$/.exec(string); 103 | if (m) { 104 | return parseInt(m[1], 10); 105 | } else { 106 | return undefined; 107 | } 108 | } 109 | 110 | 111 | /** 112 | * @param {Node} node Node. 113 | * @return {string|undefined} String. 114 | */ 115 | export function readString (node) { 116 | return trim(getAllTextContent(node, false)); 117 | } 118 | 119 | 120 | /** 121 | * @param {Node} node Node to append a TextNode with the boolean to. 122 | * @param {boolean} bool Boolean. 123 | */ 124 | export function writeBooleanTextNode (node, bool) { 125 | writeStringTextNode(node, (bool) ? '1' : '0'); 126 | } 127 | 128 | 129 | /** 130 | * @param {Node} node Node to append a TextNode with the dateTime to. 131 | * @param {number} dateTime DateTime in seconds. 132 | */ 133 | export function writeDateTimeTextNode (node, dateTime) { 134 | const date = new Date(dateTime * 1000); 135 | const string = date.getUTCFullYear() + '-' + 136 | padNumber(date.getUTCMonth() + 1, 2) + '-' + 137 | padNumber(date.getUTCDate(), 2) + 'T' + 138 | padNumber(date.getUTCHours(), 2) + ':' + 139 | padNumber(date.getUTCMinutes(), 2) + ':' + 140 | padNumber(date.getUTCSeconds(), 2) + 'Z'; 141 | node.appendChild(XMLParser.DOCUMENT.createTextNode(string)); 142 | }; 143 | 144 | 145 | /** 146 | * @param {Node} node Node to append a TextNode with the decimal to. 147 | * @param {number} decimal Decimal. 148 | */ 149 | export function writeDecimalTextNode (node, decimal) { 150 | const string = decimal.toPrecision(); 151 | node.appendChild(XMLParser.DOCUMENT.createTextNode(string)); 152 | } 153 | 154 | 155 | /** 156 | * @param {Node} node Node to append a TextNode with the decimal to. 157 | * @param {number} nonNegativeInteger Non negative integer. 158 | */ 159 | export function writeNonNegativeIntegerTextNode (node, nonNegativeInteger) { 160 | const string = nonNegativeInteger.toString(); 161 | node.appendChild(XMLParser.DOCUMENT.createTextNode(string)); 162 | } 163 | 164 | 165 | /** 166 | * @param {Node} node Node to append a TextNode with the string to. 167 | * @param {string} string String. 168 | */ 169 | export function writeStringTextNode (node, string) { 170 | node.appendChild(XMLParser.DOCUMENT.createTextNode(string)); 171 | } 172 | -------------------------------------------------------------------------------- /test/fixtures/analyses.xml: -------------------------------------------------------------------------------- 1 | OGC:WMSnowCOAST 'analyses' WMSThe nowCOAST 'analyses' Web Map Service (WMS) provides layers containing various NOAA gridded weather and oceanographic surface analysis products. The 'analyses' WMS is one of several map services provided by NOAA's nowCOAST project (http://nowcoast.noaa.gov). For further layer information such as layer icons, legend graphics, layer descriptions, and valid timestamps, see the nowCOAST 'LayerInfo Web Service' description page at: http://nowcoast.noaa.gov/help/layerinfo.shtml. For more information about nowCOAST's available Web Map Services, see the nowCOAST Map Services information page at: http://nowcoast.noaa.gov/help/mapservices.shtml. nowCOAST is a product of the NOAA/NOS/OCS Coast Survey Development Laboratory.nowCOASTNOAANOSNWSOceanSeaMarineWeatherMeteorologicalOceanographicRiverAirAtmosphereAnalysisJohn G.W. KelleyNOAA/NOS/CSDLProject Managerpostal
DurhamNH03824USA
nowcoast.team@noaa.gov
nonenone
application/vnd.ogc.wms_xmlimage/pngimage/jpegimage/gifapplication/vnd.ogc.wms_xmltext/xmltext/htmltext/plainapplication/vnd.ogc.se_xmlapplication/vnd.ogc.se_inimageapplication/vnd.ogc.se_blankanalysesEPSG:3857EPSG:4326EPSG:4267EPSG:4269EPSG:102100EPSG:102113NCEP_RAS_ANAL_RTG_SSTSea Surface Temperature Analysis - RasterEPSG:4326NCEP_POLY_ANAL_RTG_SSTSea Surface Temperature Analysis - PolygonEPSG:4326world_countries_labelMap Background - World Countries LabelsEPSG:4326world_countriesMap Background - World CountriesEPSG:4326world_riversMap Background - World RiversEPSG:4326world_lakesMap Background - World LakesEPSG:4326great_lakesMap Background - Great LakesEPSG:4326us_states_genMap Background - US States - GeneralEPSG:4326us_canada_backMap Background - US/Canada BackgroundEPSG:4326provincesMap Background - Canadian ProvincesEPSG:4326countiesMap Background - US County BordersEPSG:4326artccMap Background - Air Route Traffic Ctrl Ctr BoundariesEPSG:4326riversMap Background - RiversEPSG:4326lakesMap Background - LakesEPSG:4326watershedsMap Background - WatershedsEPSG:4326coastal_labelsMap Background - Coastal LabelsEPSG:4326RTMA_RAS_AIRTEMPRTMA Air Temperature - RasterEPSG:4326RTMA_POLY_AIRTEMPRTMA Air Temperature - PolygonEPSG:4326RTMA_RAS_WSPDRTMA Wind Speed - RasterEPSG:4326RTMA_POLY_WSPDRTMA Wind Speed - PolygonEPSG:4326NSSL_RAS_QPE_72HR72-hour AccumulationEPSG:4326NSSL_RAS_QPE_48HR48-hour AccumulationEPSG:4326NSSL_RAS_QPE_24HR24-hour AccumulationEPSG:4326NSSL_RAS_QPE_12HR12-hour AccumulationEPSG:4326NSSL_RAS_QPE_6HR6-hour AccumulationEPSG:4326NSSL_RAS_QPE_3HR3-hour AccumulationEPSG:4326NSSL_RAS_QPE_1HR1-hour AccumulationEPSG:4326RTMA_RAS_QPE_1HR1-hour AccumulationEPSG:4326RTMA_POLY_WINDRTMA Wind Barb - PolygonEPSG:4326RTMA_PT_WIND_15RTMA Wind Barb - 15ptEPSG:4326RTMA_PT_WIND_10RTMA Wind Barb - 10ptEPSG:4326RTMA_PT_WIND_05RTMA Wind Barb - 5ptEPSG:4326RTMA_PT_WIND_01RTMA Wind Barb - 1ptEPSG:4326RTMA_POLY_WINDVECTRTMA Wind Vectors - PolygonEPSG:4326RTMA_PT_WINDVECT_15RTMA Wind Vectors - 15ptEPSG:4326RTMA_PT_WINDVECT_10RTMA Wind Vectors - 10ptEPSG:4326RTMA_PT_WINDVECT_05RTMA Wind Vectors - 5ptEPSG:4326RTMA_PT_WINDVECT_01RTMA Wind Vectors - 1ptEPSG:4326urban_areas_fillMap Background - Urban Areas FillEPSG:4326urban_areas_outlineMap Background - Urban Areas OutlineEPSG:4326major_roadsMap Background - Major RoadsEPSG:4326us_interstatesMap Background - US InterstatesEPSG:4326us_runwaysMap Background - Airport Runway FootprintsEPSG:4326us_citiesMap Background - US CitiesEPSG:4326tc_warn_breakpointsMap Background - Coastal Watch/Warning BreakpointsEPSG:4326marine_sanctuariesMap Background - National Marine SanctuariesEPSG:4326eezMap Background - Exclusive Economic ZoneEPSG:4326lat_halfdegMap Background - Latitude Grid Lines - 1/2 degEPSG:4326lon_halfdegMap Background - Longitude Grid Lines - 1/2 degEPSG:4326lat_onedegMap Background - Latitude Grid Lines - 1 degEPSG:4326lon_onedegMap Background - Longitude Grid Lines - 1 degEPSG:4326lat_fivedegMap Background - Latitude Grid Lines - 5 degEPSG:4326lon_fivedegMap Background - Longitude Grid Lines - 5 degEPSG:4326
2 | -------------------------------------------------------------------------------- /test/fixtures/forecasts.xml: -------------------------------------------------------------------------------- 1 | OGC:WMSnowCOAST 'forecasts' WMSThe nowCOAST 'forecasts' Web Map Service (WMS) provides layers containing gridded weather and oceanographic surface forecast products from the NOAA/NWS National Digital Forecast Database (NDFD). The 'forecasts' WMS is one of several map services provided by NOAA's nowCOAST project (http://nowcoast.noaa.gov). For further layer information such as layer icons, legend graphics, layer descriptions, and valid timestamps, see the nowCOAST 'LayerInfo Web Service' description page at: http://nowcoast.noaa.gov/help/layerinfo.shtml. For more information about nowCOAST's available Web Map Services, see the nowCOAST Map Services information page at: http://nowcoast.noaa.gov/help/mapservices.shtml. nowCOAST is a product of the NOAA/NOS/OCS Coast Survey Development Laboratory.nowCOASTNOAANOSNWSOceanSeaMarineWeatherMeteorologicalOceanographicRiverAirAtmosphereNDFDForecastWaveWindTemperatureHumidityPrecipitationJohn G.W. KelleyNOAA/NOS/CSDLProject Managerpostal
DurhamNH03824USA
nowcoast.team@noaa.gov
nonenone
application/vnd.ogc.wms_xmlimage/pngimage/jpegimage/gifapplication/vnd.ogc.wms_xmltext/xmltext/htmltext/plainapplication/vnd.ogc.se_xmlapplication/vnd.ogc.se_inimageapplication/vnd.ogc.se_blankforecastsEPSG:3857EPSG:4326EPSG:4267EPSG:4269EPSG:102100EPSG:102113world_countries_labelMap Background - World Countries LabelsEPSG:4326world_countriesMap Background - World CountriesEPSG:4326world_riversMap Background - World RiversEPSG:4326world_lakesMap Background - World LakesEPSG:4326great_lakesMap Background - Great LakesEPSG:4326us_states_genMap Background - US States - GeneralEPSG:4326us_canada_backMap Background - US/Canada BackgroundEPSG:4326provincesMap Background - Canadian ProvincesEPSG:4326countiesMap Background - US County BordersEPSG:4326artccMap Background - Air Route Traffic Ctrl Ctr BoundariesEPSG:4326riversMap Background - RiversEPSG:4326lakesMap Background - LakesEPSG:4326watershedsMap Background - WatershedsEPSG:4326coastal_labelsMap Background - Coastal LabelsEPSG:4326NDFD_POLY_WAVEH_3_00NDFD Significant Wave Height 3 Day Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_3_00NDFD Significant Wave Height 3 Day Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_2_12NDFD Significant Wave Height 2 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_2_12NDFD Significant Wave Height 2 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_2_00NDFD Significant Wave Height 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_2_00NDFD Significant Wave Height 2 Day Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_1_12NDFD Significant Wave Height 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_1_12NDFD Significant Wave Height 1 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_1_00NDFD Significant Wave Height 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_1_00NDFD Significant Wave Height 1 Day Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_0_12NDFD Significant Wave Height 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_0_12NDFD Significant Wave Height 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WAVEH_0_00NDFD Significant Wave Height 0 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WAVEH_0_00NDFD Significant Wave Height 0 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_3_00NDFD Wind Speed 3 Day Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_3_00NDFD Wind Speed 3 Day Fcst - RasterEPSG:4326NDFD_POLY_WSPD_2_18NDFD Wind Speed 2 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_2_18NDFD Wind Speed 2 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_2_12NDFD Wind Speed 2 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_2_12NDFD Wind Speed 2 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_2_06NDFD Wind Speed 2 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_2_06NDFD Wind Speed 2 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_2_00NDFD Wind Speed 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_2_00NDFD Wind Speed 2 Day Fcst - RasterEPSG:4326NDFD_POLY_WSPD_1_18NDFD Wind Speed 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_1_18NDFD Wind Speed 1 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_1_12NDFD Wind Speed 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_1_12NDFD Wind Speed 1 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_1_06NDFD Wind Speed 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_1_06NDFD Wind Speed 1 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_1_00NDFD Wind Speed 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_1_00NDFD Wind Speed 1 Day Fcst - RasterEPSG:4326NDFD_POLY_WSPD_0_18NDFD Wind Speed 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_0_18NDFD Wind Speed 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_0_12NDFD Wind Speed 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_0_12NDFD Wind Speed 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_0_06NDFD Wind Speed 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_0_06NDFD Wind Speed 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_WSPD_0_00NDFD Wind Speed 0 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WSPD_0_00NDFD Wind Speed 0 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_2_00NDFD Wind Gust 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_2_00NDFD Wind Gust 2 Day Fcst - RasterEPSG:4326NDFD_POLY_WGUST_1_18NDFD Wind Gust 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_1_18NDFD Wind Gust 1 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_1_12NDFD Wind Gust 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_1_12NDFD Wind Gust 1 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_1_06NDFD Wind Gust 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_1_06NDFD Wind Gust 1 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_1_00NDFD Wind Gust 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_1_00NDFD Wind Gust 1 Day Fcst - RasterEPSG:4326NDFD_POLY_WGUST_0_18NDFD Wind Gust 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_0_18NDFD Wind Gust 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_0_12NDFD Wind Gust 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_0_12NDFD Wind Gust 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_0_06NDFD Wind Gust 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_0_06NDFD Wind Gust 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_WGUST_0_00NDFD Wind Gust 0 Hr Fcst - PolygonEPSG:4326NDFD_RAS_WGUST_0_00NDFD Wind Gust 0 Hr Fcst - RasterEPSG:4326NDFD_POLY_MINT_3_00NDFD Minimum Surface Air Temperature 3 Day Fcst - PolygonEPSG:4326NDFD_RAS_MINT_3_00NDFD Minimum Surface Air Temperature 3 Day Fcst - RasterEPSG:4326NDFD_POLY_MINT_2_00NDFD Minimum Surface Air Temperature 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_MINT_2_00NDFD Minimum Surface Air Temperature 2 Day Fcst - RasterEPSG:4326NDFD_POLY_MINT_1_00NDFD Minimum Surface Air Temperature 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_MINT_1_00NDFD Minimum Surface Air Temperature 1 Day Fcst - RasterEPSG:4326NDFD_POLY_MINT_0_00NDFD Minimum Surface Air Temperature 0 Day Fcst - PolygonEPSG:4326NDFD_RAS_MINT_0_00NDFD Minimum Surface Air Temperature 0 Day Fcst - RasterEPSG:4326NDFD_POLY_MAXT_3_00NDFD Maximum Surface Air Temperature 3 Day Fcst - PolygonEPSG:4326NDFD_RAS_MAXT_3_00NDFD Maximum Surface Air Temperature 3 Day Fcst - RasterEPSG:4326NDFD_POLY_MAXT_2_00NDFD Maximum Surface Air Temperature 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_MAXT_2_00NDFD Maximum Surface Air Temperature 2 Day Fcst - RasterEPSG:4326NDFD_POLY_MAXT_1_00NDFD Maximum Surface Air Temperature 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_MAXT_1_00NDFD Maximum Surface Air Temperature 1 Day Fcst - RasterEPSG:4326NDFD_POLY_MAXT_0_00NDFD Maximum Surface Air Temperature 0 Day Fcst - PolygonEPSG:4326NDFD_RAS_MAXT_0_00NDFD Maximum Surface Air Temperature 0 Day Fcst - RasterEPSG:4326NDFD_POLY_RHM_3_00NDFD Relative Humidity 3 Day Fcst - PolygonEPSG:4326NDFD_RAS_RHM_3_00NDFD Relative Humidity 3 Day Fcst - RasterEPSG:4326NDFD_POLY_RHM_2_18NDFD Relative Humidity 2 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_2_18NDFD Relative Humidity 2 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_2_12NDFD Relative Humidity 2 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_2_12NDFD Relative Humidity 2 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_2_06NDFD Relative Humidity 2 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_2_06NDFD Relative Humidity 2 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_2_00NDFD Relative Humidity 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_RHM_2_00NDFD Relative Humidity 2 Day Fcst - RasterEPSG:4326NDFD_POLY_RHM_1_18NDFD Relative Humidity 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_1_18NDFD Relative Humidity 1 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_1_12NDFD Relative Humidity 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_1_12NDFD Relative Humidity 1 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_1_06NDFD Relative Humidity 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_1_06NDFD Relative Humidity 1 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_1_00NDFD Relative Humidity 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_RHM_1_00NDFD Relative Humidity 1 Day Fcst - RasterEPSG:4326NDFD_POLY_RHM_0_18NDFD Relative Humidity 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_0_18NDFD Relative Humidity 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_0_12NDFD Relative Humidity 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_0_12NDFD Relative Humidity 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_0_06NDFD Relative Humidity 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_0_06NDFD Relative Humidity 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_RHM_0_00NDFD Relative Humidity 0 Hr Fcst - PolygonEPSG:4326NDFD_RAS_RHM_0_00NDFD Relative Humidity 0 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_2_00NDFD Quantitative Precipitation 2 Day Fcst - PolygonEPSG:4326NDFD_RAS_QPF_2_00NDFD Quantitative Precipitation 2 Day Fcst - RasterEPSG:4326NDFD_POLY_QPF_1_18NDFD Quantitative Precipitation 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_1_18NDFD Quantitative Precipitation 1 Day 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_1_12NDFD Quantitative Precipitation 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_1_12NDFD Quantitative Precipitation 1 Day 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_1_06NDFD Quantitative Precipitation 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_1_06NDFD Quantitative Precipitation 1 Day 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_1_00NDFD Quantitative Precipitation 1 Day Fcst - PolygonEPSG:4326NDFD_RAS_QPF_1_00NDFD Quantitative Precipitation 1 Day Fcst - RasterEPSG:4326NDFD_POLY_QPF_0_18NDFD Quantitative Precipitation 18 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_0_18NDFD Quantitative Precipitation 18 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_0_12NDFD Quantitative Precipitation 12 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_0_12NDFD Quantitative Precipitation 12 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_0_06NDFD Quantitative Precipitation 6 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_0_06NDFD Quantitative Precipitation 6 Hr Fcst - RasterEPSG:4326NDFD_POLY_QPF_0_00NDFD Quantitative Precipitation 0 Hr Fcst - PolygonEPSG:4326NDFD_RAS_QPF_0_00NDFD Quantitative Precipitation 0 Hr Fcst - RasterEPSG:4326NDFD_POLY_WINDVECT_3_00NDFD Wind Vector 3 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_3_00_G15NDFD Wind Vector 3 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_3_00_G10NDFD Wind Vector 3 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_3_00_G05NDFD Wind Vector 3 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_3_00_G01NDFD Wind Vector 3 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_2_18NDFD Wind Vector 2 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_2_18_G15NDFD Wind Vector 2 Day 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_2_18_G10NDFD Wind Vector 2 Day 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_2_18_G05NDFD Wind Vector 2 Day 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_2_18_G01NDFD Wind Vector 2 Day 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_2_12NDFD Wind Vector 2 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_2_12_G15NDFD Wind Vector 2 Day 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_2_12_G10NDFD Wind Vector 2 Day 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_2_12_G05NDFD Wind Vector 2 Day 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_2_12_G01NDFD Wind Vector 2 Day 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_2_06NDFD Wind Vector 2 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_2_06_G15NDFD Wind Vector 2 Day 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_2_06_G10NDFD Wind Vector 2 Day 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_2_06_G05NDFD Wind Vector 2 Day 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_2_06_G01NDFD Wind Vector 2 Day 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_2_00NDFD Wind Vector 2 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_2_00_G15NDFD Wind Vector 2 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_2_00_G10NDFD Wind Vector 2 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_2_00_G05NDFD Wind Vector 2 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_2_00_G01NDFD Wind Vector 2 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_1_18NDFD Wind Vector 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_1_18_G15NDFD Wind Vector 1 Day 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_1_18_G10NDFD Wind Vector 1 Day 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_1_18_G05NDFD Wind Vector 1 Day 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_1_18_G01NDFD Wind Vector 1 Day 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_1_12NDFD Wind Vector 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_1_12_G15NDFD Wind Vector 1 Day 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_1_12_G10NDFD Wind Vector 1 Day 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_1_12_G05NDFD Wind Vector 1 Day 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_1_12_G01NDFD Wind Vector 1 Day 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_1_06NDFD Wind Vector 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_1_06_G15NDFD Wind Vector 1 Day 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_1_06_G10NDFD Wind Vector 1 Day 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_1_06_G05NDFD Wind Vector 1 Day 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_1_06_G01NDFD Wind Vector 1 Day 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_1_00NDFD Wind Vector 1 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_1_00_G15NDFD Wind Vector 1 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_1_00_G10NDFD Wind Vector 1 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_1_00_G05NDFD Wind Vector 1 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_1_00_G01NDFD Wind Vector 1 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_0_18NDFD Wind Vector 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_0_18_G15NDFD Wind Vector 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_0_18_G10NDFD Wind Vector 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_0_18_G05NDFD Wind Vector 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_0_18_G01NDFD Wind Vector 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_0_12NDFD Wind Vector 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_0_12_G15NDFD Wind Vector 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_0_12_G10NDFD Wind Vector 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_0_12_G05NDFD Wind Vector 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_0_12_G01NDFD Wind Vector 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_0_06NDFD Wind Vector 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_0_06_G15NDFD Wind Vector 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_0_06_G10NDFD Wind Vector 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_0_06_G05NDFD Wind Vector 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_0_06_G01NDFD Wind Vector 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WINDVECT_0_00NDFD Wind Vector 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WINDVECT_0_00_G15NDFD Wind Vector 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WINDVECT_0_00_G10NDFD Wind Vector 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WINDVECT_0_00_G05NDFD Wind Vector 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WINDVECT_0_00_G01NDFD Wind Vector 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_3_00NDFD Wind Barb 3 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_3_00_G15NDFD Wind Barb 3 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_3_00_G10NDFD Wind Barb 3 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_3_00_G05NDFD Wind Barb 3 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_3_00_G01NDFD Wind Barb 3 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_2_18NDFD Wind Barb 2 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_2_18_G15NDFD Wind Barb 2 Day 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_2_18_G10NDFD Wind Barb 2 Day 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_2_18_G05NDFD Wind Barb 2 Day 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_2_18_G01NDFD Wind Barb 2 Day 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_2_12NDFD Wind Barb 2 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_2_12_G15NDFD Wind Barb 2 Day 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_2_12_G10NDFD Wind Barb 2 Day 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_2_12_G05NDFD Wind Barb 2 Day 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_2_12_G01NDFD Wind Barb 2 Day 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_2_06NDFD Wind Barb 2 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_2_06_G15NDFD Wind Barb 2 Day 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_2_06_G10NDFD Wind Barb 2 Day 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_2_06_G05NDFD Wind Barb 2 Day 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_2_06_G01NDFD Wind Barb 2 Day 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_2_00NDFD Wind Barb 2 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_2_00_G15NDFD Wind Barb 2 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_2_00_G10NDFD Wind Barb 2 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_2_00_G05NDFD Wind Barb 2 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_2_00_G01NDFD Wind Barb 2 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_1_18NDFD Wind Barb 1 Day 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_1_18_G15NDFD Wind Barb 1 Day 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_1_18_G10NDFD Wind Barb 1 Day 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_1_18_G05NDFD Wind Barb 1 Day 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_1_18_G01NDFD Wind Barb 1 Day 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_1_12NDFD Wind Barb 1 Day 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_1_12_G15NDFD Wind Barb 1 Day 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_1_12_G10NDFD Wind Barb 1 Day 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_1_12_G05NDFD Wind Barb 1 Day 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_1_12_G01NDFD Wind Barb 1 Day 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_1_06NDFD Wind Barb 1 Day 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_1_06_G15NDFD Wind Barb 1 Day 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_1_06_G10NDFD Wind Barb 1 Day 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_1_06_G05NDFD Wind Barb 1 Day 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_1_06_G01NDFD Wind Barb 1 Day 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_1_00NDFD Wind Barb 1 Day 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_1_00_G15NDFD Wind Barb 1 Day 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_1_00_G10NDFD Wind Barb 1 Day 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_1_00_G05NDFD Wind Barb 1 Day 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_1_00_G01NDFD Wind Barb 1 Day 0 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_0_18NDFD Wind Barb 18 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_0_18_G15NDFD Wind Barb 18 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_0_18_G10NDFD Wind Barb 18 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_0_18_G05NDFD Wind Barb 18 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_0_18_G01NDFD Wind Barb 18 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_0_12NDFD Wind Barb 12 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_0_12_G15NDFD Wind Barb 12 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_0_12_G10NDFD Wind Barb 12 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_0_12_G05NDFD Wind Barb 12 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_0_12_G01NDFD Wind Barb 12 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_0_06NDFD Wind Barb 6 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_0_06_G15NDFD Wind Barb 6 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_0_06_G10NDFD Wind Barb 6 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_0_06_G05NDFD Wind Barb 6 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_0_06_G01NDFD Wind Barb 6 Hr Fcst - 1ptEPSG:4326NDFD_POLY_WIND_0_00NDFD Wind Barb 0 Hr Fcst - PolygonEPSG:4326NDFD_PT_WIND_0_00_G15NDFD Wind Barb 0 Hr Fcst - 15ptEPSG:4326NDFD_PT_WIND_0_00_G10NDFD Wind Barb 0 Hr Fcst - 10ptEPSG:4326NDFD_PT_WIND_0_00_G05NDFD Wind Barb 0 Hr Fcst - 5ptEPSG:4326NDFD_PT_WIND_0_00_G01NDFD Wind Barb 0 Hr Fcst - 1ptEPSG:4326urban_areas_fillMap Background - Urban Areas FillEPSG:4326urban_areas_outlineMap Background - Urban Areas OutlineEPSG:4326major_roadsMap Background - Major RoadsEPSG:4326us_interstatesMap Background - US InterstatesEPSG:4326us_runwaysMap Background - Airport Runway FootprintsEPSG:4326us_citiesMap Background - US CitiesEPSG:4326tc_warn_breakpointsMap Background - Coastal Watch/Warning BreakpointsEPSG:4326marine_sanctuariesMap Background - National Marine SanctuariesEPSG:4326eezMap Background - Exclusive Economic ZoneEPSG:4326lat_halfdegMap Background - Latitude Grid Lines - 1/2 degEPSG:4326lon_halfdegMap Background - Longitude Grid Lines - 1/2 degEPSG:4326lat_onedegMap Background - Latitude Grid Lines - 1 degEPSG:4326lon_onedegMap Background - Longitude Grid Lines - 1 degEPSG:4326lat_fivedegMap Background - Latitude Grid Lines - 5 degEPSG:4326lon_fivedegMap Background - Longitude Grid Lines - 5 degEPSG:4326
2 | -------------------------------------------------------------------------------- /test/fixtures/obs.xml: -------------------------------------------------------------------------------- 1 | OGC:WMSnowCOAST 'obs' WMSThe nowCOAST 'obs' Web Map Service (WMS) provides layers containing near real-time surface meteorological and oceanographic observations from various sources. Some observations are updated as frequently as every six minutes, while others are updated hourly. The 'obs' WMS is one of several map services provided by NOAA's nowCOAST project (http://nowcoast.noaa.gov). For further layer information such as layer icons, legend graphics, layer descriptions, and valid timestamps, see the nowCOAST 'LayerInfo Web Service' description page at: http://nowcoast.noaa.gov/help/layerinfo.shtml. For more information about nowCOAST's available Web Map Services, see the nowCOAST Map Services information page at: http://nowcoast.noaa.gov/help/mapservices.shtml. nowCOAST is a product of the NOAA/NOS/OCS Coast Survey Development Laboratory.nowCOASTNOAANOSNWSOceanSeaMarineWeatherMeteorologicalOceanographicRiverAirAtmosphereobservationsobsMADISNESDISImageryRadarRidgeSatelliteCloudWindTemperatureVisibilitySSTWaveJohn G.W. KelleyNOAA/NOS/CSDLProject Managerpostal
DurhamNH03824USA
nowcoast.team@noaa.gov
nonenone
application/vnd.ogc.wms_xmlimage/pngimage/jpegimage/gifapplication/vnd.ogc.wms_xmltext/xmltext/htmltext/plainapplication/vnd.ogc.se_xmlapplication/vnd.ogc.se_inimageapplication/vnd.ogc.se_blankobsEPSG:3857EPSG:4326EPSG:4267EPSG:4269EPSG:102100EPSG:102113world_countries_labelMap Background - World Countries LabelsEPSG:4326world_countriesMap Background - World CountriesEPSG:4326world_riversMap Background - World RiversEPSG:4326world_lakesMap Background - World LakesEPSG:4326great_lakesMap Background - Great LakesEPSG:4326us_states_genMap Background - US States - GeneralEPSG:4326us_canada_backMap Background - US/Canada BackgroundEPSG:4326provincesMap Background - Canadian ProvincesEPSG:4326countiesMap Background - US County BordersEPSG:4326artccMap Background - Air Route Traffic Ctrl Ctr BoundariesEPSG:4326riversMap Background - RiversEPSG:4326lakesMap Background - LakesEPSG:4326watershedsMap Background - WatershedsEPSG:4326coastal_labelsMap Background - Coastal LabelsEPSG:4326RAS_GOES_I4Cloud Imagery (GOES Infrared)EPSG:4326RAS_GOESCloud Imagery (GOES Visible)EPSG:4326RAS_RIDGE_NEXRADWeather Radar MosaicEPSG:4326urban_areas_fillMap Background - Urban Areas FillEPSG:4326urban_areas_outlineMap Background - Urban Areas OutlineEPSG:4326major_roadsMap Background - Major RoadsEPSG:4326us_interstatesMap Background - US InterstatesEPSG:4326us_runwaysMap Background - Airport Runway FootprintsEPSG:4326us_citiesMap Background - US CitiesEPSG:4326tc_warn_breakpointsMap Background - Coastal Watch/Warning BreakpointsEPSG:4326marine_sanctuariesMap Background - National Marine SanctuariesEPSG:4326eezMap Background - Exclusive Economic ZoneEPSG:4326lat_halfdegMap Background - Latitude Grid Lines - 1/2 degEPSG:4326lon_halfdegMap Background - Longitude Grid Lines - 1/2 degEPSG:4326lat_onedegMap Background - Latitude Grid Lines - 1 degEPSG:4326lon_onedegMap Background - Longitude Grid Lines - 1 degEPSG:4326lat_fivedegMap Background - Latitude Grid Lines - 5 degEPSG:4326lon_fivedegMap Background - Longitude Grid Lines - 5 degEPSG:4326OBS_MAR_SWHFTSignificant Wave Height (feet)EPSG:4326OBS_MAR_SSTFSea Surface Temp (deg F)EPSG:4326OBS_MET_VISVisibility (miles)EPSG:4326OBS_MET_PRESMean Sea Level Pressure (mb)EPSG:4326OBS_MET_WINDWind (knots)EPSG:4326OBS_MET_DPDew Point Temp (deg F)EPSG:4326OBS_MET_TEMPAir Temp (deg F)EPSG:4326OBS_MET_IDStation IDEPSG:4326
2 | -------------------------------------------------------------------------------- /test/fixtures/wwa.xml: -------------------------------------------------------------------------------- 1 | OGC:WMSnowCOAST 'wwa' WMSThe nowCOAST 'wwa' Web Map Service (WMS) provides layers containing near real-time watches, warnings and advisories from the National Weather Service (NWS). These layers include the latest Tropical Cyclone Track Forecast from the NWS National Hurricane Center (NHC), updated hourly, as well as NWS short-duration warnings (Tornado, Extreme Wind, Severe Thunderstorm, Flood, Flash Flood, and Special Marine Warnings), updated each minute. The The 'wwa' WMS is one of several map services provided by NOAA's nowCOAST project (http://nowcoast.noaa.gov). For further layer information such as layer icons, legend graphics, layer descriptions, and valid timestamps, see the nowCOAST 'LayerInfo Web Service' description page at: http://nowcoast.noaa.gov/help/layerinfo.shtml. For more information about nowCOAST's available Web Map Services, see the nowCOAST Map Services information page at: http://nowcoast.noaa.gov/help/mapservices.shtml. nowCOAST is a product of the NOAA/NOS/OCS Coast Survey Development Laboratory.nowCOASTNOAANOSNWSOceanSeaMarineWeatherMeteorologicalOceanographicRiverAirAtmospherewwaWatchWarningAdvisoryTropicalCycloneHurricaneForecastNHCJohn G.W. KelleyNOAA/NOS/CSDLProject Managerpostal
DurhamNH03824USA
nowcoast.team@noaa.gov
nonenone
application/vnd.ogc.wms_xmlimage/pngimage/jpegimage/gifapplication/vnd.ogc.wms_xmltext/xmltext/htmltext/plainapplication/vnd.ogc.se_xmlapplication/vnd.ogc.se_inimageapplication/vnd.ogc.se_blankwwaEPSG:3857EPSG:4326EPSG:4267EPSG:4269EPSG:102100EPSG:102113world_countries_labelMap Background - World Countries LabelsEPSG:4326world_countriesMap Background - World CountriesEPSG:4326world_riversMap Background - World RiversEPSG:4326world_lakesMap Background - World LakesEPSG:4326great_lakesMap Background - Great LakesEPSG:4326us_states_genMap Background - US States - GeneralEPSG:4326us_canada_backMap Background - US/Canada BackgroundEPSG:4326provincesMap Background - Canadian ProvincesEPSG:4326countiesMap Background - US County BordersEPSG:4326artccMap Background - Air Route Traffic Ctrl Ctr BoundariesEPSG:4326riversMap Background - RiversEPSG:4326lakesMap Background - LakesEPSG:4326watershedsMap Background - WatershedsEPSG:4326coastal_labelsMap Background - Coastal LabelsEPSG:4326WARN_SHORT_TORTornado WarningsEPSG:4326WARN_SHORT_EWWExtreme Wind WarningsEPSG:4326WARN_SHORT_SVRSevere Thunderstorm WarningsEPSG:4326WARN_SHORT_FLWFlood WarningsEPSG:4326WARN_SHORT_FFWFlash Flood WarningsEPSG:4326WARN_SHORT_SMWSpecial Marine WarningsEPSG:4326NHC_TRACK_WWLINTrop. Cyclone Watches & WarningsEPSG:4326NHC_TRACK_LINTropical Cyclone Track LinesEPSG:4326NHC_TRACK_PTTropical Cyclone Track PointsEPSG:4326NHC_TRACK_POLYCone of UncertaintyEPSG:4326NHC_TRACK_PT_120DATEStorm Valid Times - 120hrEPSG:4326NHC_TRACK_PT_72DATEStorm Valid Times - 72hrEPSG:4326NHC_TRACK_PT_0NAMEDATEStorm Name & Valid Times - 0hrEPSG:4326NHC_TRACK_PT_120WLBLTropical Cyclone Track - 120hr Wind LabelsEPSG:4326NHC_TRACK_PT_72WLBLTropical Cyclone Track - 72hr Wind LabelsEPSG:4326NHC_TRACK_PT_72CATTropical Cyclone Track - 72hr Wind + CAT LabelsEPSG:4326NHC_TRACK_PT_120CATTropical Cyclone Track - 120hr Wind + CAT LabelsEPSG:4326NHC_TRACK_PT_MSLPLABELSTropical Cyclone Track - MSLP LabelsEPSG:4326urban_areas_fillMap Background - Urban Areas FillEPSG:4326urban_areas_outlineMap Background - Urban Areas OutlineEPSG:4326major_roadsMap Background - Major RoadsEPSG:4326us_interstatesMap Background - US InterstatesEPSG:4326us_runwaysMap Background - Airport Runway FootprintsEPSG:4326us_citiesMap Background - US CitiesEPSG:4326tc_warn_breakpointsMap Background - Coastal Watch/Warning BreakpointsEPSG:4326marine_sanctuariesMap Background - National Marine SanctuariesEPSG:4326eezMap Background - Exclusive Economic ZoneEPSG:4326lat_halfdegMap Background - Latitude Grid Lines - 1/2 degEPSG:4326lon_halfdegMap Background - Longitude Grid Lines - 1/2 degEPSG:4326lat_onedegMap Background - Latitude Grid Lines - 1 degEPSG:4326lon_onedegMap Background - Longitude Grid Lines - 1 degEPSG:4326lat_fivedegMap Background - Latitude Grid Lines - 5 degEPSG:4326lon_fivedegMap Background - Longitude Grid Lines - 5 degEPSG:4326
2 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import tape from 'tape'; 4 | import WMSCapabilities from '../dist/wms-capabilities'; 5 | import fs from 'fs'; 6 | import path from 'path'; 7 | 8 | 9 | import { DOMParser } from 'xmldom'; 10 | 11 | tape('WMSCapabilities', function(t) { 12 | 13 | t.test('forecasts.xml', function(t) { 14 | var url = path.join(process.cwd(), './test/fixtures/forecasts.xml'); 15 | var xml = fs.readFileSync(url, { 16 | encoding: 'utf-8' 17 | }); 18 | var json = new WMSCapabilities(undefined, DOMParser).parse(xml); 19 | 20 | t.ok(json, 'got result'); 21 | t.equal(typeof json, 'object', 'parsed'); 22 | t.equal(json.Capability.Layer.Layer[2].Name, "world_rivers", 'contents'); 23 | 24 | t.end(); 25 | }); 26 | 27 | t.test('obs.xml', function(t) { 28 | var url = path.join(process.cwd(), './test/fixtures/obs.xml'); 29 | var xml = fs.readFileSync(url, { 30 | encoding: 'utf-8' 31 | }); 32 | var json = new WMSCapabilities(xml, DOMParser).toJSON(); 33 | 34 | t.ok(json, 'got result'); 35 | t.equal(typeof json, 'object', 'parsed'); 36 | t.equal(json.Capability.Layer.Layer[2].Name, "world_rivers", 'contents'); 37 | t.end(); 38 | }); 39 | 40 | t.test('wwa.xml', function(t) { 41 | var url = path.join(process.cwd(), './test/fixtures/wwa.xml') 42 | var xml = fs.readFileSync(url, { 43 | encoding: 'utf-8' 44 | }); 45 | var json = new WMSCapabilities(xml, DOMParser).toJSON(); 46 | 47 | t.ok(json, 'got result'); 48 | t.equal(typeof json, 'object', 'parsed'); 49 | t.equal(json.Capability.Layer.Layer[2].Name, "world_rivers", 'contents'); 50 | 51 | t.end(); 52 | }); 53 | 54 | t.test('analyses.xml', function(t) { 55 | var url = path.join(process.cwd(), './test/fixtures/analyses.xml'); 56 | var xml = fs.readFileSync(url, { 57 | encoding: 'utf-8' 58 | }); 59 | var json = new WMSCapabilities(xml, DOMParser).toJSON(); 60 | 61 | t.ok(json, 'got result'); 62 | t.equal(typeof json, 'object', 'parsed'); 63 | t.equal(json.Capability.Layer.Layer[2].Name, "world_countries_label", 'contents'); 64 | 65 | t.end(); 66 | }); 67 | 68 | t.test('dmsp.xml', function(t) { 69 | var url = path.join(process.cwd(), './test/fixtures/dmsp.xml'); 70 | var xml = fs.readFileSync(url, { 71 | encoding: 'utf-8' 72 | }); 73 | var json = new WMSCapabilities(xml, DOMParser).toJSON(); 74 | 75 | t.ok(json, 'got result'); 76 | t.equal(typeof json, 'object', 'parsed'); 77 | t.equal(json.Capability.Layer.Layer[2].Name, "eez", 'contents'); 78 | 79 | t.end(); 80 | }); 81 | 82 | t.end(); 83 | }); 84 | --------------------------------------------------------------------------------