├── .deepsource.toml ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .husky └── pre-commit ├── CHANGELOG.md ├── CNAME ├── LICENSE ├── README.md ├── docs ├── .nojekyll ├── CNAME ├── assets │ ├── highlight.css │ ├── icons.css │ ├── icons.png │ ├── icons@2x.png │ ├── main.js │ ├── search.js │ ├── style.css │ ├── widgets.png │ └── widgets@2x.png ├── classes │ └── HintError.html ├── index.html ├── interfaces │ └── HintIssue.html └── modules.html ├── lib ├── array_is_numbers.ts ├── check_duplicate_keys.ts ├── enforce_bbox.ts ├── enforce_position.ts ├── enforce_position_array.ts ├── enforce_same_position.ts ├── errors.ts ├── forbid_confusing_properties.ts ├── get_array.ts ├── get_member_value.ts ├── get_object.ts ├── get_type.ts ├── index.ts └── types.ts ├── mise.toml ├── package.json ├── pnpm-lock.yaml ├── test ├── bad.test.ts ├── fixture │ ├── bad │ │ ├── bad-duplicate-properties.cli-output-json │ │ ├── bad-duplicate-properties.geojson │ │ ├── bad-featurelist.cli-output-json │ │ ├── bad-featurelist.geojson │ │ ├── bad-json.cli-output-json │ │ ├── bad-json.geojson │ │ ├── bad-object-type.cli-output-json │ │ ├── bad-object-type.geojson │ │ ├── bad-polygonloop.cli-output-json │ │ ├── bad-polygonloop.geojson │ │ ├── bad-properties-as-array.cli-output-json │ │ ├── bad-properties-as-array.geojson │ │ ├── badcoord.cli-output-json │ │ ├── badcoord.geojson │ │ ├── badcoordtype.cli-output-json │ │ ├── badcoordtype.geojson │ │ ├── badfeature-no-properties.geojson │ │ ├── badfeature.cli-output-json │ │ ├── badfeature.geojson │ │ ├── badfeatureid.cli-output-json │ │ ├── badfeatureid.geojson │ │ ├── bbox-4or6elements.geojson │ │ ├── bbox-contains-string.geojson │ │ ├── bbox-string.geojson │ │ ├── different-first-last.geojson │ │ ├── different-first-size.geojson │ │ ├── expected_object.geojson │ │ ├── false.geojson │ │ ├── feature_changed_semantics.geojson │ │ ├── featurecollection-obj.geojson │ │ ├── featurecollection-type-case.geojson │ │ ├── featurecollection.geojson │ │ ├── featurecollection_changed_semantics.geojson │ │ ├── geometry_changed_semantics.geojson │ │ ├── geometrycollection_nested.geojson │ │ ├── multiple_problems.geojson │ │ ├── multipoint-multidimension.geojson │ │ ├── multipoint-nocoordinates.geojson │ │ ├── multipoint-nondimension.geojson │ │ ├── nofeaturetype.geojson │ │ ├── notype.geojson │ │ ├── point-labeled-as-a-multipolygon.geojson │ │ ├── point-string.geojson │ │ ├── point-toofew.geojson │ │ ├── point-toomany.geojson │ │ ├── point.geojson │ │ ├── rootstring.geojson │ │ ├── short-line.geojson │ │ ├── short-linearring.geojson │ │ ├── short-multilinestring.geojson │ │ ├── stringcoord.geojson │ │ └── unknowntype.geojson │ └── good │ │ ├── good-feature-with-id.geojson │ │ ├── good-feature-with-string-id.geojson │ │ ├── good-feature.geojson │ │ ├── good-featurecollection-bbox.geojson │ │ ├── good-featurecollection-bbox3d.geojson │ │ ├── good-featurecollection-extensions.geojson │ │ ├── good-featurecollection.geojson │ │ ├── good-geometrycollection.geojson │ │ ├── good-linestring.geojson │ │ ├── good-multilinestring.geojson │ │ ├── good-multipoint.geojson │ │ ├── good-point-3d.geojson │ │ ├── good-point.geojson │ │ ├── good-polygon.geojson │ │ ├── multipolygon.geojson │ │ ├── nullgeometry.geojson │ │ └── nullproperties.geojson ├── good.test.ts └── scavenge.test.ts ├── tsconfig.json ├── typedoc.json └── types └── momoa.d.ts /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | test_patterns = [ 4 | "**.test.ts", 5 | "**.test.tsx" 6 | ] 7 | 8 | exclude_patterns = ["db/migrations/**"] 9 | 10 | [[analyzers]] 11 | name = "javascript" 12 | enabled = true 13 | 14 | [analyzers.meta] 15 | plugins = ["react"] -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 7 | parser: '@typescript-eslint/parser', 8 | parserOptions: { 9 | ecmaVersion: 13, 10 | sourceType: 'module', 11 | }, 12 | plugins: ['@typescript-eslint'], 13 | rules: {}, 14 | ignorePatterns: [ 15 | 'docs/*', 16 | 'dist/*.js', 17 | 'dist/*.ts', 18 | '.eslintrc.js', 19 | 'jest.config.js', 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-generated 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | test: 5 | uses: "placemark/workflows/.github/workflows/vitest.yml@v3" 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | coverage 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm test run 5 | pnpm lint 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [0.1.11](https://github.com/placemark/check-geojson/compare/v0.1.10...v0.1.11) (2022-04-28) 6 | 7 | ### [0.1.10](https://github.com/placemark/check-geojson/compare/v0.1.9...v0.1.10) (2022-04-13) 8 | 9 | 10 | ### Bug Fixes 11 | 12 | * correct module entry point ([455081a](https://github.com/placemark/check-geojson/commit/455081ae6cd482d873b348ecd7d1b5cffb1d20be)) 13 | 14 | ### [0.1.9](https://github.com/placemark/check-geojson/compare/v0.1.8...v0.1.9) (2022-03-19) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * Avoid downlevel iteration ([48dc9f3](https://github.com/placemark/check-geojson/commit/48dc9f3b9a6419eee8125390bf7905916a40f969)) 20 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | check-geojson.docs.placemark.io 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tom MacWright 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # check-geojson 2 | 3 | A spiritual successor to [geojsonhint](https://github.com/mapbox/geojsonhint), which is no longer maintained. 4 | 5 | check-geojson is a parser and validator for GeoJSON strings. It is tailored to the use cases of validating user-generated GeoJSON content, or troubleshooting GeoJSON that you've received. 6 | 7 | _Note: the API is not yet stable._ 8 | 9 | ## [📕 API Documentation](http://check-geojson.docs.placemark.io/) 10 | 11 | ### Main differences from geojsonhint 12 | 13 | - Actively maintained 14 | - Written in TypeScript and includes types 15 | - Uses [momoa](https://github.com/humanwhocodes/momoa) to parse JSON instead of a homemade 16 | parser. This is probably the biggest one. jsonlint-lines was a hack, which I created 17 | because I could not find a single parser that actually parsed JSON and gave line numbers 18 | for values. momoa is much better than that hack, and using it makes line-level errors 19 | much cleaner. 20 | 21 | Unlike geojsonhint, this checker _only_ produces errors, not warnings. So things 22 | that geojsonhint would warn about, like: 23 | 24 | - excessive coordinate precision 25 | - right-hand rule compliance 26 | 27 | This does not check for. Additionally, the `crs` member is ignored by this tool: as of 28 | the latest GeoJSON specification, this is [not used](https://datatracker.ietf.org/doc/html/rfc7946#appendix-B.1). 29 | 30 | We're using the same test fixtures as geojsonhint as a starter. 31 | 32 | ### Install 33 | 34 | ```shell 35 | pnpm add @placemarkio/check-geojson 36 | yarn add @placemarkio/check-geojson 37 | ``` 38 | 39 | ### Usage 40 | 41 | ```ts 42 | import { check } from "@placemarkio/check-geojson" 43 | 44 | let geojsonObject; 45 | try { 46 | geojsonObject = check('… geojson string …') 47 | } catch (e) { 48 | /// e.issues 49 | } 50 | ``` 51 | 52 | If your GeoJSON is already an object, you will need to convert it to a string first. geojson-check will re-parse it. You should consider the performance penalty of this. 53 | 54 | ```ts 55 | const issues = getIssues(JSON.stringify(geojsonObject)); 56 | if (issues.length > 0) { 57 | // ... 58 | } 59 | ``` 60 | 61 | --- 62 | 63 | [![Maintainability](https://api.codeclimate.com/v1/badges/0a1d1c0755b9e67406f1/maintainability)](https://codeclimate.com/repos/60a64f6aa449b13bc40002bd/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/0a1d1c0755b9e67406f1/test_coverage)](https://codeclimate.com/repos/60a64f6aa449b13bc40002bd/test_coverage) 64 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | check-geojson.docs.placemark.io 2 | -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #AF00DB; 3 | --dark-hl-0: #C586C0; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #001080; 7 | --dark-hl-2: #9CDCFE; 8 | --light-hl-3: #A31515; 9 | --dark-hl-3: #CE9178; 10 | --light-hl-4: #0000FF; 11 | --dark-hl-4: #569CD6; 12 | --light-hl-5: #0070C1; 13 | --dark-hl-5: #4FC1FF; 14 | --light-hl-6: #795E26; 15 | --dark-hl-6: #DCDCAA; 16 | --light-hl-7: #008000; 17 | --dark-hl-7: #6A9955; 18 | --light-code-background: #F5F5F5; 19 | --dark-code-background: #1E1E1E; 20 | } 21 | 22 | @media (prefers-color-scheme: light) { :root { 23 | --hl-0: var(--light-hl-0); 24 | --hl-1: var(--light-hl-1); 25 | --hl-2: var(--light-hl-2); 26 | --hl-3: var(--light-hl-3); 27 | --hl-4: var(--light-hl-4); 28 | --hl-5: var(--light-hl-5); 29 | --hl-6: var(--light-hl-6); 30 | --hl-7: var(--light-hl-7); 31 | --code-background: var(--light-code-background); 32 | } } 33 | 34 | @media (prefers-color-scheme: dark) { :root { 35 | --hl-0: var(--dark-hl-0); 36 | --hl-1: var(--dark-hl-1); 37 | --hl-2: var(--dark-hl-2); 38 | --hl-3: var(--dark-hl-3); 39 | --hl-4: var(--dark-hl-4); 40 | --hl-5: var(--dark-hl-5); 41 | --hl-6: var(--dark-hl-6); 42 | --hl-7: var(--dark-hl-7); 43 | --code-background: var(--dark-code-background); 44 | } } 45 | 46 | body.light { 47 | --hl-0: var(--light-hl-0); 48 | --hl-1: var(--light-hl-1); 49 | --hl-2: var(--light-hl-2); 50 | --hl-3: var(--light-hl-3); 51 | --hl-4: var(--light-hl-4); 52 | --hl-5: var(--light-hl-5); 53 | --hl-6: var(--light-hl-6); 54 | --hl-7: var(--light-hl-7); 55 | --code-background: var(--light-code-background); 56 | } 57 | 58 | body.dark { 59 | --hl-0: var(--dark-hl-0); 60 | --hl-1: var(--dark-hl-1); 61 | --hl-2: var(--dark-hl-2); 62 | --hl-3: var(--dark-hl-3); 63 | --hl-4: var(--dark-hl-4); 64 | --hl-5: var(--dark-hl-5); 65 | --hl-6: var(--dark-hl-6); 66 | --hl-7: var(--dark-hl-7); 67 | --code-background: var(--dark-code-background); 68 | } 69 | 70 | .hl-0 { color: var(--hl-0); } 71 | .hl-1 { color: var(--hl-1); } 72 | .hl-2 { color: var(--hl-2); } 73 | .hl-3 { color: var(--hl-3); } 74 | .hl-4 { color: var(--hl-4); } 75 | .hl-5 { color: var(--hl-5); } 76 | .hl-6 { color: var(--hl-6); } 77 | .hl-7 { color: var(--hl-7); } 78 | pre, code { background: var(--code-background); } 79 | -------------------------------------------------------------------------------- /docs/assets/icons.css: -------------------------------------------------------------------------------- 1 | .tsd-kind-icon { 2 | display: block; 3 | position: relative; 4 | padding-left: 20px; 5 | text-indent: -20px; 6 | } 7 | .tsd-kind-icon:before { 8 | content: ""; 9 | display: inline-block; 10 | vertical-align: middle; 11 | width: 17px; 12 | height: 17px; 13 | margin: 0 3px 2px 0; 14 | background-image: url(./icons.png); 15 | } 16 | @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { 17 | .tsd-kind-icon:before { 18 | background-image: url(./icons@2x.png); 19 | background-size: 238px 204px; 20 | } 21 | } 22 | 23 | .tsd-signature.tsd-kind-icon:before { 24 | background-position: 0 -153px; 25 | } 26 | 27 | .tsd-kind-object-literal > .tsd-kind-icon:before { 28 | background-position: 0px -17px; 29 | } 30 | .tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { 31 | background-position: -17px -17px; 32 | } 33 | .tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { 34 | background-position: -34px -17px; 35 | } 36 | 37 | .tsd-kind-class > .tsd-kind-icon:before { 38 | background-position: 0px -34px; 39 | } 40 | .tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { 41 | background-position: -17px -34px; 42 | } 43 | .tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { 44 | background-position: -34px -34px; 45 | } 46 | 47 | .tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { 48 | background-position: 0px -51px; 49 | } 50 | .tsd-kind-class.tsd-has-type-parameter.tsd-is-protected 51 | > .tsd-kind-icon:before { 52 | background-position: -17px -51px; 53 | } 54 | .tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { 55 | background-position: -34px -51px; 56 | } 57 | 58 | .tsd-kind-interface > .tsd-kind-icon:before { 59 | background-position: 0px -68px; 60 | } 61 | .tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { 62 | background-position: -17px -68px; 63 | } 64 | .tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { 65 | background-position: -34px -68px; 66 | } 67 | 68 | .tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { 69 | background-position: 0px -85px; 70 | } 71 | .tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected 72 | > .tsd-kind-icon:before { 73 | background-position: -17px -85px; 74 | } 75 | .tsd-kind-interface.tsd-has-type-parameter.tsd-is-private 76 | > .tsd-kind-icon:before { 77 | background-position: -34px -85px; 78 | } 79 | 80 | .tsd-kind-namespace > .tsd-kind-icon:before { 81 | background-position: 0px -102px; 82 | } 83 | .tsd-kind-namespace.tsd-is-protected > .tsd-kind-icon:before { 84 | background-position: -17px -102px; 85 | } 86 | .tsd-kind-namespace.tsd-is-private > .tsd-kind-icon:before { 87 | background-position: -34px -102px; 88 | } 89 | 90 | .tsd-kind-module > .tsd-kind-icon:before { 91 | background-position: 0px -102px; 92 | } 93 | .tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { 94 | background-position: -17px -102px; 95 | } 96 | .tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { 97 | background-position: -34px -102px; 98 | } 99 | 100 | .tsd-kind-enum > .tsd-kind-icon:before { 101 | background-position: 0px -119px; 102 | } 103 | .tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { 104 | background-position: -17px -119px; 105 | } 106 | .tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { 107 | background-position: -34px -119px; 108 | } 109 | 110 | .tsd-kind-enum-member > .tsd-kind-icon:before { 111 | background-position: 0px -136px; 112 | } 113 | .tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { 114 | background-position: -17px -136px; 115 | } 116 | .tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { 117 | background-position: -34px -136px; 118 | } 119 | 120 | .tsd-kind-signature > .tsd-kind-icon:before { 121 | background-position: 0px -153px; 122 | } 123 | .tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { 124 | background-position: -17px -153px; 125 | } 126 | .tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { 127 | background-position: -34px -153px; 128 | } 129 | 130 | .tsd-kind-type-alias > .tsd-kind-icon:before { 131 | background-position: 0px -170px; 132 | } 133 | .tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { 134 | background-position: -17px -170px; 135 | } 136 | .tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { 137 | background-position: -34px -170px; 138 | } 139 | 140 | .tsd-kind-type-alias.tsd-has-type-parameter > .tsd-kind-icon:before { 141 | background-position: 0px -187px; 142 | } 143 | .tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-protected 144 | > .tsd-kind-icon:before { 145 | background-position: -17px -187px; 146 | } 147 | .tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-private 148 | > .tsd-kind-icon:before { 149 | background-position: -34px -187px; 150 | } 151 | 152 | .tsd-kind-variable > .tsd-kind-icon:before { 153 | background-position: -136px -0px; 154 | } 155 | .tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { 156 | background-position: -153px -0px; 157 | } 158 | .tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { 159 | background-position: -119px -0px; 160 | } 161 | .tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { 162 | background-position: -51px -0px; 163 | } 164 | .tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited 165 | > .tsd-kind-icon:before { 166 | background-position: -68px -0px; 167 | } 168 | .tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected 169 | > .tsd-kind-icon:before { 170 | background-position: -85px -0px; 171 | } 172 | .tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 173 | > .tsd-kind-icon:before { 174 | background-position: -102px -0px; 175 | } 176 | .tsd-kind-variable.tsd-parent-kind-class.tsd-is-private 177 | > .tsd-kind-icon:before { 178 | background-position: -119px -0px; 179 | } 180 | .tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { 181 | background-position: -170px -0px; 182 | } 183 | .tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected 184 | > .tsd-kind-icon:before { 185 | background-position: -187px -0px; 186 | } 187 | .tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 188 | background-position: -119px -0px; 189 | } 190 | .tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { 191 | background-position: -204px -0px; 192 | } 193 | .tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited 194 | > .tsd-kind-icon:before { 195 | background-position: -221px -0px; 196 | } 197 | 198 | .tsd-kind-property > .tsd-kind-icon:before { 199 | background-position: -136px -0px; 200 | } 201 | .tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { 202 | background-position: -153px -0px; 203 | } 204 | .tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { 205 | background-position: -119px -0px; 206 | } 207 | .tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { 208 | background-position: -51px -0px; 209 | } 210 | .tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited 211 | > .tsd-kind-icon:before { 212 | background-position: -68px -0px; 213 | } 214 | .tsd-kind-property.tsd-parent-kind-class.tsd-is-protected 215 | > .tsd-kind-icon:before { 216 | background-position: -85px -0px; 217 | } 218 | .tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 219 | > .tsd-kind-icon:before { 220 | background-position: -102px -0px; 221 | } 222 | .tsd-kind-property.tsd-parent-kind-class.tsd-is-private 223 | > .tsd-kind-icon:before { 224 | background-position: -119px -0px; 225 | } 226 | .tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { 227 | background-position: -170px -0px; 228 | } 229 | .tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected 230 | > .tsd-kind-icon:before { 231 | background-position: -187px -0px; 232 | } 233 | .tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 234 | background-position: -119px -0px; 235 | } 236 | .tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { 237 | background-position: -204px -0px; 238 | } 239 | .tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited 240 | > .tsd-kind-icon:before { 241 | background-position: -221px -0px; 242 | } 243 | 244 | .tsd-kind-get-signature > .tsd-kind-icon:before { 245 | background-position: -136px -17px; 246 | } 247 | .tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { 248 | background-position: -153px -17px; 249 | } 250 | .tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { 251 | background-position: -119px -17px; 252 | } 253 | .tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { 254 | background-position: -51px -17px; 255 | } 256 | .tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited 257 | > .tsd-kind-icon:before { 258 | background-position: -68px -17px; 259 | } 260 | .tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected 261 | > .tsd-kind-icon:before { 262 | background-position: -85px -17px; 263 | } 264 | .tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 265 | > .tsd-kind-icon:before { 266 | background-position: -102px -17px; 267 | } 268 | .tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private 269 | > .tsd-kind-icon:before { 270 | background-position: -119px -17px; 271 | } 272 | .tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { 273 | background-position: -170px -17px; 274 | } 275 | .tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected 276 | > .tsd-kind-icon:before { 277 | background-position: -187px -17px; 278 | } 279 | .tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private 280 | > .tsd-kind-icon:before { 281 | background-position: -119px -17px; 282 | } 283 | .tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { 284 | background-position: -204px -17px; 285 | } 286 | .tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited 287 | > .tsd-kind-icon:before { 288 | background-position: -221px -17px; 289 | } 290 | 291 | .tsd-kind-set-signature > .tsd-kind-icon:before { 292 | background-position: -136px -34px; 293 | } 294 | .tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { 295 | background-position: -153px -34px; 296 | } 297 | .tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { 298 | background-position: -119px -34px; 299 | } 300 | .tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { 301 | background-position: -51px -34px; 302 | } 303 | .tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited 304 | > .tsd-kind-icon:before { 305 | background-position: -68px -34px; 306 | } 307 | .tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected 308 | > .tsd-kind-icon:before { 309 | background-position: -85px -34px; 310 | } 311 | .tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 312 | > .tsd-kind-icon:before { 313 | background-position: -102px -34px; 314 | } 315 | .tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private 316 | > .tsd-kind-icon:before { 317 | background-position: -119px -34px; 318 | } 319 | .tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { 320 | background-position: -170px -34px; 321 | } 322 | .tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected 323 | > .tsd-kind-icon:before { 324 | background-position: -187px -34px; 325 | } 326 | .tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private 327 | > .tsd-kind-icon:before { 328 | background-position: -119px -34px; 329 | } 330 | .tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { 331 | background-position: -204px -34px; 332 | } 333 | .tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited 334 | > .tsd-kind-icon:before { 335 | background-position: -221px -34px; 336 | } 337 | 338 | .tsd-kind-accessor > .tsd-kind-icon:before { 339 | background-position: -136px -51px; 340 | } 341 | .tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { 342 | background-position: -153px -51px; 343 | } 344 | .tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { 345 | background-position: -119px -51px; 346 | } 347 | .tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { 348 | background-position: -51px -51px; 349 | } 350 | .tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited 351 | > .tsd-kind-icon:before { 352 | background-position: -68px -51px; 353 | } 354 | .tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected 355 | > .tsd-kind-icon:before { 356 | background-position: -85px -51px; 357 | } 358 | .tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 359 | > .tsd-kind-icon:before { 360 | background-position: -102px -51px; 361 | } 362 | .tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private 363 | > .tsd-kind-icon:before { 364 | background-position: -119px -51px; 365 | } 366 | .tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { 367 | background-position: -170px -51px; 368 | } 369 | .tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected 370 | > .tsd-kind-icon:before { 371 | background-position: -187px -51px; 372 | } 373 | .tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 374 | background-position: -119px -51px; 375 | } 376 | .tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { 377 | background-position: -204px -51px; 378 | } 379 | .tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited 380 | > .tsd-kind-icon:before { 381 | background-position: -221px -51px; 382 | } 383 | 384 | .tsd-kind-function > .tsd-kind-icon:before { 385 | background-position: -136px -68px; 386 | } 387 | .tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { 388 | background-position: -153px -68px; 389 | } 390 | .tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { 391 | background-position: -119px -68px; 392 | } 393 | .tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { 394 | background-position: -51px -68px; 395 | } 396 | .tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited 397 | > .tsd-kind-icon:before { 398 | background-position: -68px -68px; 399 | } 400 | .tsd-kind-function.tsd-parent-kind-class.tsd-is-protected 401 | > .tsd-kind-icon:before { 402 | background-position: -85px -68px; 403 | } 404 | .tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 405 | > .tsd-kind-icon:before { 406 | background-position: -102px -68px; 407 | } 408 | .tsd-kind-function.tsd-parent-kind-class.tsd-is-private 409 | > .tsd-kind-icon:before { 410 | background-position: -119px -68px; 411 | } 412 | .tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { 413 | background-position: -170px -68px; 414 | } 415 | .tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected 416 | > .tsd-kind-icon:before { 417 | background-position: -187px -68px; 418 | } 419 | .tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 420 | background-position: -119px -68px; 421 | } 422 | .tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { 423 | background-position: -204px -68px; 424 | } 425 | .tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited 426 | > .tsd-kind-icon:before { 427 | background-position: -221px -68px; 428 | } 429 | 430 | .tsd-kind-method > .tsd-kind-icon:before { 431 | background-position: -136px -68px; 432 | } 433 | .tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { 434 | background-position: -153px -68px; 435 | } 436 | .tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { 437 | background-position: -119px -68px; 438 | } 439 | .tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { 440 | background-position: -51px -68px; 441 | } 442 | .tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited 443 | > .tsd-kind-icon:before { 444 | background-position: -68px -68px; 445 | } 446 | .tsd-kind-method.tsd-parent-kind-class.tsd-is-protected 447 | > .tsd-kind-icon:before { 448 | background-position: -85px -68px; 449 | } 450 | .tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 451 | > .tsd-kind-icon:before { 452 | background-position: -102px -68px; 453 | } 454 | .tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { 455 | background-position: -119px -68px; 456 | } 457 | .tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { 458 | background-position: -170px -68px; 459 | } 460 | .tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { 461 | background-position: -187px -68px; 462 | } 463 | .tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 464 | background-position: -119px -68px; 465 | } 466 | .tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { 467 | background-position: -204px -68px; 468 | } 469 | .tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited 470 | > .tsd-kind-icon:before { 471 | background-position: -221px -68px; 472 | } 473 | 474 | .tsd-kind-call-signature > .tsd-kind-icon:before { 475 | background-position: -136px -68px; 476 | } 477 | .tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { 478 | background-position: -153px -68px; 479 | } 480 | .tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { 481 | background-position: -119px -68px; 482 | } 483 | .tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { 484 | background-position: -51px -68px; 485 | } 486 | .tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited 487 | > .tsd-kind-icon:before { 488 | background-position: -68px -68px; 489 | } 490 | .tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected 491 | > .tsd-kind-icon:before { 492 | background-position: -85px -68px; 493 | } 494 | .tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 495 | > .tsd-kind-icon:before { 496 | background-position: -102px -68px; 497 | } 498 | .tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private 499 | > .tsd-kind-icon:before { 500 | background-position: -119px -68px; 501 | } 502 | .tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { 503 | background-position: -170px -68px; 504 | } 505 | .tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected 506 | > .tsd-kind-icon:before { 507 | background-position: -187px -68px; 508 | } 509 | .tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private 510 | > .tsd-kind-icon:before { 511 | background-position: -119px -68px; 512 | } 513 | .tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { 514 | background-position: -204px -68px; 515 | } 516 | .tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited 517 | > .tsd-kind-icon:before { 518 | background-position: -221px -68px; 519 | } 520 | 521 | .tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { 522 | background-position: -136px -85px; 523 | } 524 | .tsd-kind-function.tsd-has-type-parameter.tsd-is-protected 525 | > .tsd-kind-icon:before { 526 | background-position: -153px -85px; 527 | } 528 | .tsd-kind-function.tsd-has-type-parameter.tsd-is-private 529 | > .tsd-kind-icon:before { 530 | background-position: -119px -85px; 531 | } 532 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class 533 | > .tsd-kind-icon:before { 534 | background-position: -51px -85px; 535 | } 536 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited 537 | > .tsd-kind-icon:before { 538 | background-position: -68px -85px; 539 | } 540 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected 541 | > .tsd-kind-icon:before { 542 | background-position: -85px -85px; 543 | } 544 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 545 | > .tsd-kind-icon:before { 546 | background-position: -102px -85px; 547 | } 548 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private 549 | > .tsd-kind-icon:before { 550 | background-position: -119px -85px; 551 | } 552 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum 553 | > .tsd-kind-icon:before { 554 | background-position: -170px -85px; 555 | } 556 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected 557 | > .tsd-kind-icon:before { 558 | background-position: -187px -85px; 559 | } 560 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private 561 | > .tsd-kind-icon:before { 562 | background-position: -119px -85px; 563 | } 564 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface 565 | > .tsd-kind-icon:before { 566 | background-position: -204px -85px; 567 | } 568 | .tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited 569 | > .tsd-kind-icon:before { 570 | background-position: -221px -85px; 571 | } 572 | 573 | .tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { 574 | background-position: -136px -85px; 575 | } 576 | .tsd-kind-method.tsd-has-type-parameter.tsd-is-protected 577 | > .tsd-kind-icon:before { 578 | background-position: -153px -85px; 579 | } 580 | .tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { 581 | background-position: -119px -85px; 582 | } 583 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class 584 | > .tsd-kind-icon:before { 585 | background-position: -51px -85px; 586 | } 587 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited 588 | > .tsd-kind-icon:before { 589 | background-position: -68px -85px; 590 | } 591 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected 592 | > .tsd-kind-icon:before { 593 | background-position: -85px -85px; 594 | } 595 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 596 | > .tsd-kind-icon:before { 597 | background-position: -102px -85px; 598 | } 599 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private 600 | > .tsd-kind-icon:before { 601 | background-position: -119px -85px; 602 | } 603 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum 604 | > .tsd-kind-icon:before { 605 | background-position: -170px -85px; 606 | } 607 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected 608 | > .tsd-kind-icon:before { 609 | background-position: -187px -85px; 610 | } 611 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private 612 | > .tsd-kind-icon:before { 613 | background-position: -119px -85px; 614 | } 615 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface 616 | > .tsd-kind-icon:before { 617 | background-position: -204px -85px; 618 | } 619 | .tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited 620 | > .tsd-kind-icon:before { 621 | background-position: -221px -85px; 622 | } 623 | 624 | .tsd-kind-constructor > .tsd-kind-icon:before { 625 | background-position: -136px -102px; 626 | } 627 | .tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { 628 | background-position: -153px -102px; 629 | } 630 | .tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { 631 | background-position: -119px -102px; 632 | } 633 | .tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { 634 | background-position: -51px -102px; 635 | } 636 | .tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited 637 | > .tsd-kind-icon:before { 638 | background-position: -68px -102px; 639 | } 640 | .tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected 641 | > .tsd-kind-icon:before { 642 | background-position: -85px -102px; 643 | } 644 | .tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 645 | > .tsd-kind-icon:before { 646 | background-position: -102px -102px; 647 | } 648 | .tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private 649 | > .tsd-kind-icon:before { 650 | background-position: -119px -102px; 651 | } 652 | .tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { 653 | background-position: -170px -102px; 654 | } 655 | .tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected 656 | > .tsd-kind-icon:before { 657 | background-position: -187px -102px; 658 | } 659 | .tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private 660 | > .tsd-kind-icon:before { 661 | background-position: -119px -102px; 662 | } 663 | .tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { 664 | background-position: -204px -102px; 665 | } 666 | .tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited 667 | > .tsd-kind-icon:before { 668 | background-position: -221px -102px; 669 | } 670 | 671 | .tsd-kind-constructor-signature > .tsd-kind-icon:before { 672 | background-position: -136px -102px; 673 | } 674 | .tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { 675 | background-position: -153px -102px; 676 | } 677 | .tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { 678 | background-position: -119px -102px; 679 | } 680 | .tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { 681 | background-position: -51px -102px; 682 | } 683 | .tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited 684 | > .tsd-kind-icon:before { 685 | background-position: -68px -102px; 686 | } 687 | .tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected 688 | > .tsd-kind-icon:before { 689 | background-position: -85px -102px; 690 | } 691 | .tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 692 | > .tsd-kind-icon:before { 693 | background-position: -102px -102px; 694 | } 695 | .tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private 696 | > .tsd-kind-icon:before { 697 | background-position: -119px -102px; 698 | } 699 | .tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { 700 | background-position: -170px -102px; 701 | } 702 | .tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected 703 | > .tsd-kind-icon:before { 704 | background-position: -187px -102px; 705 | } 706 | .tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private 707 | > .tsd-kind-icon:before { 708 | background-position: -119px -102px; 709 | } 710 | .tsd-kind-constructor-signature.tsd-parent-kind-interface 711 | > .tsd-kind-icon:before { 712 | background-position: -204px -102px; 713 | } 714 | .tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited 715 | > .tsd-kind-icon:before { 716 | background-position: -221px -102px; 717 | } 718 | 719 | .tsd-kind-index-signature > .tsd-kind-icon:before { 720 | background-position: -136px -119px; 721 | } 722 | .tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { 723 | background-position: -153px -119px; 724 | } 725 | .tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { 726 | background-position: -119px -119px; 727 | } 728 | .tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { 729 | background-position: -51px -119px; 730 | } 731 | .tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited 732 | > .tsd-kind-icon:before { 733 | background-position: -68px -119px; 734 | } 735 | .tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected 736 | > .tsd-kind-icon:before { 737 | background-position: -85px -119px; 738 | } 739 | .tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 740 | > .tsd-kind-icon:before { 741 | background-position: -102px -119px; 742 | } 743 | .tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private 744 | > .tsd-kind-icon:before { 745 | background-position: -119px -119px; 746 | } 747 | .tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { 748 | background-position: -170px -119px; 749 | } 750 | .tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected 751 | > .tsd-kind-icon:before { 752 | background-position: -187px -119px; 753 | } 754 | .tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private 755 | > .tsd-kind-icon:before { 756 | background-position: -119px -119px; 757 | } 758 | .tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { 759 | background-position: -204px -119px; 760 | } 761 | .tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited 762 | > .tsd-kind-icon:before { 763 | background-position: -221px -119px; 764 | } 765 | 766 | .tsd-kind-event > .tsd-kind-icon:before { 767 | background-position: -136px -136px; 768 | } 769 | .tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { 770 | background-position: -153px -136px; 771 | } 772 | .tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { 773 | background-position: -119px -136px; 774 | } 775 | .tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { 776 | background-position: -51px -136px; 777 | } 778 | .tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { 779 | background-position: -68px -136px; 780 | } 781 | .tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { 782 | background-position: -85px -136px; 783 | } 784 | .tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 785 | > .tsd-kind-icon:before { 786 | background-position: -102px -136px; 787 | } 788 | .tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { 789 | background-position: -119px -136px; 790 | } 791 | .tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { 792 | background-position: -170px -136px; 793 | } 794 | .tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { 795 | background-position: -187px -136px; 796 | } 797 | .tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 798 | background-position: -119px -136px; 799 | } 800 | .tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { 801 | background-position: -204px -136px; 802 | } 803 | .tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited 804 | > .tsd-kind-icon:before { 805 | background-position: -221px -136px; 806 | } 807 | 808 | .tsd-is-static > .tsd-kind-icon:before { 809 | background-position: -136px -153px; 810 | } 811 | .tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { 812 | background-position: -153px -153px; 813 | } 814 | .tsd-is-static.tsd-is-private > .tsd-kind-icon:before { 815 | background-position: -119px -153px; 816 | } 817 | .tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { 818 | background-position: -51px -153px; 819 | } 820 | .tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { 821 | background-position: -68px -153px; 822 | } 823 | .tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { 824 | background-position: -85px -153px; 825 | } 826 | .tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 827 | > .tsd-kind-icon:before { 828 | background-position: -102px -153px; 829 | } 830 | .tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { 831 | background-position: -119px -153px; 832 | } 833 | .tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { 834 | background-position: -170px -153px; 835 | } 836 | .tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { 837 | background-position: -187px -153px; 838 | } 839 | .tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { 840 | background-position: -119px -153px; 841 | } 842 | .tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { 843 | background-position: -204px -153px; 844 | } 845 | .tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited 846 | > .tsd-kind-icon:before { 847 | background-position: -221px -153px; 848 | } 849 | 850 | .tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { 851 | background-position: -136px -170px; 852 | } 853 | .tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { 854 | background-position: -153px -170px; 855 | } 856 | .tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { 857 | background-position: -119px -170px; 858 | } 859 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { 860 | background-position: -51px -170px; 861 | } 862 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited 863 | > .tsd-kind-icon:before { 864 | background-position: -68px -170px; 865 | } 866 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected 867 | > .tsd-kind-icon:before { 868 | background-position: -85px -170px; 869 | } 870 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 871 | > .tsd-kind-icon:before { 872 | background-position: -102px -170px; 873 | } 874 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private 875 | > .tsd-kind-icon:before { 876 | background-position: -119px -170px; 877 | } 878 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { 879 | background-position: -170px -170px; 880 | } 881 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected 882 | > .tsd-kind-icon:before { 883 | background-position: -187px -170px; 884 | } 885 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private 886 | > .tsd-kind-icon:before { 887 | background-position: -119px -170px; 888 | } 889 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-interface 890 | > .tsd-kind-icon:before { 891 | background-position: -204px -170px; 892 | } 893 | .tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited 894 | > .tsd-kind-icon:before { 895 | background-position: -221px -170px; 896 | } 897 | 898 | .tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { 899 | background-position: -136px -170px; 900 | } 901 | .tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { 902 | background-position: -153px -170px; 903 | } 904 | .tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { 905 | background-position: -119px -170px; 906 | } 907 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { 908 | background-position: -51px -170px; 909 | } 910 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited 911 | > .tsd-kind-icon:before { 912 | background-position: -68px -170px; 913 | } 914 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected 915 | > .tsd-kind-icon:before { 916 | background-position: -85px -170px; 917 | } 918 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 919 | > .tsd-kind-icon:before { 920 | background-position: -102px -170px; 921 | } 922 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private 923 | > .tsd-kind-icon:before { 924 | background-position: -119px -170px; 925 | } 926 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { 927 | background-position: -170px -170px; 928 | } 929 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected 930 | > .tsd-kind-icon:before { 931 | background-position: -187px -170px; 932 | } 933 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private 934 | > .tsd-kind-icon:before { 935 | background-position: -119px -170px; 936 | } 937 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-interface 938 | > .tsd-kind-icon:before { 939 | background-position: -204px -170px; 940 | } 941 | .tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited 942 | > .tsd-kind-icon:before { 943 | background-position: -221px -170px; 944 | } 945 | 946 | .tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { 947 | background-position: -136px -170px; 948 | } 949 | .tsd-is-static.tsd-kind-call-signature.tsd-is-protected 950 | > .tsd-kind-icon:before { 951 | background-position: -153px -170px; 952 | } 953 | .tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { 954 | background-position: -119px -170px; 955 | } 956 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class 957 | > .tsd-kind-icon:before { 958 | background-position: -51px -170px; 959 | } 960 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited 961 | > .tsd-kind-icon:before { 962 | background-position: -68px -170px; 963 | } 964 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected 965 | > .tsd-kind-icon:before { 966 | background-position: -85px -170px; 967 | } 968 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 969 | > .tsd-kind-icon:before { 970 | background-position: -102px -170px; 971 | } 972 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private 973 | > .tsd-kind-icon:before { 974 | background-position: -119px -170px; 975 | } 976 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum 977 | > .tsd-kind-icon:before { 978 | background-position: -170px -170px; 979 | } 980 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected 981 | > .tsd-kind-icon:before { 982 | background-position: -187px -170px; 983 | } 984 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private 985 | > .tsd-kind-icon:before { 986 | background-position: -119px -170px; 987 | } 988 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface 989 | > .tsd-kind-icon:before { 990 | background-position: -204px -170px; 991 | } 992 | .tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited 993 | > .tsd-kind-icon:before { 994 | background-position: -221px -170px; 995 | } 996 | 997 | .tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { 998 | background-position: -136px -187px; 999 | } 1000 | .tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { 1001 | background-position: -153px -187px; 1002 | } 1003 | .tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { 1004 | background-position: -119px -187px; 1005 | } 1006 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { 1007 | background-position: -51px -187px; 1008 | } 1009 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited 1010 | > .tsd-kind-icon:before { 1011 | background-position: -68px -187px; 1012 | } 1013 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected 1014 | > .tsd-kind-icon:before { 1015 | background-position: -85px -187px; 1016 | } 1017 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited 1018 | > .tsd-kind-icon:before { 1019 | background-position: -102px -187px; 1020 | } 1021 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private 1022 | > .tsd-kind-icon:before { 1023 | background-position: -119px -187px; 1024 | } 1025 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { 1026 | background-position: -170px -187px; 1027 | } 1028 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected 1029 | > .tsd-kind-icon:before { 1030 | background-position: -187px -187px; 1031 | } 1032 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private 1033 | > .tsd-kind-icon:before { 1034 | background-position: -119px -187px; 1035 | } 1036 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-interface 1037 | > .tsd-kind-icon:before { 1038 | background-position: -204px -187px; 1039 | } 1040 | .tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited 1041 | > .tsd-kind-icon:before { 1042 | background-position: -221px -187px; 1043 | } 1044 | -------------------------------------------------------------------------------- /docs/assets/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/placemark/check-geojson/5701f8fe832c10793275c9174dd387829c4b4c7b/docs/assets/icons.png -------------------------------------------------------------------------------- /docs/assets/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/placemark/check-geojson/5701f8fe832c10793275c9174dd387829c4b4c7b/docs/assets/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = JSON.parse("{\"kinds\":{\"64\":\"Function\",\"128\":\"Class\",\"256\":\"Interface\",\"512\":\"Constructor\",\"1024\":\"Property\",\"65536\":\"Type literal\",\"262144\":\"Accessor\"},\"rows\":[{\"id\":0,\"kind\":64,\"name\":\"getIssues\",\"url\":\"modules.html#getIssues\",\"classes\":\"tsd-kind-function\"},{\"id\":1,\"kind\":64,\"name\":\"check\",\"url\":\"modules.html#check\",\"classes\":\"tsd-kind-function\"},{\"id\":2,\"kind\":64,\"name\":\"scavenge\",\"url\":\"modules.html#scavenge\",\"classes\":\"tsd-kind-function\"},{\"id\":3,\"kind\":128,\"name\":\"HintError\",\"url\":\"classes/HintError.html\",\"classes\":\"tsd-kind-class\"},{\"id\":4,\"kind\":65536,\"name\":\"__type\",\"url\":\"classes/HintError.html#__type\",\"classes\":\"tsd-kind-type-literal tsd-parent-kind-class\",\"parent\":\"HintError\"},{\"id\":5,\"kind\":512,\"name\":\"constructor\",\"url\":\"classes/HintError.html#constructor\",\"classes\":\"tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite\",\"parent\":\"HintError\"},{\"id\":6,\"kind\":1024,\"name\":\"issues\",\"url\":\"classes/HintError.html#issues\",\"classes\":\"tsd-kind-property tsd-parent-kind-class\",\"parent\":\"HintError\"},{\"id\":7,\"kind\":262144,\"name\":\"message\",\"url\":\"classes/HintError.html#message\",\"classes\":\"tsd-kind-get-signature tsd-parent-kind-class tsd-is-overwrite\",\"parent\":\"HintError\"},{\"id\":8,\"kind\":256,\"name\":\"HintIssue\",\"url\":\"interfaces/HintIssue.html\",\"classes\":\"tsd-kind-interface\"},{\"id\":9,\"kind\":1024,\"name\":\"from\",\"url\":\"interfaces/HintIssue.html#from\",\"classes\":\"tsd-kind-property tsd-parent-kind-interface\",\"parent\":\"HintIssue\"},{\"id\":10,\"kind\":1024,\"name\":\"to\",\"url\":\"interfaces/HintIssue.html#to\",\"classes\":\"tsd-kind-property tsd-parent-kind-interface\",\"parent\":\"HintIssue\"},{\"id\":11,\"kind\":1024,\"name\":\"node\",\"url\":\"interfaces/HintIssue.html#node\",\"classes\":\"tsd-kind-property tsd-parent-kind-interface\",\"parent\":\"HintIssue\"},{\"id\":12,\"kind\":1024,\"name\":\"severity\",\"url\":\"interfaces/HintIssue.html#severity\",\"classes\":\"tsd-kind-property tsd-parent-kind-interface\",\"parent\":\"HintIssue\"},{\"id\":13,\"kind\":1024,\"name\":\"message\",\"url\":\"interfaces/HintIssue.html#message\",\"classes\":\"tsd-kind-property tsd-parent-kind-interface\",\"parent\":\"HintIssue\"}],\"index\":{\"version\":\"2.3.9\",\"fields\":[\"name\",\"parent\"],\"fieldVectors\":[[\"name/0\",[0,23.026]],[\"parent/0\",[]],[\"name/1\",[1,23.026]],[\"parent/1\",[]],[\"name/2\",[2,23.026]],[\"parent/2\",[]],[\"name/3\",[3,10.033]],[\"parent/3\",[]],[\"name/4\",[4,23.026]],[\"parent/4\",[3,0.818]],[\"name/5\",[5,23.026]],[\"parent/5\",[3,0.818]],[\"name/6\",[6,23.026]],[\"parent/6\",[3,0.818]],[\"name/7\",[7,17.918]],[\"parent/7\",[3,0.818]],[\"name/8\",[8,8.362]],[\"parent/8\",[]],[\"name/9\",[9,23.026]],[\"parent/9\",[8,0.681]],[\"name/10\",[10,23.026]],[\"parent/10\",[8,0.681]],[\"name/11\",[11,23.026]],[\"parent/11\",[8,0.681]],[\"name/12\",[12,23.026]],[\"parent/12\",[8,0.681]],[\"name/13\",[7,17.918]],[\"parent/13\",[8,0.681]]],\"invertedIndex\":[[\"__type\",{\"_index\":4,\"name\":{\"4\":{}},\"parent\":{}}],[\"check\",{\"_index\":1,\"name\":{\"1\":{}},\"parent\":{}}],[\"constructor\",{\"_index\":5,\"name\":{\"5\":{}},\"parent\":{}}],[\"from\",{\"_index\":9,\"name\":{\"9\":{}},\"parent\":{}}],[\"getissues\",{\"_index\":0,\"name\":{\"0\":{}},\"parent\":{}}],[\"hinterror\",{\"_index\":3,\"name\":{\"3\":{}},\"parent\":{\"4\":{},\"5\":{},\"6\":{},\"7\":{}}}],[\"hintissue\",{\"_index\":8,\"name\":{\"8\":{}},\"parent\":{\"9\":{},\"10\":{},\"11\":{},\"12\":{},\"13\":{}}}],[\"issues\",{\"_index\":6,\"name\":{\"6\":{}},\"parent\":{}}],[\"message\",{\"_index\":7,\"name\":{\"7\":{},\"13\":{}},\"parent\":{}}],[\"node\",{\"_index\":11,\"name\":{\"11\":{}},\"parent\":{}}],[\"scavenge\",{\"_index\":2,\"name\":{\"2\":{}},\"parent\":{}}],[\"severity\",{\"_index\":12,\"name\":{\"12\":{}},\"parent\":{}}],[\"to\",{\"_index\":10,\"name\":{\"10\":{}},\"parent\":{}}]],\"pipeline\":[]}}"); -------------------------------------------------------------------------------- /docs/assets/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/placemark/check-geojson/5701f8fe832c10793275c9174dd387829c4b4c7b/docs/assets/widgets.png -------------------------------------------------------------------------------- /docs/assets/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/placemark/check-geojson/5701f8fe832c10793275c9174dd387829c4b4c7b/docs/assets/widgets@2x.png -------------------------------------------------------------------------------- /docs/classes/HintError.html: -------------------------------------------------------------------------------- 1 | HintError | @placemarkio/check-geojson
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • Error
    • HintError

Index

Constructors

Properties

cause?: Error
issues: HintIssue[] = []
name: string
stack?: string
prepareStackTrace?: (err: Error, stackTraces: CallSite[]) => any

Type declaration

stackTraceLimit: number

Accessors

  • get message(): string

Methods

  • captureStackTrace(targetObject: object, constructorOpt?: Function): void
  • 5 |

    Create .stack property on a target object

    6 |

    Parameters

    • targetObject: object
    • Optional constructorOpt: Function

    Returns void

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | @placemarkio/check-geojson
Options
All
  • Public
  • Public/Protected
  • All
Menu

@placemarkio/check-geojson

2 | 3 |

check-geojson

4 |
5 |

check-geojson development is supported by 🌎 placemark.io

6 |

geojsonhint for 2021.

7 |

I started working on geojsonhint in 2014. It's a pretty useful project. 8 | But it has been stagnant for a long time now, and has some annoying long-term 9 | issues.

10 |

check-geojson is intended to be a full successor for geojsonhint. Like geojsonhint, 11 | it is tailored to a particular usecase: writing GeoJSON by hand, or quickly sussing 12 | out issues in GeoJSON that you've received.

13 | 14 | 15 |

📕 API Documentation

16 | 17 | 18 | 19 |

Main differences from geojsonhint

20 |
21 |
    22 |
  • Actively maintained
  • 23 |
  • Written in TypeScript and includes types
  • 24 |
  • Uses momoa to parse JSON instead of a homemade 25 | parser. This is probably the biggest one. jsonlint-lines was a hack, which I created 26 | because I could not find a single parser that actually parsed JSON and gave line numbers 27 | for values. momoa is much better than that hack, and using it makes line-level errors 28 | much cleaner.
  • 29 |
30 |

Unlike geojsonhint, this checker only produces errors, not warnings. So things 31 | that geojsonhint would warn about, like:

32 |
    33 |
  • excessive coordinate precision
  • 34 |
  • right-hand rule compliance
  • 35 |
36 |

This does not check for. Additionally, the crs member is ignored by this tool: as of 37 | the latest GeoJSON specification, this is not used.

38 |

We're using the same test fixtures as geojsonhint as a starter.

39 | 40 | 41 |

Usage

42 |
43 |

Not finalized yet

44 |
import { check } from "check-geojson"

try {
const parseValue = check('… geojson string …')
} catch (e) {
/// e.issues
} 45 |
46 |
47 |

Maintainability Test Coverage

48 |

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/interfaces/HintIssue.html: -------------------------------------------------------------------------------- 1 | HintIssue | @placemarkio/check-geojson
Options
All
  • Public
  • Public/Protected
  • All
Menu

Hierarchy

  • HintIssue

Index

Properties

from: number
message: string
node?: Node
severity: "error"
to: number

Generated using TypeDoc

-------------------------------------------------------------------------------- /docs/modules.html: -------------------------------------------------------------------------------- 1 | @placemarkio/check-geojson
Options
All
  • Public
  • Public/Protected
  • All
Menu

@placemarkio/check-geojson

Index

Functions

  • check(jsonStr: string): GeoJSON
  • 2 |

    This catches the same issues as getIssues, but instead 3 | of returning a list of issues, it will throw a HintError 4 | if any errors are detected, and return the parsed 5 | GeoJSON as an object if no errors are.

    6 |

    Parameters

    • jsonStr: string

    Returns GeoJSON

  • 7 |

    Given a string of possibly valid GeoJSON data, 8 | return an array of issues. This will handle invalid JSON 9 | data, invalid GeoJSON structure, and anything else that will 10 | prevent this string of data from being parsed and 11 | displayed on a map.

    12 |

    check-geojson looks for invalid structure and invalid syntax. 13 | It does not check for complex geometry issues like 14 | self-intersections, which are hard to detect and don't cause 15 | failures in most display & manipulation software.

    16 |

    Parameters

    • jsonStr: string

    Returns HintIssue[]

  • scavenge(jsonStr: string): { rejected: { feature: any; reasons: HintIssue[] }[]; result: GeoJSON }
  • 17 |

    This method will allow you to parse a possibly-valid 18 | bit of GeoJSON data. If nothing can be parsed, it will 19 | throw a HintError. However, if the GeoJSON can be parsed, 20 | it will return an object with valid features 21 | and rejected features along with the reasons why those 22 | features were rejected.

    23 |

    Parameters

    • jsonStr: string

    Returns { rejected: { feature: any; reasons: HintIssue[] }[]; result: GeoJSON }

    • rejected: { feature: any; reasons: HintIssue[] }[]
    • result: GeoJSON

Generated using TypeDoc

-------------------------------------------------------------------------------- /lib/array_is_numbers.ts: -------------------------------------------------------------------------------- 1 | import { Node } from '@humanwhocodes/momoa'; 2 | import { makeIssue } from './errors'; 3 | import { Ctx } from './types'; 4 | 5 | export function arrayIsNumbers(ctx: Ctx, elements: Node[], name: string) { 6 | for (const element of elements) { 7 | if (element.type !== 'Number') { 8 | ctx.issues.push( 9 | makeIssue(`Each element in a ${name} must be a number.`, element) 10 | ); 11 | return; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/check_duplicate_keys.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ObjectNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | export function checkDuplicateKeys(ctx: Ctx, parent: ObjectNode): ObjectNode { 6 | const keys = new Set(); 7 | for (const node of parent.members) { 8 | const { 9 | name: { value }, 10 | } = node; 11 | if (keys.has(value)) { 12 | ctx.issues.push( 13 | makeIssue('Duplicate properties are ambiguous in GeoJSON', node) 14 | ); 15 | } 16 | keys.add(value); 17 | } 18 | return parent; 19 | } 20 | -------------------------------------------------------------------------------- /lib/enforce_bbox.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ObjectNode } from '@humanwhocodes/momoa'; 3 | import { getArray } from './get_array'; 4 | import { arrayIsNumbers } from './array_is_numbers'; 5 | import { Ctx } from './types'; 6 | 7 | export function enforceBbox(ctx: Ctx, node: ObjectNode) { 8 | const member = node.members.find((member) => { 9 | return member.name.value === 'bbox'; 10 | }); 11 | 12 | // bboxes are optional 13 | if (member === undefined) return; 14 | 15 | const array = getArray(ctx, member.value); 16 | 17 | if (!array) return; 18 | 19 | if (!(array.elements.length === 4 || array.elements.length === 6)) { 20 | ctx.issues.push(makeIssue('A bbox must have 4 or 6 positions', array)); 21 | } 22 | 23 | arrayIsNumbers(ctx, array.elements, 'bbox'); 24 | } 25 | -------------------------------------------------------------------------------- /lib/enforce_position.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ArrayNode } from '@humanwhocodes/momoa'; 3 | import { arrayIsNumbers } from './array_is_numbers'; 4 | import { Ctx } from './types'; 5 | 6 | export function enforcePosition(ctx: Ctx, node: ArrayNode | null) { 7 | // This error has already been caught. Allow a no-op for simplicity. 8 | if (node === null) return; 9 | 10 | if (node.elements.length < 2 || node.elements.length > 3) { 11 | ctx.issues.push( 12 | makeIssue( 13 | `A position should have 2 or 3 elements - found ${node.elements.length}.`, 14 | node 15 | ) 16 | ); 17 | } 18 | 19 | arrayIsNumbers(ctx, node.elements, 'position'); 20 | } 21 | -------------------------------------------------------------------------------- /lib/enforce_position_array.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { enforcePosition } from './enforce_position'; 3 | import { enforceSamePosition } from './enforce_same_position'; 4 | import { Node, ArrayNode } from '@humanwhocodes/momoa'; 5 | import { Ctx } from './types'; 6 | 7 | function getArray(ctx: Ctx, node: Node): ArrayNode | null { 8 | if (node.type !== 'Array') { 9 | ctx.issues.push( 10 | makeIssue('Expected to find an array of positions here.', node) 11 | ); 12 | return null; 13 | } 14 | return node; 15 | } 16 | 17 | type PositionKind = 'Polygon' | 'LineString'; 18 | 19 | export function enforcePositionArray( 20 | ctx: Ctx, 21 | node: Node | null, 22 | kind?: PositionKind 23 | ) { 24 | // This error has already been caught. Allow a no-op for simplicity. 25 | if (node === null) return; 26 | 27 | node = getArray(ctx, node); 28 | if (!node) return; 29 | 30 | for (const element of node.elements) { 31 | if (element.type !== 'Array') { 32 | ctx.issues.push( 33 | makeIssue( 34 | 'Expected to find a position here, found another type.', 35 | element 36 | ) 37 | ); 38 | return; 39 | } else { 40 | enforcePosition(ctx, element); 41 | } 42 | } 43 | 44 | switch (kind) { 45 | case 'LineString': { 46 | if (node.elements.length < 2) { 47 | ctx.issues.push( 48 | makeIssue('Expected to find two or more positions here.', node) 49 | ); 50 | } 51 | break; 52 | } 53 | case 'Polygon': 54 | if (node.elements.length < 4) { 55 | ctx.issues.push( 56 | makeIssue('Expected to find four or more positions here.', node) 57 | ); 58 | } 59 | enforceSamePosition(ctx, node); 60 | break; 61 | } 62 | } 63 | 64 | export function enforcePositionArray2( 65 | ctx: Ctx, 66 | node: Node | null, 67 | kind?: PositionKind 68 | ) { 69 | // This error has already been caught. Allow a no-op for simplicity. 70 | if (node === null) return; 71 | 72 | node = getArray(ctx, node); 73 | if (!node) return; 74 | 75 | for (const element of node.elements) { 76 | enforcePositionArray(ctx, element, kind); 77 | } 78 | } 79 | 80 | export function enforcePositionArray3( 81 | ctx: Ctx, 82 | node: ArrayNode | null, 83 | kind?: PositionKind 84 | ) { 85 | // This error has already been caught. Allow a no-op for simplicity. 86 | if (node === null) return; 87 | 88 | node = getArray(ctx, node); 89 | if (!node) return; 90 | 91 | for (const element of node.elements) { 92 | enforcePositionArray2(ctx, element, kind); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/enforce_same_position.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ArrayNode, NumberNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | export function enforceSamePosition(ctx: Ctx, node: ArrayNode) { 6 | const first = node.elements[0] as ArrayNode; 7 | const last = node.elements[node.elements.length - 1] as ArrayNode; 8 | const len = Math.max(first.elements.length, last.elements.length); 9 | 10 | for (let j = 0; j < len; j++) { 11 | const firstValue = (first.elements[j] as NumberNode | undefined)?.value; 12 | const secondValue = (last.elements[j] as NumberNode | undefined)?.value; 13 | if (firstValue !== secondValue) { 14 | ctx.issues.push( 15 | makeIssue( 16 | 'First and last positions of a Polygon or MultiPolygon’s ring should be the same.', 17 | first 18 | ), 19 | makeIssue( 20 | 'First and last positions of a Polygon or MultiPolygon’s ring should be the same.', 21 | last 22 | ) 23 | ); 24 | return; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/errors.ts: -------------------------------------------------------------------------------- 1 | import { Node } from '@humanwhocodes/momoa'; 2 | import { HintIssue } from './types'; 3 | 4 | export function makeIssue(message: string, node: Node): HintIssue { 5 | return { 6 | message, 7 | severity: 'error', 8 | // node, 9 | from: node.loc.start.offset, 10 | to: node.loc.end.offset, 11 | }; 12 | } 13 | 14 | export class HintError extends Error { 15 | issues: HintIssue[] = []; 16 | 17 | constructor(issues: HintIssue[]) { 18 | super(); 19 | // restore prototype chain 20 | const actualProto = new.target.prototype; 21 | Object.setPrototypeOf(this, actualProto); 22 | this.issues = issues; 23 | } 24 | 25 | get message() { 26 | return JSON.stringify(this.issues, null, 2); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/forbid_confusing_properties.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ObjectNode, MemberNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | type PropertiesFrom = 'Feature' | 'FeatureCollection' | 'Geometry'; 6 | 7 | function forbidProperty( 8 | ctx: Ctx, 9 | member: MemberNode, 10 | propertiesFrom: PropertiesFrom, 11 | name: string 12 | ) { 13 | if (member.name.value === name) { 14 | ctx.issues.push( 15 | makeIssue( 16 | `${propertiesFrom} objects cannot contain a member named ${member.name.value}`, 17 | member.name 18 | ) 19 | ); 20 | } 21 | } 22 | 23 | const FORBIDDEN_PROPERTIES = { 24 | Geometry: ['properties', 'geometry', 'features'], 25 | Feature: ['features'], 26 | FeatureCollection: ['properties', 'coordinates'], 27 | } as const; 28 | 29 | export function forbidConfusingProperties( 30 | ctx: Ctx, 31 | node: ObjectNode, 32 | propertiesFrom: PropertiesFrom 33 | ) { 34 | for (const member of node.members) { 35 | for (const property of FORBIDDEN_PROPERTIES[propertiesFrom]) { 36 | forbidProperty(ctx, member, propertiesFrom, property); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/get_array.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { Node, ArrayNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | export function getArray(ctx: Ctx, node: Node | null): ArrayNode | null { 6 | if (node?.type === 'Array') return node; 7 | if (node) { 8 | ctx.issues.push(makeIssue('This must be an array.', node)); 9 | } 10 | return null; 11 | } 12 | -------------------------------------------------------------------------------- /lib/get_member_value.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { ObjectNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | export function getMemberValue(ctx: Ctx, node: ObjectNode, name: string) { 6 | const member = node.members.find((member) => { 7 | return member.name.value === name; 8 | }); 9 | 10 | if (!member) { 11 | ctx.issues.push( 12 | makeIssue( 13 | `This GeoJSON object requires a ${name} member but it is missing.`, 14 | node 15 | ) 16 | ); 17 | 18 | return null; 19 | } 20 | 21 | return member.value; 22 | } 23 | -------------------------------------------------------------------------------- /lib/get_object.ts: -------------------------------------------------------------------------------- 1 | import { makeIssue } from './errors'; 2 | import { Node, ObjectNode } from '@humanwhocodes/momoa'; 3 | import { Ctx } from './types'; 4 | 5 | export function getObject(ctx: Ctx, node: Node | null): ObjectNode | null { 6 | if (node?.type === 'Object') return node; 7 | if (node) { 8 | ctx.issues.push(makeIssue('This must be an object.', node)); 9 | } 10 | return null; 11 | } 12 | -------------------------------------------------------------------------------- /lib/get_type.ts: -------------------------------------------------------------------------------- 1 | import { Ctx, GeoJSONTypeSet } from './types'; 2 | import { GeoJSON } from 'geojson'; 3 | import { HintError, makeIssue } from './errors'; 4 | import { Node } from '@humanwhocodes/momoa'; 5 | 6 | export function getType(ctx: Ctx, node: Node, allowedTypes: GeoJSONTypeSet) { 7 | if (node.type !== 'Object') { 8 | throw new HintError([ 9 | makeIssue('Expected an object, but found an incorrect type.', node), 10 | ]); 11 | } 12 | 13 | const typeMember = node.members.find((member) => { 14 | return member.name.value === 'type'; 15 | }); 16 | 17 | if (!typeMember) { 18 | ctx.issues.push( 19 | makeIssue('This GeoJSON object is missing its type member.', node) 20 | ); 21 | return {}; 22 | } 23 | 24 | const value = typeMember.value; 25 | 26 | if (value.type !== 'String') { 27 | ctx.issues.push( 28 | makeIssue('The type member should have been a string.', node) 29 | ); 30 | 31 | return {}; 32 | } 33 | 34 | if (!allowedTypes.has(value.value as any)) { 35 | ctx.issues.push( 36 | makeIssue('This type of GeoJSON object is not allowed here.', node) 37 | ); 38 | 39 | return {}; 40 | } 41 | 42 | return { 43 | type: value.value as GeoJSON['type'], 44 | objectNode: node, 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | parse, 3 | evaluate, 4 | DocumentNode, 5 | Node, 6 | ObjectNode, 7 | } from '@humanwhocodes/momoa'; 8 | import { GeoJSON } from 'geojson'; 9 | import { HintError, makeIssue } from './errors'; 10 | import { 11 | GeoJSONTypeSet, 12 | GEOJSON_TYPES, 13 | GEOJSON_GEOMETRY_TYPES, 14 | GEOJSON_GEOMETRY_TYPES_EX_GEOMETRY_COLLECTION, 15 | GEOJSON_FEATURE_TYPE, 16 | HintIssue, 17 | Ctx, 18 | } from './types'; 19 | import { getType } from './get_type'; 20 | import { getMemberValue } from './get_member_value'; 21 | import { getArray } from './get_array'; 22 | import { getObject } from './get_object'; 23 | import { enforcePosition } from './enforce_position'; 24 | import { checkDuplicateKeys } from './check_duplicate_keys'; 25 | import { 26 | enforcePositionArray, 27 | enforcePositionArray2, 28 | enforcePositionArray3, 29 | } from './enforce_position_array'; 30 | import { enforceBbox } from './enforce_bbox'; 31 | import { forbidConfusingProperties } from './forbid_confusing_properties'; 32 | 33 | type Checker = (ctx: Ctx, node: ObjectNode) => void; 34 | 35 | function getCoordinates(ctx: Ctx, node: ObjectNode) { 36 | const coordinatesMember = getMemberValue(ctx, node, 'coordinates'); 37 | if (!coordinatesMember) return null; 38 | return getArray(ctx, coordinatesMember); 39 | } 40 | 41 | const checkGeometryShared: Checker = (ctx, node) => { 42 | enforceBbox(ctx, node); 43 | forbidConfusingProperties(ctx, node, 'Geometry'); 44 | }; 45 | 46 | const checkLineString: Checker = (ctx, node) => { 47 | enforcePositionArray(ctx, getCoordinates(ctx, node), 'LineString'); 48 | checkGeometryShared(ctx, node); 49 | }; 50 | 51 | const checkMultiLineString: Checker = (ctx, node) => { 52 | enforcePositionArray2(ctx, getCoordinates(ctx, node), 'LineString'); 53 | checkGeometryShared(ctx, node); 54 | }; 55 | 56 | const checkPolygon: Checker = (ctx, node) => { 57 | enforcePositionArray2(ctx, getCoordinates(ctx, node), 'Polygon'); 58 | checkGeometryShared(ctx, node); 59 | }; 60 | 61 | const checkMultiPolygon: Checker = (ctx, node) => { 62 | enforcePositionArray3(ctx, getCoordinates(ctx, node), 'Polygon'); 63 | checkGeometryShared(ctx, node); 64 | }; 65 | 66 | const checkPoint: Checker = (ctx, node) => { 67 | enforcePosition(ctx, getCoordinates(ctx, node)); 68 | checkGeometryShared(ctx, node); 69 | }; 70 | 71 | const checkMultiPoint: Checker = (ctx, node) => { 72 | enforcePositionArray(ctx, getCoordinates(ctx, node)); 73 | checkGeometryShared(ctx, node); 74 | }; 75 | 76 | const checkGeometryCollection: Checker = (ctx, node) => { 77 | checkGeometryShared(ctx, node); 78 | const geometriesMember = getArray( 79 | ctx, 80 | getMemberValue(ctx, node, 'geometries') 81 | ); 82 | if (!geometriesMember) return; 83 | for (const element of geometriesMember.elements) { 84 | checkObject(ctx, element, GEOJSON_GEOMETRY_TYPES_EX_GEOMETRY_COLLECTION); 85 | } 86 | }; 87 | 88 | const checkFeature: Checker = (ctx, node) => { 89 | forbidConfusingProperties(ctx, node, 'Feature'); 90 | const geometryMember = getMemberValue(ctx, node, 'geometry'); 91 | enforceBbox(ctx, node); 92 | if (geometryMember?.type !== 'Null') { 93 | const geometry = getObject(ctx, geometryMember); 94 | if (geometry) checkObject(ctx, geometry, GEOJSON_GEOMETRY_TYPES); 95 | } 96 | 97 | const idMember = node.members.find((member) => { 98 | return member.name.value === 'id'; 99 | }); 100 | if ( 101 | idMember && 102 | !(idMember.value.type === 'String' || idMember.value.type === 'Number') 103 | ) { 104 | ctx.issues.push( 105 | makeIssue(`The Feature id must be a string or number.`, node) 106 | ); 107 | } 108 | 109 | const properties = getMemberValue(ctx, node, 'properties'); 110 | if (!properties) { 111 | ctx.issues.push(makeIssue(`The properties member is missing.`, node)); 112 | return; 113 | } 114 | 115 | const { type } = properties; 116 | 117 | if (!(type === 'Object' || type === 'Null')) { 118 | ctx.issues.push( 119 | makeIssue(`The Feature properties member can be an object or null.`, node) 120 | ); 121 | } 122 | }; 123 | 124 | const checkFeatureCollection: Checker = (ctx, node) => { 125 | forbidConfusingProperties(ctx, node, 'FeatureCollection'); 126 | const featuresMember = getArray(ctx, getMemberValue(ctx, node, 'features')); 127 | if (!featuresMember) return; 128 | for (const feature of featuresMember.elements) { 129 | const beforeCount = ctx.issues.length; 130 | const obj = getObject(ctx, feature); 131 | if (obj) { 132 | getType(ctx, obj, GEOJSON_FEATURE_TYPE); 133 | checkFeature(ctx, obj); 134 | } 135 | ctx.valid.push( 136 | ctx.issues.length === beforeCount 137 | ? undefined 138 | : ctx.issues.slice(beforeCount) 139 | ); 140 | } 141 | }; 142 | 143 | const CHECKERS: Record = { 144 | LineString: checkLineString, 145 | MultiLineString: checkMultiLineString, 146 | 147 | Polygon: checkPolygon, 148 | MultiPolygon: checkMultiPolygon, 149 | 150 | Point: checkPoint, 151 | MultiPoint: checkMultiPoint, 152 | 153 | GeometryCollection: checkGeometryCollection, 154 | 155 | Feature: checkFeature, 156 | FeatureCollection: checkFeatureCollection, 157 | }; 158 | 159 | function checkObject( 160 | ctx: Ctx, 161 | node: Node, 162 | typeSet: GeoJSONTypeSet = GEOJSON_TYPES 163 | ) { 164 | const { type, objectNode } = getType(ctx, node, typeSet); 165 | if (!(type && objectNode)) return; 166 | checkDuplicateKeys(ctx, objectNode); 167 | CHECKERS[type](ctx, objectNode); 168 | } 169 | 170 | function checkInternal(jsonStr: string): { 171 | ast: DocumentNode | undefined; 172 | ctx: Ctx; 173 | } { 174 | if (typeof jsonStr !== 'string') { 175 | throw new TypeError('Input must be a string. (Use JSON.stringify first.)'); 176 | } 177 | const ctx: Ctx = { 178 | issues: [], 179 | valid: [], 180 | }; 181 | let ast; 182 | try { 183 | ast = parse(jsonStr, { 184 | ranges: true, 185 | }); 186 | checkObject(ctx, ast.body); 187 | } catch (e: unknown) { 188 | ctx.issues.push({ 189 | message: `Invalid JSON: ${(e as Error).message}`, 190 | from: 0, 191 | to: 0, 192 | severity: 'error', 193 | }); 194 | } 195 | 196 | return { ast, ctx }; 197 | } 198 | 199 | /** 200 | * Given a string of possibly valid GeoJSON data, 201 | * return an array of issues. This will handle invalid JSON 202 | * data, invalid GeoJSON structure, and anything else that will 203 | * prevent this string of data from being parsed and 204 | * displayed on a map. 205 | * 206 | * check-geojson looks for invalid structure and invalid syntax. 207 | * It does not check for complex geometry issues like 208 | * self-intersections, which are hard to detect and don't cause 209 | * failures in most display & manipulation software. 210 | */ 211 | export const getIssues = (jsonStr: string): HintIssue[] => { 212 | return checkInternal(jsonStr).ctx.issues; 213 | }; 214 | 215 | /** 216 | * This catches the same issues as `getIssues`, but instead 217 | * of returning a list of issues, it will throw a HintError 218 | * if any errors are detected, and return the parsed 219 | * GeoJSON as an object if no errors are. 220 | */ 221 | export const check = (jsonStr: string): GeoJSON => { 222 | const { ctx, ast } = checkInternal(jsonStr); 223 | if (ctx.issues.length || !ast) throw new HintError(ctx.issues); 224 | return evaluate(ast); 225 | }; 226 | 227 | /** 228 | * This method will allow you to parse a possibly-valid 229 | * bit of GeoJSON data. If nothing can be parsed, it will 230 | * throw a HintError. However, if the GeoJSON can be parsed, 231 | * it will return an object with valid features 232 | * and rejected features along with the reasons why those 233 | * features were rejected. 234 | */ 235 | export const scavenge = ( 236 | jsonStr: string 237 | ): { 238 | result: GeoJSON; 239 | rejected: Array<{ feature: any; reasons: HintIssue[] }>; 240 | } => { 241 | const { 242 | ctx: { issues, valid }, 243 | ast, 244 | } = checkInternal(jsonStr); 245 | // If GeoJSON can't be parsed, throw. 246 | if (!ast) throw new HintError(issues); 247 | const result = evaluate(ast) as GeoJSON; 248 | // If everything was fine, return. 249 | if (!issues.length) return { result, rejected: [] }; 250 | // If there were errors but it's a featurecollection, sift 251 | if (result.type === 'FeatureCollection') { 252 | return { 253 | result: { 254 | ...result, 255 | features: result.features.filter((_, i) => { 256 | return valid[i] === undefined; 257 | }), 258 | }, 259 | rejected: result.features.flatMap((feature, i) => { 260 | const reasons = valid[i]; 261 | if (reasons) { 262 | return [ 263 | { 264 | feature, 265 | reasons, 266 | }, 267 | ]; 268 | } 269 | return []; 270 | }), 271 | }; 272 | } 273 | // Otherwise, throw 274 | throw new HintError(issues); 275 | }; 276 | 277 | export { HintError, HintIssue }; 278 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | import { GeoJSON } from 'geojson'; 2 | 3 | export type GeoJSONTypeSet = Set; 4 | 5 | export interface HintIssue { 6 | from: number; 7 | to: number; 8 | node?: Node; 9 | severity: 'error'; 10 | message: string; 11 | } 12 | 13 | export interface Ctx { 14 | issues: HintIssue[]; 15 | valid: Array; 16 | } 17 | 18 | export const GEOJSON_FEATURE_TYPE = new Set(['Feature']); 19 | 20 | export const GEOJSON_GEOMETRY_TYPES = new Set([ 21 | 'Point', 22 | 'MultiPoint', 23 | 'Polygon', 24 | 'MultiPolygon', 25 | 'LineString', 26 | 'MultiLineString', 27 | 'GeometryCollection', 28 | ]); 29 | 30 | export const GEOJSON_GEOMETRY_TYPES_EX_GEOMETRY_COLLECTION = new Set< 31 | GeoJSON['type'] 32 | >([ 33 | 'Point', 34 | 'MultiPoint', 35 | 'Polygon', 36 | 'MultiPolygon', 37 | 'LineString', 38 | 'MultiLineString', 39 | ]); 40 | 41 | export const GEOJSON_TYPES = new Set( 42 | Array.from(GEOJSON_GEOMETRY_TYPES).concat([ 43 | 'Feature', 44 | 'FeatureCollection', 45 | ])); 46 | -------------------------------------------------------------------------------- /mise.toml: -------------------------------------------------------------------------------- 1 | [tools] 2 | node = "22" 3 | pnpm = "latest" 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.14", 3 | "license": "MIT", 4 | "main": "dist/index.js", 5 | "typings": "dist/index.d.ts", 6 | "source": "lib/index.ts", 7 | "module": "dist/index.esm.mjs", 8 | "files": [ 9 | "dist", 10 | "lib" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/placemark/check-geojson.git" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "engines": { 20 | "node": ">=10" 21 | }, 22 | "scripts": { 23 | "build": "microbundle", 24 | "doc": "typedoc --options ./typedoc.json", 25 | "test": "vitest", 26 | "lint": "eslint . && tsc --noEmit", 27 | "prepare": "husky install", 28 | "prepublishOnly": "pnpm run build && pnpm run doc && cp CNAME docs/CNAME" 29 | }, 30 | "peerDependencies": {}, 31 | "husky": { 32 | "hooks": { 33 | "pre-commit": "pnpm lint" 34 | } 35 | }, 36 | "prettier": { 37 | "printWidth": 80, 38 | "semi": true, 39 | "singleQuote": true, 40 | "trailingComma": "es5" 41 | }, 42 | "name": "@placemarkio/check-geojson", 43 | "author": "Tom MacWright", 44 | "devDependencies": { 45 | "@humanwhocodes/momoa": "^2.0.4", 46 | "@types/geojson": "^7946.0.8", 47 | "@typescript-eslint/eslint-plugin": "^5.25.0", 48 | "@typescript-eslint/parser": "^5.25.0", 49 | "eslint": "^8.16.0", 50 | "husky": "^8.0.1", 51 | "microbundle": "^0.15.0", 52 | "prettier": "^2.6.2", 53 | "tslib": "^2.4.0", 54 | "type-fest": "^2.12.2", 55 | "typedoc": "^0.22.15", 56 | "typescript": "^4.6.4", 57 | "vitest": "^0.24.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/bad.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { check, getIssues, HintError } from "../lib"; 3 | import * as Path from "path"; 4 | import { readFileSync, readdirSync } from "fs"; 5 | 6 | describe("check", () => { 7 | it("invalid input", () => { 8 | // @ts-expect-error this is a test for wrong usage 9 | expect(() => check({})).toThrow(TypeError); 10 | // @ts-expect-error this is a test for wrong usage 11 | expect(() => check([])).toThrow(TypeError); 12 | }); 13 | it("invalid root object", () => { 14 | expect(() => check(JSON.stringify([]))).toThrow(HintError); 15 | expect(() => check(JSON.stringify({}))).toThrow(HintError); 16 | try { 17 | check('{"foo": 1.}'); 18 | } catch (e) { 19 | expect(e.issues).toHaveLength(1); 20 | } 21 | expect(() => check(JSON.stringify({ type: ["foo"] }))).toThrow(HintError); 22 | expect(() => check(JSON.stringify({ type: "foo" }))).toThrow(HintError); 23 | expect(getIssues(JSON.stringify({}))).toEqual([ 24 | { 25 | from: 0, 26 | message: "This GeoJSON object is missing its type member.", 27 | severity: "error", 28 | to: 2, 29 | }, 30 | ]); 31 | expect(getIssues(JSON.stringify({ type: "test" }))).toEqual([ 32 | { 33 | from: 0, 34 | message: "This type of GeoJSON object is not allowed here.", 35 | severity: "error", 36 | to: 15, 37 | }, 38 | ]); 39 | }); 40 | 41 | it("invalid coordinates", () => { 42 | expect(() => check(JSON.stringify({ type: "Point" }))).toThrow(HintError); 43 | expect(() => 44 | check(JSON.stringify({ type: "Point", coordinates: null })), 45 | ).toThrow(HintError); 46 | }); 47 | 48 | it("bad points", () => { 49 | expect(() => 50 | check(JSON.stringify({ type: "Point", coordinates: [1] })), 51 | ).toThrow(HintError); 52 | expect(() => 53 | check(JSON.stringify({ type: "Point", coordinates: [1, 2, 3, 4] })), 54 | ).toThrow(HintError); 55 | }); 56 | 57 | it("bad multipoints", () => { 58 | expect(() => 59 | check(JSON.stringify({ type: "MultiPoint", coordinates: [[true]] })), 60 | ).toThrow(HintError); 61 | }); 62 | 63 | it("geometry collection with null", () => { 64 | expect(() => 65 | check(JSON.stringify({ type: "GeometryCollection", geometries: [null] })), 66 | ).toThrow(HintError); 67 | }); 68 | 69 | it("forbidden properties", () => { 70 | expect(() => 71 | check( 72 | JSON.stringify({ type: "Point", coordinates: [1, 2], properties: {} }), 73 | ), 74 | ).toThrow(HintError); 75 | }); 76 | 77 | it("bad json", () => { 78 | try { 79 | check( 80 | `{ 81 | "type": "MultiPoint" 82 | "coordinates": [["foo", "bar"]] 83 | }`, 84 | ); 85 | } catch (e) { 86 | expect(e.issues[0]).toEqual({ 87 | message: 88 | 'Invalid JSON: Unexpected token String("coordinates") found. (3:5)', 89 | from: 0, 90 | to: 0, 91 | severity: "error", 92 | }); 93 | } 94 | }); 95 | 96 | describe("works with fixtures", () => { 97 | const fixtureNames = readdirSync(Path.join(__dirname, "./fixture/bad/")); 98 | for (const name of fixtureNames) { 99 | it(`fixture: ${name}`, () => { 100 | const input = readFileSync( 101 | Path.join(__dirname, "./fixture/bad/", name), 102 | "utf8", 103 | ); 104 | expect(() => check(input)).toThrow(HintError); 105 | }); 106 | } 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-duplicate-properties.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "An object contained duplicate members, making parsing ambigous: type", 4 | "line": 1 5 | }, 6 | { 7 | "message": "Feature object cannot contain a \"features\" member", 8 | "line": 1 9 | }, 10 | { 11 | "message": "\"properties\" member required", 12 | "line": 1 13 | }, 14 | { 15 | "message": "\"geometry\" member required", 16 | "line": 1 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-duplicate-properties.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "type": "Feature", 4 | "features": [] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-featurelist.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "a number was found where a coordinate array should have been found: this needs to be nested more deeply", 4 | "line": 3 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-featurelist.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Polygon", 3 | "coordinates": [5] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-json.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "line": 1, 4 | "message": "Parse error on line 2:\n...\": \"MultiPoint\" \"coordinates\": [[\"fo\n----------------------^\nExpecting 'EOF', '}', ':', ',', ']', got 'STRING'", 5 | "error": { 6 | "message": "Parse error on line 2:\n...\": \"MultiPoint\" \"coordinates\": [[\"fo\n----------------------^\nExpecting 'EOF', '}', ':', ',', ']', got 'STRING'", 7 | "hash": { 8 | "text": "\"coordinates\"", 9 | "token": "STRING", 10 | "line": 2, 11 | "loc": { 12 | "first_line": 2, 13 | "last_line": 2, 14 | "first_column": 12, 15 | "last_column": 24 16 | }, 17 | "expected": [ 18 | "'EOF'", 19 | "'}'", 20 | "':'", 21 | "','", 22 | "']'" 23 | ] 24 | } 25 | } 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-json.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "MultiPoint" 3 | "coordinates": [["foo", "bar"]] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-object-type.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "\"type\" member should be string, but is an object instead", 4 | "line": 1 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-object-type.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": {} 3 | } 4 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-polygonloop.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "geometry": { 3 | "type": "Polygon", 4 | "coordinates": [ 5 | [ 6 | -80.59869, 7 | 41.05173 8 | ], 9 | [ 10 | -80.59482, 11 | 41.21846 12 | ], 13 | [ 14 | -80.59094, 15 | 41.21862 16 | ], 17 | [ 18 | -80.58705, 19 | 41.21873 20 | ], 21 | [ 22 | -80.58315, 23 | 41.21879 24 | ], 25 | [ 26 | -80.57925, 27 | 41.2188 28 | ], 29 | [ 30 | -80.57535, 31 | 41.21875 32 | ], 33 | [ 34 | -80.57144, 35 | 41.21865 36 | ], 37 | [ 38 | -80.56752, 39 | 41.2185 40 | ], 41 | [ 42 | -80.56361, 43 | 41.2183 44 | ], 45 | [ 46 | -80.5597, 47 | 41.21804 48 | ], 49 | [ 50 | -80.55578, 51 | 41.21773 52 | ], 53 | [ 54 | -80.55187, 55 | 41.21737 56 | ], 57 | [ 58 | -80.54797, 59 | 41.21696 60 | ], 61 | [ 62 | -80.54407, 63 | 41.21649 64 | ], 65 | [ 66 | -80.54017, 67 | 41.21597 68 | ], 69 | [ 70 | -80.53629, 71 | 41.2154 72 | ], 73 | [ 74 | -80.53241, 75 | 41.21477 76 | ], 77 | [ 78 | -80.52854, 79 | 41.21409 80 | ], 81 | [ 82 | -80.52469, 83 | 41.21336 84 | ], 85 | [ 86 | -80.52085, 87 | 41.21258 88 | ], 89 | [ 90 | -80.51702, 91 | 41.21174 92 | ], 93 | [ 94 | -80.51332, 95 | 41.21064 96 | ], 97 | [ 98 | -80.50965, 99 | 41.2095 100 | ], 101 | [ 102 | -80.506, 103 | 41.2083 104 | ], 105 | [ 106 | -80.50238, 107 | 41.20706 108 | ], 109 | [ 110 | -80.4988, 111 | 41.20577 112 | ], 113 | [ 114 | -80.49524, 115 | 41.20443 116 | ], 117 | [ 118 | -80.49171, 119 | 41.20304 120 | ], 121 | [ 122 | -80.48822, 123 | 41.20161 124 | ], 125 | [ 126 | -80.48476, 127 | 41.20013 128 | ], 129 | [ 130 | -80.48134, 131 | 41.19861 132 | ], 133 | [ 134 | -80.47789, 135 | 41.19712 136 | ], 137 | [ 138 | -80.47447, 139 | 41.19559 140 | ], 141 | [ 142 | -80.47108, 143 | 41.19401 144 | ], 145 | [ 146 | -80.46773, 147 | 41.19238 148 | ], 149 | [ 150 | -80.46442, 151 | 41.19071 152 | ], 153 | [ 154 | -80.46115, 155 | 41.18899 156 | ], 157 | [ 158 | -80.45791, 159 | 41.18724 160 | ], 161 | [ 162 | -80.45472, 163 | 41.18543 164 | ], 165 | [ 166 | -80.45156, 167 | 41.18359 168 | ], 169 | [ 170 | -80.44845, 171 | 41.1817 172 | ], 173 | [ 174 | -80.44538, 175 | 41.17977 176 | ], 177 | [ 178 | -80.44236, 179 | 41.1778 180 | ], 181 | [ 182 | -80.43937, 183 | 41.17579 184 | ], 185 | [ 186 | -80.43644, 187 | 41.17374 188 | ], 189 | [ 190 | -80.43355, 191 | 41.17164 192 | ], 193 | [ 194 | -80.43071, 195 | 41.16951 196 | ], 197 | [ 198 | -80.42792, 199 | 41.16735 200 | ], 201 | [ 202 | -80.42518, 203 | 41.16514 204 | ], 205 | [ 206 | -80.42249, 207 | 41.1629 208 | ], 209 | [ 210 | -80.41985, 211 | 41.16062 212 | ], 213 | [ 214 | -80.41716, 215 | 41.15836 216 | ], 217 | [ 218 | -80.41453, 219 | 41.15606 220 | ], 221 | [ 222 | -80.41195, 223 | 41.15372 224 | ], 225 | [ 226 | -80.40943, 227 | 41.15135 228 | ], 229 | [ 230 | -80.40695, 231 | 41.14895 232 | ], 233 | [ 234 | -80.40454, 235 | 41.14651 236 | ], 237 | [ 238 | -80.40217, 239 | 41.14403 240 | ], 241 | [ 242 | -80.39986, 243 | 41.14152 244 | ], 245 | [ 246 | -80.39761, 247 | 41.13898 248 | ], 249 | [ 250 | -80.39542, 251 | 41.1364 252 | ], 253 | [ 254 | -80.3935, 255 | 41.13372 256 | ], 257 | [ 258 | -80.39163, 259 | 41.131 260 | ], 261 | [ 262 | -80.38983, 263 | 41.12827 264 | ], 265 | [ 266 | -80.38809, 267 | 41.1255 268 | ], 269 | [ 270 | -80.38642, 271 | 41.12272 272 | ], 273 | [ 274 | -80.38481, 275 | 41.11992 276 | ], 277 | [ 278 | -80.38327, 279 | 41.11709 280 | ], 281 | [ 282 | -80.38179, 283 | 41.11425 284 | ], 285 | [ 286 | -80.38038, 287 | 41.11138 288 | ], 289 | [ 290 | -80.37904, 291 | 41.1085 292 | ], 293 | [ 294 | -80.37799, 295 | 41.10555 296 | ], 297 | [ 298 | -80.377, 299 | 41.10258 300 | ], 301 | [ 302 | -80.37609, 303 | 41.09961 304 | ], 305 | [ 306 | -80.37525, 307 | 41.09663 308 | ], 309 | [ 310 | -80.37447, 311 | 41.09364 312 | ], 313 | [ 314 | -80.37377, 315 | 41.09064 316 | ], 317 | [ 318 | -80.37314, 319 | 41.08764 320 | ], 321 | [ 322 | -80.37258, 323 | 41.08463 324 | ], 325 | [ 326 | -80.3721, 327 | 41.08162 328 | ], 329 | [ 330 | -80.37168, 331 | 41.0786 332 | ], 333 | [ 334 | -80.37145, 335 | 41.07558 336 | ], 337 | [ 338 | -80.3713, 339 | 41.07255 340 | ], 341 | [ 342 | -80.37121, 343 | 41.06953 344 | ], 345 | [ 346 | -80.3712, 347 | 41.06651 348 | ], 349 | [ 350 | -80.37126, 351 | 41.06349 352 | ], 353 | [ 354 | -80.37139, 355 | 41.06048 356 | ], 357 | [ 358 | -80.37159, 359 | 41.05748 360 | ], 361 | [ 362 | -80.37187, 363 | 41.05449 364 | ], 365 | [ 366 | -80.37221, 367 | 41.0515 368 | ], 369 | [ 370 | -80.37262, 371 | 41.04853 372 | ], 373 | [ 374 | -80.37408, 375 | 41.04559 376 | ], 377 | [ 378 | -80.37562, 379 | 41.04269 380 | ], 381 | [ 382 | -80.37725, 383 | 41.03983 384 | ], 385 | [ 386 | -80.37895, 387 | 41.03702 388 | ], 389 | [ 390 | -80.38072, 391 | 41.03424 392 | ], 393 | [ 394 | -80.38258, 395 | 41.03151 396 | ], 397 | [ 398 | -80.3845, 399 | 41.02882 400 | ], 401 | [ 402 | -80.38651, 403 | 41.02618 404 | ], 405 | [ 406 | -80.38857, 407 | 41.02358 408 | ], 409 | [ 410 | -80.3893, 411 | 41.02083 412 | ], 413 | [ 414 | -80.38629, 415 | 41.01747 416 | ], 417 | [ 418 | -80.38244, 419 | 41.01385 420 | ], 421 | [ 422 | -80.37529, 423 | 41.00948 424 | ], 425 | [ 426 | -80.3636, 427 | 41.00395 428 | ], 429 | [ 430 | -80.3516, 431 | 40.99799 432 | ], 433 | [ 434 | -80.34035, 435 | 40.99182 436 | ], 437 | [ 438 | -80.33112, 439 | 40.98579 440 | ], 441 | [ 442 | -80.32527, 443 | 40.98033 444 | ], 445 | [ 446 | -80.32169, 447 | 40.97528 448 | ], 449 | [ 450 | -80.32132, 451 | 40.971 452 | ], 453 | [ 454 | -80.32489, 455 | 40.96787 456 | ], 457 | [ 458 | -80.33313, 459 | 40.96631 460 | ], 461 | [ 462 | -80.34609, 463 | 40.96653 464 | ], 465 | [ 466 | -80.36318, 467 | 40.96857 468 | ], 469 | [ 470 | -80.38569, 471 | 40.9731 472 | ], 473 | [ 474 | -80.41078, 475 | 40.97929 476 | ], 477 | [ 478 | -80.43525, 479 | 40.98601 480 | ], 481 | [ 482 | -80.44189, 483 | 40.98601 484 | ], 485 | [ 486 | -80.44487, 487 | 40.98459 488 | ], 489 | [ 490 | -80.44787, 491 | 40.98322 492 | ], 493 | [ 494 | -80.4513, 495 | 40.98211 496 | ], 497 | [ 498 | -80.45473, 499 | 40.98106 500 | ], 501 | [ 502 | -80.45815, 503 | 40.98008 504 | ], 505 | [ 506 | -80.46162, 507 | 40.97919 508 | ], 509 | [ 510 | -80.46505, 511 | 40.97835 512 | ], 513 | [ 514 | -80.46851, 515 | 40.97759 516 | ], 517 | [ 518 | -80.472, 519 | 40.97693 520 | ], 521 | [ 522 | -80.47545, 523 | 40.97632 524 | ], 525 | [ 526 | -80.47894, 527 | 40.9758 528 | ], 529 | [ 530 | -80.48243, 531 | 40.97537 532 | ], 533 | [ 534 | -80.48542, 535 | 40.97467 536 | ], 537 | [ 538 | -80.48843, 539 | 40.97405 540 | ], 541 | [ 542 | -80.49147, 543 | 40.97351 544 | ], 545 | [ 546 | -80.49448, 547 | 40.973 548 | ], 549 | [ 550 | -80.4975, 551 | 40.97257 552 | ], 553 | [ 554 | -80.50054, 555 | 40.97222 556 | ], 557 | [ 558 | -80.50357, 559 | 40.97193 560 | ], 561 | [ 562 | -80.5066, 563 | 40.9717 564 | ], 565 | [ 566 | -80.50963, 567 | 40.97156 568 | ], 569 | [ 570 | -80.51267, 571 | 40.97149 572 | ], 573 | [ 574 | -80.5148, 575 | 40.97063 576 | ], 577 | [ 578 | -80.51694, 579 | 40.96979 580 | ], 581 | [ 582 | -80.5191, 583 | 40.96899 584 | ], 585 | [ 586 | -80.52127, 587 | 40.96822 588 | ], 589 | [ 590 | -80.52346, 591 | 40.96749 592 | ], 593 | [ 594 | -80.52565, 595 | 40.96679 596 | ], 597 | [ 598 | -80.52786, 599 | 40.96612 600 | ], 601 | [ 602 | -80.53008, 603 | 40.96549 604 | ], 605 | [ 606 | -80.53231, 607 | 40.96489 608 | ], 609 | [ 610 | -80.53454, 611 | 40.96433 612 | ], 613 | [ 614 | -80.53636, 615 | 40.9632 616 | ], 617 | [ 618 | -80.53821, 619 | 40.96209 620 | ], 621 | [ 622 | -80.5401, 623 | 40.96101 624 | ], 625 | [ 626 | -80.54202, 627 | 40.95995 628 | ], 629 | [ 630 | -80.54397, 631 | 40.95891 632 | ], 633 | [ 634 | -80.54596, 635 | 40.95791 636 | ], 637 | [ 638 | -80.54797, 639 | 40.95692 640 | ], 641 | [ 642 | -80.55002, 643 | 40.95597 644 | ], 645 | [ 646 | -80.55209, 647 | 40.95504 648 | ], 649 | [ 650 | -80.5542, 651 | 40.95414 652 | ], 653 | [ 654 | -80.55617, 655 | 40.9529 656 | ], 657 | [ 658 | -80.55819, 659 | 40.95169 660 | ], 661 | [ 662 | -80.56026, 663 | 40.9505 664 | ], 665 | [ 666 | -80.56237, 667 | 40.94935 668 | ], 669 | [ 670 | -80.56452, 671 | 40.94823 672 | ], 673 | [ 674 | -80.56672, 675 | 40.94715 676 | ], 677 | [ 678 | -80.56896, 679 | 40.9461 680 | ], 681 | [ 682 | -80.57124, 683 | 40.94507 684 | ], 685 | [ 686 | -80.57355, 687 | 40.94407 688 | ], 689 | [ 690 | -80.57591, 691 | 40.9431 692 | ], 693 | [ 694 | -80.5783, 695 | 40.94217 696 | ], 697 | [ 698 | -80.58073, 699 | 40.94129 700 | ], 701 | [ 702 | -80.5832, 703 | 40.94044 704 | ], 705 | [ 706 | -80.5857, 707 | 40.93962 708 | ], 709 | [ 710 | -80.58823, 711 | 40.93882 712 | ], 713 | [ 714 | -80.5908, 715 | 40.93807 716 | ], 717 | [ 718 | -80.5934, 719 | 40.93735 720 | ], 721 | [ 722 | -80.59603, 723 | 40.93667 724 | ], 725 | [ 726 | -80.59868, 727 | 40.93604 728 | ], 729 | [ 730 | -80.60137, 731 | 40.93545 732 | ], 733 | [ 734 | -80.60413, 735 | 40.9339 736 | ], 737 | [ 738 | -80.60696, 739 | 40.93241 740 | ], 741 | [ 742 | -80.60986, 743 | 40.931 744 | ], 745 | [ 746 | -80.61283, 747 | 40.92961 748 | ], 749 | [ 750 | -80.61586, 751 | 40.92828 752 | ], 753 | [ 754 | -80.61895, 755 | 40.92704 756 | ], 757 | [ 758 | -80.62211, 759 | 40.9258 760 | ], 761 | [ 762 | -80.62532, 763 | 40.92464 764 | ], 765 | [ 766 | -80.62859, 767 | 40.92355 768 | ], 769 | [ 770 | -80.63193, 771 | 40.92248 772 | ], 773 | [ 774 | -80.63523, 775 | 40.9218 776 | ], 777 | [ 778 | -80.63856, 779 | 40.92119 780 | ], 781 | [ 782 | -80.64193, 783 | 40.92064 784 | ], 785 | [ 786 | -80.64536, 787 | 40.92009 788 | ], 789 | [ 790 | -80.64881, 791 | 40.91961 792 | ], 793 | [ 794 | -80.6523, 795 | 40.9192 796 | ], 797 | [ 798 | -80.65582, 799 | 40.91884 800 | ], 801 | [ 802 | -80.65939, 803 | 40.91848 804 | ], 805 | [ 806 | -80.66299, 807 | 40.9182 808 | ], 809 | [ 810 | -80.66661, 811 | 40.91799 812 | ], 813 | [ 814 | -80.67006, 815 | 40.91821 816 | ], 817 | [ 818 | -80.67354, 819 | 40.91845 820 | ], 821 | [ 822 | -80.67702, 823 | 40.91875 824 | ], 825 | [ 826 | -80.6805, 827 | 40.91911 828 | ], 829 | [ 830 | -80.68399, 831 | 40.91953 832 | ], 833 | [ 834 | -80.68748, 835 | 40.92 836 | ], 837 | [ 838 | -80.69098, 839 | 40.92053 840 | ], 841 | [ 842 | -80.69447, 843 | 40.92111 844 | ], 845 | [ 846 | -80.69796, 847 | 40.92176 848 | ], 849 | [ 850 | -80.70144, 851 | 40.92246 852 | ], 853 | [ 854 | -80.70508, 855 | 40.92303 856 | ], 857 | [ 858 | -80.70871, 859 | 40.92366 860 | ], 861 | [ 862 | -80.71234, 863 | 40.92436 864 | ], 865 | [ 866 | -80.71596, 867 | 40.92511 868 | ], 869 | [ 870 | -80.71958, 871 | 40.92594 872 | ], 873 | [ 874 | -80.72319, 875 | 40.92683 876 | ], 877 | [ 878 | -80.72678, 879 | 40.92778 880 | ], 881 | [ 882 | -80.73037, 883 | 40.92879 884 | ], 885 | [ 886 | -80.73394, 887 | 40.92987 888 | ], 889 | [ 890 | -80.73749, 891 | 40.93101 892 | ], 893 | [ 894 | -80.74071, 895 | 40.93247 896 | ], 897 | [ 898 | -80.7439, 899 | 40.93399 900 | ], 901 | [ 902 | -80.74706, 903 | 40.93555 904 | ], 905 | [ 906 | -80.75019, 907 | 40.93717 908 | ], 909 | [ 910 | -80.7533, 911 | 40.93883 912 | ], 913 | [ 914 | -80.75637, 915 | 40.94054 916 | ], 917 | [ 918 | -80.7594, 919 | 40.9423 920 | ], 921 | [ 922 | -80.7624, 923 | 40.94411 924 | ], 925 | [ 926 | -80.76536, 927 | 40.94597 928 | ], 929 | [ 930 | -80.76828, 931 | 40.94787 932 | ], 933 | [ 934 | -80.77091, 935 | 40.94997 936 | ], 937 | [ 938 | -80.7735, 939 | 40.9521 940 | ], 941 | [ 942 | -80.77603, 943 | 40.95428 944 | ], 945 | [ 946 | -80.77852, 947 | 40.95649 948 | ], 949 | [ 950 | -80.78096, 951 | 40.95873 952 | ], 953 | [ 954 | -80.78335, 955 | 40.96102 956 | ], 957 | [ 958 | -80.78569, 959 | 40.96333 960 | ], 961 | [ 962 | -80.78798, 963 | 40.96568 964 | ], 965 | [ 966 | -80.79022, 967 | 40.96807 968 | ], 969 | [ 970 | -80.7924, 971 | 40.97048 972 | ], 973 | [ 974 | -80.79487, 975 | 40.9728 976 | ], 977 | [ 978 | -80.79728, 979 | 40.97516 980 | ], 981 | [ 982 | -80.79964, 983 | 40.97755 984 | ], 985 | [ 986 | -80.80194, 987 | 40.97999 988 | ], 989 | [ 990 | -80.80419, 991 | 40.98247 992 | ], 993 | [ 994 | -80.80639, 995 | 40.98499 996 | ], 997 | [ 998 | -80.80852, 999 | 40.98754 1000 | ], 1001 | [ 1002 | -80.8106, 1003 | 40.99013 1004 | ], 1005 | [ 1006 | -80.81263, 1007 | 40.99275 1008 | ], 1009 | [ 1010 | -80.81459, 1011 | 40.99541 1012 | ], 1013 | [ 1014 | -80.81632, 1015 | 40.99815 1016 | ], 1017 | [ 1018 | -80.81799, 1019 | 41.00092 1020 | ], 1021 | [ 1022 | -80.81959, 1023 | 41.00371 1024 | ], 1025 | [ 1026 | -80.82113, 1027 | 41.00653 1028 | ], 1029 | [ 1030 | -80.82261, 1031 | 41.00938 1032 | ], 1033 | [ 1034 | -80.82402, 1035 | 41.01225 1036 | ], 1037 | [ 1038 | -80.82536, 1039 | 41.01515 1040 | ], 1041 | [ 1042 | -80.82663, 1043 | 41.01807 1044 | ], 1045 | [ 1046 | -80.82784, 1047 | 41.02101 1048 | ], 1049 | [ 1050 | -80.82899, 1051 | 41.02398 1052 | ], 1053 | [ 1054 | -80.8296, 1055 | 41.02701 1056 | ], 1057 | [ 1058 | -80.83014, 1059 | 41.03006 1060 | ], 1061 | [ 1062 | -80.83061, 1063 | 41.0331 1064 | ], 1065 | [ 1066 | -80.83101, 1067 | 41.03616 1068 | ], 1069 | [ 1070 | -80.83134, 1071 | 41.03922 1072 | ], 1073 | [ 1074 | -80.8316, 1075 | 41.04228 1076 | ], 1077 | [ 1078 | -80.83179, 1079 | 41.04535 1080 | ], 1081 | [ 1082 | -80.83191, 1083 | 41.04842 1084 | ], 1085 | [ 1086 | -80.83195, 1087 | 41.05149 1088 | ], 1089 | [ 1090 | -80.83193, 1091 | 41.05456 1092 | ], 1093 | [ 1094 | -80.83183, 1095 | 41.05763 1096 | ], 1097 | [ 1098 | -80.83167, 1099 | 41.0607 1100 | ], 1101 | [ 1102 | -80.83143, 1103 | 41.06376 1104 | ], 1105 | [ 1106 | -80.83112, 1107 | 41.06682 1108 | ], 1109 | [ 1110 | -80.83074, 1111 | 41.06988 1112 | ], 1113 | [ 1114 | -80.83006, 1115 | 41.07291 1116 | ], 1117 | [ 1118 | -80.82931, 1119 | 41.07593 1120 | ], 1121 | [ 1122 | -80.82848, 1123 | 41.07893 1124 | ], 1125 | [ 1126 | -80.82759, 1127 | 41.08192 1128 | ], 1129 | [ 1130 | -80.82664, 1131 | 41.0849 1132 | ], 1133 | [ 1134 | -80.82584, 1135 | 41.08789 1136 | ], 1137 | [ 1138 | -80.82497, 1139 | 41.09088 1140 | ], 1141 | [ 1142 | -80.82403, 1143 | 41.09385 1144 | ], 1145 | [ 1146 | -80.82303, 1147 | 41.09681 1148 | ], 1149 | [ 1150 | -80.82196, 1151 | 41.09975 1152 | ], 1153 | [ 1154 | -80.82082, 1155 | 41.10269 1156 | ], 1157 | [ 1158 | -80.81961, 1159 | 41.1056 1160 | ], 1161 | [ 1162 | -80.81833, 1163 | 41.1085 1164 | ], 1165 | [ 1166 | -80.81699, 1167 | 41.11138 1168 | ], 1169 | [ 1170 | -80.81558, 1171 | 41.11425 1172 | ], 1173 | [ 1174 | -80.81412, 1175 | 41.1171 1176 | ], 1177 | [ 1178 | -80.8126, 1179 | 41.11993 1180 | ], 1181 | [ 1182 | -80.81101, 1183 | 41.12274 1184 | ], 1185 | [ 1186 | -80.80936, 1187 | 41.12553 1188 | ], 1189 | [ 1190 | -80.80764, 1191 | 41.1283 1192 | ], 1193 | [ 1194 | -80.80586, 1195 | 41.13105 1196 | ], 1197 | [ 1198 | -80.80402, 1199 | 41.13377 1200 | ], 1201 | [ 1202 | -80.80211, 1203 | 41.13647 1204 | ], 1205 | [ 1206 | -80.80014, 1207 | 41.13914 1208 | ], 1209 | [ 1210 | -80.79811, 1211 | 41.14179 1212 | ], 1213 | [ 1214 | -80.79599, 1215 | 41.1444 1216 | ], 1217 | [ 1218 | -80.79382, 1219 | 41.14699 1220 | ], 1221 | [ 1222 | -80.79158, 1223 | 41.14954 1224 | ], 1225 | [ 1226 | -80.79085, 1227 | 41.15288 1228 | ], 1229 | [ 1230 | -80.79138, 1231 | 41.15697 1232 | ], 1233 | [ 1234 | -80.79229, 1235 | 41.1614 1236 | ], 1237 | [ 1238 | -80.79478, 1239 | 41.16689 1240 | ], 1241 | [ 1242 | -80.79937, 1243 | 41.17388 1244 | ], 1245 | [ 1246 | -80.80562, 1247 | 41.18222 1248 | ], 1249 | [ 1250 | -80.81232, 1251 | 41.19128 1252 | ], 1253 | [ 1254 | -80.81787, 1255 | 41.20001 1256 | ], 1257 | [ 1258 | -80.82153, 1259 | 41.20785 1260 | ], 1261 | [ 1262 | -80.82297, 1263 | 41.21444 1264 | ], 1265 | [ 1266 | -80.82322, 1267 | 41.2204 1268 | ], 1269 | [ 1270 | -80.82282, 1271 | 41.22607 1272 | ], 1273 | [ 1274 | -80.82211, 1275 | 41.2317 1276 | ], 1277 | [ 1278 | -80.82079, 1279 | 41.237 1280 | ], 1281 | [ 1282 | -80.81856, 1283 | 41.24171 1284 | ], 1285 | [ 1286 | -80.81487, 1287 | 41.24525 1288 | ], 1289 | [ 1290 | -80.80924, 1291 | 41.24703 1292 | ], 1293 | [ 1294 | -80.80169, 1295 | 41.24691 1296 | ], 1297 | [ 1298 | -80.79285, 1299 | 41.2453 1300 | ], 1301 | [ 1302 | -80.78279, 1303 | 41.24211 1304 | ], 1305 | [ 1306 | -80.77179, 1307 | 41.2375 1308 | ], 1309 | [ 1310 | -80.76044, 1311 | 41.23196 1312 | ], 1313 | [ 1314 | -80.74976, 1315 | 41.2266 1316 | ], 1317 | [ 1318 | -80.74038, 1319 | 41.2222 1320 | ], 1321 | [ 1322 | -80.73242, 1323 | 41.21907 1324 | ], 1325 | [ 1326 | -80.72538, 1327 | 41.21673 1328 | ], 1329 | [ 1330 | -80.71954, 1331 | 41.21567 1332 | ], 1333 | [ 1334 | -80.71359, 1335 | 41.21423 1336 | ], 1337 | [ 1338 | -80.708, 1339 | 41.21307 1340 | ], 1341 | [ 1342 | -80.70323, 1343 | 41.21293 1344 | ], 1345 | [ 1346 | -80.69894, 1347 | 41.21341 1348 | ], 1349 | [ 1350 | -80.6952, 1351 | 41.21476 1352 | ], 1353 | [ 1354 | -80.69193, 1355 | 41.21693 1356 | ], 1357 | [ 1358 | -80.68926, 1359 | 41.22032 1360 | ], 1361 | [ 1362 | -80.68634, 1363 | 41.22345 1364 | ], 1365 | [ 1366 | -80.68369, 1367 | 41.22735 1368 | ], 1369 | [ 1370 | -80.6805, 1371 | 41.23039 1372 | ], 1373 | [ 1374 | -80.67657, 1375 | 41.23197 1376 | ], 1377 | [ 1378 | -80.67201, 1379 | 41.23206 1380 | ], 1381 | [ 1382 | -80.66693, 1383 | 41.23069 1384 | ], 1385 | [ 1386 | -80.66194, 1387 | 41.22926 1388 | ], 1389 | [ 1390 | -80.65711, 1391 | 41.22795 1392 | ], 1393 | [ 1394 | -80.65234, 1395 | 41.2265 1396 | ], 1397 | [ 1398 | -80.64744, 1399 | 41.22423 1400 | ], 1401 | [ 1402 | -80.64236, 1403 | 41.22074 1404 | ], 1405 | [ 1406 | -80.63744, 1407 | 41.21707 1408 | ], 1409 | [ 1410 | -80.63356, 1411 | 41.21737 1412 | ], 1413 | [ 1414 | -80.6297, 1415 | 41.21771 1416 | ], 1417 | [ 1418 | -80.62583, 1419 | 41.218 1420 | ], 1421 | [ 1422 | -80.62195, 1423 | 41.21823 1424 | ], 1425 | [ 1426 | -80.61807, 1427 | 41.21842 1428 | ], 1429 | [ 1430 | -80.61419, 1431 | 41.21855 1432 | ], 1433 | [ 1434 | -80.61031, 1435 | 41.21864 1436 | ], 1437 | [ 1438 | -80.60643, 1439 | 41.21867 1440 | ], 1441 | [ 1442 | -80.60256, 1443 | 41.21865 1444 | ], 1445 | [ 1446 | -80.59868, 1447 | 41.21858 1448 | ] 1449 | ] 1450 | }, 1451 | "type": "Feature", 1452 | "properties": { 1453 | "application_id": "811", 1454 | "description": "WKTL BLED-1805", 1455 | "service": "FM" 1456 | } 1457 | } 1458 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-properties-as-array.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "\"properties\" member should be object, but is an Array instead", 4 | "line": 1 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/bad-properties-as-array.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": [], 4 | "geometry": null 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/badcoord.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "each element in a position must be a number", 4 | "line": 3 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/badcoord.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [ 1, 2, "f" ] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/badcoordtype.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "a number was found where a coordinate array should have been found: this needs to be nested more deeply", 4 | "line": 9 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/badcoordtype.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "MultiPolygon", 9 | "coordinates": [[[1]]] 10 | } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/bad/badfeature-no-properties.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", "geometry": null } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/badfeature.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Every feature must be an object", 4 | "line": 1 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/badfeature.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", "features": [null] } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/badfeatureid.cli-output-json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "message": "Feature \"id\" member must have a string or number value", 4 | "line": 4 5 | } 6 | ] 7 | -------------------------------------------------------------------------------- /test/fixture/bad/badfeatureid.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "id": {}, 7 | "properties": {}, 8 | "geometry": { 9 | "type": "Point", 10 | "coordinates": [ 11 | -100.898438, 12 | 39.368279 13 | ] 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /test/fixture/bad/bbox-4or6elements.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [2, 2], 4 | "bbox": [1, 2, 3] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/bbox-contains-string.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [2, 2], 4 | "bbox": [1, 2, 3, "4"] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/bbox-string.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [2, 2], 4 | "bbox": "string" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/different-first-last.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Polygon", 2 | "coordinates": [ 3 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0] ] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/different-first-size.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPolygon", 2 | "coordinates": [ 3 | [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 4 | [[[100.0, 0.0], [100.0, 1.0], [101.0, 1.0], [101.0, 0.0], [100.0, 0.0, 4.9]], 5 | [[100.2, 0.2], [100.2, 0.8], [100.8, 0.8], [100.8, 0.2], [100.2, 0.2]]] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/fixture/bad/expected_object.geojson: -------------------------------------------------------------------------------- 1 | {"type": "Feature", "properties": 123, "geometry": null} 2 | -------------------------------------------------------------------------------- /test/fixture/bad/false.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "GeometryCollection", 3 | "geometries": [false] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/feature_changed_semantics.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", 2 | "features": [], 3 | "coordinates": null, 4 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 5 | "properties": {"prop0": "value0"} 6 | } 7 | -------------------------------------------------------------------------------- /test/fixture/bad/featurecollection-obj.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", "features": {} } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/featurecollection-type-case.geojson: -------------------------------------------------------------------------------- 1 | { "type": "featurecollection", 2 | "features": [ 3 | { "type": "feature", 4 | "geometry": {"type": "point", "coordinates": [102.0, 0.5]}, 5 | "properties": {"prop0": "value0"} 6 | }, 7 | { "type": "feature", 8 | "geometry": { 9 | "type": "linestring", 10 | "coordinates": [ 11 | [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] 12 | ] 13 | }, 14 | "properties": { 15 | "prop0": "value0", 16 | "prop1": 0.0 17 | } 18 | }, 19 | { "type": "feature", 20 | "geometry": { 21 | "type": "polygon", 22 | "coordinates": [ 23 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], 24 | [100.0, 1.0], [100.0, 0.0] ] 25 | ] 26 | }, 27 | "properties": { 28 | "prop0": "value0", 29 | "prop1": {"this": "that"} 30 | } 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /test/fixture/bad/featurecollection.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection" } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/featurecollection_changed_semantics.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", 2 | "properties": {}, 3 | "coordinates": null, 4 | "features": [ 5 | { "type": "Feature", 6 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 7 | "properties": {"prop0": "value0"} 8 | }, 9 | { "type": "Feature", 10 | "geometry": { 11 | "type": "LineString", 12 | "coordinates": [ 13 | [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] 14 | ] 15 | }, 16 | "properties": { 17 | "prop0": "value0", 18 | "prop1": 0.0 19 | } 20 | }, 21 | { "type": "Feature", 22 | "geometry": { 23 | "type": "Polygon", 24 | "coordinates": [ 25 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] 26 | ] 27 | }, 28 | "properties": { 29 | "prop0": "value0", 30 | "prop1": {"this": "that"} 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /test/fixture/bad/geometry_changed_semantics.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Point", 2 | "features": [], 3 | "geometry": null, 4 | "properties": {}, 5 | "coordinates": [100.0, 0.0] } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/geometrycollection_nested.geojson: -------------------------------------------------------------------------------- 1 | { "type": "GeometryCollection", 2 | "geometries": [ 3 | { "type": "Point", 4 | "coordinates": [101.0, 2.0] 5 | }, 6 | { "type": "GeometryCollection", 7 | "geometries": [ 8 | { "type": "Point", 9 | "coordinates": [100.0, 0.0] 10 | }, 11 | { "type": "LineString", 12 | "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /test/fixture/bad/multiple_problems.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "id": {}, 8 | "geometry": { 9 | "type": "Point", 10 | "coordinates": [ 11 | -100.8984375, 12 | "39.36827914916011" 13 | ] 14 | } 15 | }, 16 | { 17 | "type": "Feature", 18 | "properties": {}, 19 | "geometry": { 20 | "type": "Point", 21 | "coordinates": [ 22 | 20.390625, 23 | 41.508577 24 | ] 25 | } 26 | }, 27 | { 28 | "type": "Foo", 29 | "properties": {}, 30 | "geometry": { 31 | "type": "Point", 32 | "coordinates": [ 33 | 20.390625, 34 | 41.508577 35 | ] 36 | } 37 | }, 38 | { 39 | "type": "Foo", 40 | "properties": {}, 41 | "geometry": { 42 | "type": "Point", 43 | "coordinates": "foo" 44 | } 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/fixture/bad/multipoint-multidimension.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPoint", "coordinates": [[[0,0],[0,0]]] } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/multipoint-nocoordinates.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPoint" } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/multipoint-nondimension.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPoint", "coordinates": [0,0] } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/nofeaturetype.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Featre", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -73.828125, 11 | 38.548165 12 | ] 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test/fixture/bad/notype.geojson: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /test/fixture/bad/point-labeled-as-a-multipolygon.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": { 5 | "type": "MultiPolygon", 6 | "coordinates": [ 7 | -0.087890625, 8 | 51.56341232867588 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/fixture/bad/point-string.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": ["foo", 2] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/point-toofew.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [2] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/point-toomany.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Point", 3 | "coordinates": [2, 4, 6, 8] 4 | } 5 | -------------------------------------------------------------------------------- /test/fixture/bad/point.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Point" } 2 | -------------------------------------------------------------------------------- /test/fixture/bad/rootstring.geojson: -------------------------------------------------------------------------------- 1 | null 2 | -------------------------------------------------------------------------------- /test/fixture/bad/short-line.geojson: -------------------------------------------------------------------------------- 1 | { "type": "LineString", 2 | "coordinates": [ [100.0, 0.0] ] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixture/bad/short-linearring.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Polygon", 2 | "coordinates": [ 3 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0] ] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/bad/short-multilinestring.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiLineString", 2 | "coordinates": [ 3 | [ [100.0, 0.0], [101.0, 1.0] ], 4 | [ [102.0, 2.0] ] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixture/bad/stringcoord.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -100.8984375, 11 | "39.36827914916011" 12 | ] 13 | } 14 | }, 15 | { 16 | "type": "Feature", 17 | "properties": {}, 18 | "geometry": { 19 | "type": "Point", 20 | "coordinates": [ 21 | 20.390625, 22 | 41.508577 23 | ] 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /test/fixture/bad/unknowntype.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FooBar" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixture/good/good-feature-with-id.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", 2 | "id": 100, 3 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 4 | "properties": {"prop0": "value0"} 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/good/good-feature-with-string-id.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", 2 | "id": "myfeature", 3 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 4 | "properties": {"prop0": "value0"} 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/good/good-feature.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "bbox": [102.0, 0.5, 102.0, 0.5], 4 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 5 | "properties": {"prop0": "value0"} 6 | } 7 | -------------------------------------------------------------------------------- /test/fixture/good/good-featurecollection-bbox.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", 2 | "bbox": [100.0, 0.5, 102.0, 2.5], 3 | "features": [ 4 | { "type": "Feature", 5 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 6 | "properties": {"prop0": "value0"} 7 | }, 8 | { "type": "Feature", 9 | "geometry": {"type": "Point", "coordinates": [100.0, 2.5]}, 10 | "properties": {"prop0": "value0"} 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/good/good-featurecollection-bbox3d.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", 2 | "bbox": [100.0, 0.5, 15.0, 102.0, 2.5, 25.0], 3 | "features": [ 4 | { "type": "Feature", 5 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5, 15.0]}, 6 | "properties": {"prop0": "value0"} 7 | }, 8 | { "type": "Feature", 9 | "geometry": {"type": "Point", "coordinates": [100.0, 2.5, 25.0]}, 10 | "properties": {"prop0": "value0"} 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/fixture/good/good-featurecollection-extensions.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", 2 | "custom": true, 3 | "features": [ 4 | { "type": "Feature", 5 | "geometry": { 6 | "type": "Point", 7 | "custom": true, 8 | "coordinates": [102.0, 0.5]}, 9 | "properties": {"prop0": "value0"}, 10 | "custom": true 11 | }, 12 | { "type": "Feature", 13 | "geometry": { 14 | "type": "LineString", 15 | "custom": true, 16 | "coordinates": [ 17 | [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] 18 | ] 19 | }, 20 | "properties": { 21 | "prop0": "value0", 22 | "prop1": 0.0 23 | } 24 | }, 25 | { "type": "Feature", 26 | "geometry": { 27 | "type": "Polygon", 28 | "custom": true, 29 | "coordinates": [ 30 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], 31 | [100.0, 1.0], [100.0, 0.0] ] 32 | ] 33 | }, 34 | "properties": { 35 | "prop0": "value0", 36 | "prop1": {"this": "that"} 37 | } 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /test/fixture/good/good-featurecollection.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", 2 | "features": [ 3 | { "type": "Feature", 4 | "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, 5 | "properties": {"prop0": "value0"} 6 | }, 7 | { "type": "Feature", 8 | "geometry": { 9 | "type": "LineString", 10 | "coordinates": [ 11 | [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] 12 | ] 13 | }, 14 | "properties": { 15 | "prop0": "value0", 16 | "prop1": 0.0 17 | } 18 | }, 19 | { "type": "Feature", 20 | "geometry": { 21 | "type": "Polygon", 22 | "coordinates": [ 23 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] 24 | ] 25 | }, 26 | "properties": { 27 | "prop0": "value0", 28 | "prop1": {"this": "that"} 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /test/fixture/good/good-geometrycollection.geojson: -------------------------------------------------------------------------------- 1 | { "type": "GeometryCollection", 2 | "geometries": [ 3 | { "type": "Point", 4 | "coordinates": [100.0, 0.0] 5 | }, 6 | { "type": "LineString", 7 | "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/fixture/good/good-linestring.geojson: -------------------------------------------------------------------------------- 1 | { "type": "LineString", 2 | "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixture/good/good-multilinestring.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiLineString", 2 | "coordinates": [ 3 | [ [100.0, 0.0], [101.0, 1.0] ], 4 | [ [102.0, 2.0], [103.0, 3.0] ] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixture/good/good-multipoint.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPoint", "coordinates": [[100.0, 0.0]] } 2 | -------------------------------------------------------------------------------- /test/fixture/good/good-point-3d.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Point", "coordinates": [100.0, 0.0, 15.0] } 2 | -------------------------------------------------------------------------------- /test/fixture/good/good-point.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Point", "coordinates": [100.0, 0.0] } 2 | -------------------------------------------------------------------------------- /test/fixture/good/good-polygon.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Polygon", 2 | "coordinates": [ 3 | [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] ] 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/fixture/good/multipolygon.geojson: -------------------------------------------------------------------------------- 1 | { "type": "MultiPolygon", 2 | "coordinates": [ 3 | [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], 4 | [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], 5 | [[100.2, 0.2], [100.2, 0.8], [100.8, 0.8], [100.8, 0.2], [100.2, 0.2]]] 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test/fixture/good/nullgeometry.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", "features": [{ 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": null 5 | }] } 6 | -------------------------------------------------------------------------------- /test/fixture/good/nullproperties.geojson: -------------------------------------------------------------------------------- 1 | { "type": "FeatureCollection", "features": [{ 2 | "type": "Feature", 3 | "properties": null, 4 | "geometry": { 5 | "type": "Point", 6 | "coordinates": [0,0] 7 | } 8 | }] } 9 | -------------------------------------------------------------------------------- /test/good.test.ts: -------------------------------------------------------------------------------- 1 | import { check } from '../lib'; 2 | import { describe, expect, it } from 'vitest'; 3 | import * as Path from 'path'; 4 | import { readFileSync, readdirSync } from 'fs'; 5 | 6 | const examplePoint = { 7 | type: 'Point', 8 | coordinates: [0, 1], 9 | }; 10 | const exampleFeature = { 11 | type: 'Feature', 12 | properties: { 13 | test: true, 14 | }, 15 | geometry: examplePoint, 16 | }; 17 | const exampleFeatureCollection = { 18 | type: 'FeatureCollection', 19 | features: [exampleFeature], 20 | }; 21 | describe('check', () => { 22 | it('works with fixed examples', () => { 23 | const examples = [ 24 | examplePoint, 25 | exampleFeature, 26 | exampleFeatureCollection, 27 | { 28 | type: 'Polygon', 29 | coordinates: [ 30 | [ 31 | [100.0, 0.0], 32 | [101.0, 0.0], 33 | [101.0, 1.0], 34 | [100.0, 1.0], 35 | [100.0, 0.0], 36 | ], 37 | [ 38 | [100.8, 0.8], 39 | [100.8, 0.2], 40 | [100.2, 0.2], 41 | [100.2, 0.8], 42 | [100.8, 0.8], 43 | ], 44 | ], 45 | }, 46 | { 47 | type: 'MultiPoint', 48 | coordinates: [ 49 | [100.0, 0.0], 50 | [101.0, 1.0], 51 | ], 52 | }, 53 | { 54 | type: 'MultiLineString', 55 | coordinates: [ 56 | [ 57 | [100.0, 0.0], 58 | [101.0, 1.0], 59 | ], 60 | [ 61 | [102.0, 2.0], 62 | [103.0, 3.0], 63 | ], 64 | ], 65 | }, 66 | { 67 | type: 'MultiPolygon', 68 | coordinates: [ 69 | [ 70 | [ 71 | [102.0, 2.0], 72 | [103.0, 2.0], 73 | [103.0, 3.0], 74 | [102.0, 3.0], 75 | [102.0, 2.0], 76 | ], 77 | ], 78 | [ 79 | [ 80 | [100.0, 0.0], 81 | [101.0, 0.0], 82 | [101.0, 1.0], 83 | [100.0, 1.0], 84 | [100.0, 0.0], 85 | ], 86 | [ 87 | [100.2, 0.2], 88 | [100.2, 0.8], 89 | [100.8, 0.8], 90 | [100.8, 0.2], 91 | [100.2, 0.2], 92 | ], 93 | ], 94 | ], 95 | }, 96 | { 97 | type: 'GeometryCollection', 98 | geometries: [ 99 | { 100 | type: 'Point', 101 | coordinates: [100.0, 0.0], 102 | }, 103 | { 104 | type: 'LineString', 105 | coordinates: [ 106 | [101.0, 0.0], 107 | [102.0, 1.0], 108 | ], 109 | }, 110 | ], 111 | }, 112 | ]; 113 | for (const obj of examples) { 114 | check(JSON.stringify(obj)); 115 | expect(check(JSON.stringify(obj))).toEqual(obj); 116 | } 117 | }); 118 | describe('works with fixtures', () => { 119 | const fixtureNames = readdirSync(Path.join(__dirname, './fixture/good/')); 120 | for (const name of fixtureNames) { 121 | it(`fixture: ${name}`, () => { 122 | const input = readFileSync( 123 | Path.join(__dirname, './fixture/good/', name), 124 | 'utf8' 125 | ); 126 | expect(check(input)).toEqual(JSON.parse(input)); 127 | }); 128 | } 129 | }); 130 | }); 131 | -------------------------------------------------------------------------------- /test/scavenge.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | import { scavenge } from '../lib'; 3 | 4 | describe('scavenge', () => { 5 | it('partially correct fc', () => { 6 | expect( 7 | scavenge( 8 | JSON.stringify({ 9 | type: 'FeatureCollection', 10 | 11 | features: [ 12 | { 13 | type: 'Feature', 14 | geometry: { 15 | type: 'Point', 16 | coordinates: [0, 0], 17 | }, 18 | properties: {}, 19 | }, 20 | { 21 | type: 'bad', 22 | }, 23 | ], 24 | }) 25 | ) 26 | ).toEqual({ 27 | result: { 28 | type: 'FeatureCollection', 29 | features: [ 30 | { 31 | type: 'Feature', 32 | geometry: { 33 | type: 'Point', 34 | coordinates: [0, 0], 35 | }, 36 | properties: {}, 37 | }, 38 | ], 39 | }, 40 | rejected: [ 41 | { 42 | feature: { 43 | type: 'bad', 44 | }, 45 | reasons: [ 46 | { 47 | from: 123, 48 | message: 'This type of GeoJSON object is not allowed here.', 49 | severity: 'error', 50 | to: 137, 51 | }, 52 | { 53 | from: 123, 54 | message: 55 | 'This GeoJSON object requires a geometry member but it is missing.', 56 | severity: 'error', 57 | to: 137, 58 | }, 59 | { 60 | from: 123, 61 | message: 62 | 'This GeoJSON object requires a properties member but it is missing.', 63 | severity: 'error', 64 | to: 137, 65 | }, 66 | { 67 | from: 123, 68 | message: 'The properties member is missing.', 69 | severity: 'error', 70 | to: 137, 71 | }, 72 | ], 73 | }, 74 | ], 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["lib", "types"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "target": "es6", 7 | "lib": ["dom", "esnext"], 8 | "importHelpers": true, 9 | // output .d.ts declaration files for consumers 10 | "declaration": true, 11 | // output .js.map sourcemap files for consumers 12 | "sourceMap": true, 13 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 14 | "rootDir": "./lib", 15 | // stricter type-checking for stronger correctness. Recommended by TS 16 | "strict": true, 17 | // linter checks for common issues 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 21 | "noUnusedLocals": true, 22 | // "noUnusedParameters": true, 23 | // use Node's module resolution algorithm, instead of the legacy TS one 24 | "moduleResolution": "node", 25 | // transpile JSX to React.createElement 26 | "jsx": "preserve", 27 | // interop between ESM and CJS modules. Recommended by TS 28 | "esModuleInterop": true, 29 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 30 | "skipLibCheck": true, 31 | // error out if import and file system have a casing mismatch. Recommended by TS 32 | "forceConsistentCasingInFileNames": true, 33 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 34 | "noEmit": true, 35 | "downlevelIteration": true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "entryPoints": ["./lib/index.ts"], 3 | "entryPointStrategy":"Expand", 4 | "out": "docs" 5 | } 6 | -------------------------------------------------------------------------------- /types/momoa.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@humanwhocodes/momoa' { 2 | import { JSONValue } from 'type-fest'; 3 | 4 | type ParseOptions = { 5 | ranges?: boolean; 6 | comments?: boolean; 7 | tokens?: boolean; 8 | }; 9 | 10 | export type Loc = { 11 | line: number; 12 | column: number; 13 | offset: number; 14 | }; 15 | 16 | export type LocRange = { 17 | start: Loc; 18 | end: Loc; 19 | }; 20 | 21 | export type Range = [number, number]; 22 | 23 | interface BaseNode { 24 | loc: LocRange; 25 | range: { 26 | range: Range; 27 | }; 28 | } 29 | 30 | export interface DocumentNode extends BaseNode { 31 | type: 'Document'; 32 | body: Node; 33 | } 34 | 35 | export interface BooleanNode extends BaseNode { 36 | type: 'Boolean'; 37 | value: boolean; 38 | } 39 | 40 | export interface NumberNode extends BaseNode { 41 | type: 'Number'; 42 | value: number; 43 | } 44 | 45 | export interface NullNode extends BaseNode { 46 | type: 'Null'; 47 | value: null; 48 | } 49 | 50 | export interface StringNode extends BaseNode { 51 | type: 'String'; 52 | value: string; 53 | } 54 | 55 | export interface ObjectNode extends BaseNode { 56 | type: 'Object'; 57 | name: StringNode; 58 | members: MemberNode[]; 59 | } 60 | 61 | export interface ArrayNode extends BaseNode { 62 | type: 'Array'; 63 | elements: Node[]; 64 | } 65 | 66 | export interface MemberNode extends BaseNode { 67 | type: 'Member'; 68 | name: StringNode; 69 | value: Node; 70 | } 71 | 72 | type Node = 73 | | MemberNode 74 | | ObjectNode 75 | | StringNode 76 | | NumberNode 77 | | DocumentNode 78 | | NullNode 79 | | ArrayNode; 80 | 81 | type Phase = 'enter' | 'exit'; 82 | 83 | interface IteratorState { 84 | node: Node; 85 | parent: Node; 86 | phase: Phase; 87 | } 88 | 89 | export function parse(jsonStr: string, options?: ParseOptions): DocumentNode; 90 | export function iterator(ast: Node): Generator; 91 | export function evaluate(ast: Node): JSONValue; 92 | } 93 | --------------------------------------------------------------------------------