├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── demo ├── bundle.min.js ├── bundle.min.js.map ├── demo.css ├── demo.js ├── favicon.png └── polys.parquet ├── eslint.config.js ├── geoparquet.jpg ├── index.html ├── package.json ├── rollup.config.js ├── src ├── geojson.d.ts ├── index.js ├── toGeoJson.js └── wkb.js ├── test ├── files │ ├── compressed │ │ ├── example.brotli.parquet │ │ ├── example.gzip.parquet │ │ ├── example.snappy.parquet │ │ ├── example.uncompressed.parquet │ │ └── example.zstd.parquet │ ├── data-linestring-encoding_wkb.json │ ├── data-linestring-encoding_wkb.parquet │ ├── data-multilinestring-encoding_wkb.json │ ├── data-multilinestring-encoding_wkb.parquet │ ├── data-multipoint-encoding_wkb.json │ ├── data-multipoint-encoding_wkb.parquet │ ├── example.json │ ├── example.parquet │ ├── polys.json │ └── polys.parquet ├── package.test.js ├── toGeoJson.test.js └── wkb.test.js └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.min.js -diff 2 | *.min.js.map -diff 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - run: npm i 12 | - run: npm run build 13 | 14 | lint: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: npm i 19 | - run: npm run lint 20 | 21 | typecheck: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - run: npm i 26 | - run: npx tsc 27 | 28 | test: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v4 32 | - run: npm i 33 | - run: npm run coverage 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | coverage 4 | *.tgz 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoParquet 2 | 3 | ![GeoParquet parakeets gathered around a map](geoparquet.jpg) 4 | 5 | [![npm](https://img.shields.io/npm/v/geoparquet)](https://www.npmjs.com/package/geoparquet) 6 | [![minzipped](https://img.shields.io/bundlephobia/minzip/geoparquet)](https://www.npmjs.com/package/geoparquet) 7 | [![workflow status](https://github.com/hyparam/geoparquet/actions/workflows/ci.yml/badge.svg)](https://github.com/hyparam/geoparquet/actions) 8 | [![mit license](https://img.shields.io/badge/License-MIT-orange.svg)](https://opensource.org/licenses/MIT) 9 | ![coverage](https://img.shields.io/badge/Coverage-95-darkred) 10 | [![dependencies](https://img.shields.io/badge/Dependencies-1-blueviolet)](https://www.npmjs.com/package/geoparquet?activeTab=dependencies) 11 | 12 | **GeoParquet** provides a pure JavaScript workflow to read and convert [GeoParquet](https://github.com/opengeospatial/geoparquet) files into [GeoJSON](https://datatracker.ietf.org/doc/html/rfc7946). Under the hood, it uses the [hyparquet](https://github.com/hyparam/hyparquet) library for efficient in-browser parquet parsing, enabling minimal overhead and fast loading. 13 | 14 | ## Why GeoParquet? 15 | 16 | **GeoParquet** is an emerging standard for storing geospatial vector data in the columnar [Parquet](https://parquet.apache.org/) format. By leveraging Parquet’s columnar storage, GeoParquet files can be significantly more compact and faster to query than their GeoJSON equivalents, especially for large datasets. This can result in: 17 | 18 | - **Smaller file sizes:** A GeoParquet file plus this lightweight library can often be much smaller than the equivalent GeoJSON, reducing bandwidth costs and improving load times. 19 | - **Faster loading:** Pure JavaScript parsing of Parquet files in the browser, with tiny package size means fast page load times. 20 | - **Seamless integration:** GeoParquet is based on well-established standards (GeoJSON, Parquet), ensuring broad compatibility and extensibility. 21 | 22 | ## Usage 23 | 24 | Convert a GeoParquet file to GeoJSON: 25 | 26 | ```javascript 27 | const { asyncBufferFromUrl, toGeoJson } = await import('geoparquet') 28 | 29 | const file = asyncBufferFromUrl({ url: 'example.parquet' }) 30 | const geoJson = toGeoJson({ file }) 31 | ``` 32 | 33 | ## Demo 34 | 35 | Check out the [live demo](https://hyparam.github.io/geoparquet/) to see GeoParquet in action. 36 | 37 | ## Parquet Compression Formats 38 | 39 | By default, geoparquet.js supports the most common parquet compression formats: uncompressed and snappy compression. 40 | To enable support for other compression codecs, such as gzip, brotli, zstd, etc you can use the `compressors` option. 41 | The `hyparquet-compressors` package adds support for all parquet compression codecs: 42 | 43 | ```javascript 44 | import { asyncBufferFromFile, toGeoJson } from 'geoparquet' 45 | import { compressors } from 'hyparquet-compressors' 46 | 47 | const file = asyncBufferFromFile('example.zstd.parquet') 48 | const geoJson = toGeoJson({ file, compressors }) 49 | ``` 50 | 51 | ## References 52 | 53 | - https://geoparquet.org/ 54 | - https://github.com/opengeospatial/geoparquet 55 | - https://geojson.org/ 56 | - https://datatracker.ietf.org/doc/html/rfc7946 57 | - https://github.com/hyparam/hyparquet 58 | - https://github.com/hyparam/hyparquet-compressors 59 | - https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry 60 | -------------------------------------------------------------------------------- /demo/bundle.min.js: -------------------------------------------------------------------------------- 1 | const e=["BOOLEAN","INT32","INT64","INT96","FLOAT","DOUBLE","BYTE_ARRAY","FIXED_LEN_BYTE_ARRAY"],t=["PLAIN",void 0,"PLAIN_DICTIONARY","RLE","BIT_PACKED","DELTA_BINARY_PACKED","DELTA_LENGTH_BYTE_ARRAY","DELTA_BYTE_ARRAY","RLE_DICTIONARY","BYTE_STREAM_SPLIT"],n=["REQUIRED","OPTIONAL","REPEATED"],r=["UTF8","MAP","MAP_KEY_VALUE","LIST","ENUM","DECIMAL","DATE","TIME_MILLIS","TIME_MICROS","TIMESTAMP_MILLIS","TIMESTAMP_MICROS","UINT_8","UINT_16","UINT_32","UINT_64","INT_8","INT_16","INT_32","INT_64","JSON","BSON","INTERVAL"],i=["UNCOMPRESSED","SNAPPY","GZIP","LZO","BROTLI","LZ4","ZSTD","LZ4_RAW"],o=["DATA_PAGE","INDEX_PAGE","DICTIONARY_PAGE","DATA_PAGE_V2"],f=864e5;function s(e,t,n,r,i=!0){if(t&&r.endsWith("_DICTIONARY")){t=l(t,n,i);let r=e;e instanceof Uint8Array&&!(t instanceof Uint8Array)&&(r=new t.constructor(e.length));for(let n=0;nJSON.parse(t.decode(e))))}if("BSON"===r)throw new Error("parquet bson not supported");if("INTERVAL"===r)throw new Error("parquet interval not supported");if("UTF8"===r||n&&"BYTE_ARRAY"===t.type){const t=new TextDecoder,n=new Array(e.length);for(let r=0;r>64n)-2440588n),n=Number((0xffffffffffffffffn&e)/1000000n);return new Date(t*f+n)}function u(e){if(!e)return;const t=e[1]<<8|e[0],n=t>>15?-1:1,r=t>>10&31,i=1023&t;return 0===r?n*Math.pow(2,-14)*(i/1024):31===r?i?NaN:n*(1/0):n*Math.pow(2,r-15)*(1+i/1024)}function d(e,t,n){const r=e[t],i=[];let o=1;if(r.num_children)for(;i.lengtht.element.name===e));if(!i)throw new Error(`parquet schema element not found: ${t}`);r.push(i),n=i}return r}function p(e){let t=0;for(const{element:n}of e)"REPEATED"===n.repetition_type&&t++;return t}function w(e){let t=0;for(const{element:n}of e.slice(1))"REQUIRED"!==n.repetition_type&&t++;return t}const g=0,h=1,y=2,m=3,E=4,A=5,I=6,v=7,b=8,T=9,L=12,N=13;function D(e){let t=0;const n={};for(;e.offset>>1^-(1&t)}(e);case I:return U(e);case v:{const t=e.view.getFloat64(e.offset,!0);return e.offset+=8,t}case b:{const t=O(e),n=new Uint8Array(e.view.buffer,e.view.byteOffset+e.offset,t);return e.offset+=t,n}case T:{const[t,n]=function(e){const t=e.view.getUint8(e.offset++),n=t>>4,r=P(t);if(15===n){return[r,O(e)]}return[r,n]}(e),r=t===h||t===y,i=new Array(n);for(let o=0;o>BigInt(1)^-(t&BigInt(1))}function P(e){return 15&e}function M(e,t){const n=e.view.getUint8(e.offset++);if((15&n)===g)return[0,0,t];const r=n>>4;let i;if(!r)throw new Error("non-delta field id not supported");return i=t+r,[P(n),i,i]}async function S(e,t=1<<19){if(!e)throw new Error("parquet file is required");if(!(e.byteLength>=0))throw new Error("parquet file byteLength is required");const n=Math.max(0,e.byteLength-t),r=await e.slice(n,e.byteLength),i=new DataView(r);if(827474256!==i.getUint32(r.byteLength-4,!0))throw new Error("parquet file invalid (footer != PAR1)");const o=i.getUint32(r.byteLength-8,!0);if(o>e.byteLength-8)throw new Error(`parquet metadata length ${o} exceeds available buffer ${e.byteLength-8}`);if(o+8>t){const t=e.byteLength-o-8,i=await e.slice(t,n),f=new ArrayBuffer(o+8),s=new Uint8Array(f);return s.set(new Uint8Array(i)),s.set(new Uint8Array(r),n-t),B(f)}return B(r)}function B(f){if(!f)throw new Error("parquet file is required");const s=new DataView(f);if(s.byteLength<8)throw new Error("parquet file is too short");if(827474256!==s.getUint32(s.byteLength-4,!0))throw new Error("parquet file invalid (footer != PAR1)");const l=s.byteLength-8,a=s.getUint32(l,!0);if(a>s.byteLength-8)throw new Error(`parquet metadata length ${a} exceeds available buffer ${s.byteLength-8}`);const c=D({view:s,offset:l-a}),u=new TextDecoder;function d(e){return e&&u.decode(e)}const _=c.field_1,p=c.field_2.map((t=>({type:e[t.field_1],type_length:t.field_2,repetition_type:n[t.field_3],name:d(t.field_4),num_children:t.field_5,converted_type:r[t.field_6],scale:t.field_7,precision:t.field_8,field_id:t.field_9,logical_type:q(t.field_10)}))),w=p.filter((e=>e.type)),g=c.field_3,h=c.field_4.map((n=>({columns:n.field_1.map(((n,r)=>({file_path:d(n.field_1),file_offset:n.field_2,meta_data:n.field_3&&{type:e[n.field_3.field_1],encodings:n.field_3.field_2?.map((e=>t[e])),path_in_schema:n.field_3.field_3.map(d),codec:i[n.field_3.field_4],num_values:n.field_3.field_5,total_uncompressed_size:n.field_3.field_6,total_compressed_size:n.field_3.field_7,key_value_metadata:n.field_3.field_8,data_page_offset:n.field_3.field_9,index_page_offset:n.field_3.field_10,dictionary_page_offset:n.field_3.field_11,statistics:Y(n.field_3.field_12,w[r]),encoding_stats:n.field_3.field_13?.map((e=>({page_type:o[e.field_1],encoding:t[e.field_2],count:e.field_3}))),bloom_filter_offset:n.field_3.field_14,bloom_filter_length:n.field_3.field_15,size_statistics:n.field_3.field_16&&{unencoded_byte_array_data_bytes:n.field_3.field_16.field_1,repetition_level_histogram:n.field_3.field_16.field_2,definition_level_histogram:n.field_3.field_16.field_3}},offset_index_offset:n.field_4,offset_index_length:n.field_5,column_index_offset:n.field_6,column_index_length:n.field_7,crypto_metadata:n.field_7,encrypted_column_metadata:n.field_8}))),total_byte_size:n.field_2,num_rows:n.field_3,sorting_columns:n.field_4?.map((e=>({column_idx:e.field_1,descending:e.field_2,nulls_first:e.field_3}))),file_offset:n.field_5,total_compressed_size:n.field_6,ordinal:n.field_7}))),y=c.field_5?.map((e=>({key:d(e.field_1),value:d(e.field_2)})));return{version:_,schema:p,num_rows:g,row_groups:h,key_value_metadata:y,created_by:d(c.field_6),metadata_length:a}}function q(e){return e?.field_1?{type:"STRING"}:e?.field_2?{type:"MAP"}:e?.field_3?{type:"LIST"}:e?.field_4?{type:"ENUM"}:e?.field_5?{type:"DECIMAL",scale:e.field_5.field_1,precision:e.field_5.field_2}:e?.field_6?{type:"DATE"}:e?.field_7?{type:"TIME",isAdjustedToUTC:e.field_7.field_1,unit:$(e.field_7.field_2)}:e?.field_8?{type:"TIMESTAMP",isAdjustedToUTC:e.field_8.field_1,unit:$(e.field_8.field_2)}:e?.field_10?{type:"INTEGER",bitWidth:e.field_10.field_1,isSigned:e.field_10.field_2}:e?.field_11?{type:"NULL"}:e?.field_12?{type:"JSON"}:e?.field_13?{type:"BSON"}:e?.field_14?{type:"UUID"}:e?.field_15?{type:"FLOAT16"}:e}function $(e){if(e.field_1)return"MILLIS";if(e.field_2)return"MICROS";if(e.field_3)return"NANOS";throw new Error("parquet time unit required")}function Y(e,t){return e&&{max:F(e.field_1,t),min:F(e.field_2,t),null_count:e.field_3,distinct_count:e.field_4,max_value:F(e.field_5,t),min_value:F(e.field_6,t),is_max_value_exact:e.field_7,is_min_value_exact:e.field_8}}function F(e,t){const{type:n,converted_type:r,logical_type:i}=t;if(void 0===e)return e;if("BOOLEAN"===n)return 1===e[0];if("BYTE_ARRAY"===n)return(new TextDecoder).decode(e);const o=new DataView(e.buffer,e.byteOffset,e.byteLength);return"FLOAT"===n&&4===o.byteLength?o.getFloat32(0,!0):"DOUBLE"===n&&8===o.byteLength?o.getFloat64(0,!0):"INT32"===n&&"DATE"===r?new Date(864e5*o.getInt32(0,!0)):"INT64"===n&&"TIMESTAMP_MICROS"===r?new Date(Number(o.getBigInt64(0,!0)/1000n)):"INT64"===n&&"TIMESTAMP_MILLIS"===r?new Date(Number(o.getBigInt64(0,!0))):"INT64"===n&&"TIMESTAMP"===i?.type&&"NANOS"===i?.unit?new Date(Number(o.getBigInt64(0,!0)/1000000n)):"INT64"===n&&"TIMESTAMP"===i?.type&&"MICROS"===i?.unit?new Date(Number(o.getBigInt64(0,!0)/1000n)):"INT64"===n&&"TIMESTAMP"===i?.type?new Date(Number(o.getBigInt64(0,!0))):"INT32"===n&&4===o.byteLength?o.getInt32(0,!0):"INT64"===n&&8===o.byteLength?o.getBigInt64(0,!0):"DECIMAL"===r?a(e)*Math.pow(10,-(t.scale||0)):"FLOAT16"===i?.type?u(e):e}function C(e,t,n,r,i,o){const f=t?.length||n.length;let s=0;const l=[e];let a=e,c=0,u=0,d=0;if(n[0])for(;c1)return!1;const t=e.children[0];return!(t.children.length>1)&&"REPEATED"===t.element.repetition_type}(t)){let f=t.children[0],s=o;1===f.children.length&&(f=f.children[0],s++),x(e,f,s);const l=f.path.join("."),a=e.get(l);if(!a)throw new Error("parquet list column missing values");return i&&k(a,n),e.set(r,a),void e.delete(l)}if(function(e){if(!e)return!1;if("MAP"!==e.element.converted_type)return!1;if(e.children.length>1)return!1;const t=e.children[0];if(2!==t.children.length)return!1;if("REPEATED"!==t.element.repetition_type)return!1;const n=t.children.find((e=>"key"===e.element.name));if("REPEATED"===n?.element.repetition_type)return!1;const r=t.children.find((e=>"value"===e.element.name));return"REPEATED"!==r?.element.repetition_type}(t)){const f=t.children[0].element.name;x(e,t.children[0].children[0],o+1),x(e,t.children[0].children[1],o+1);const s=e.get(`${r}.${f}.key`),l=e.get(`${r}.${f}.value`);if(!s)throw new Error("parquet map column missing keys");if(!l)throw new Error("parquet map column missing values");if(s.length!==l.length)throw new Error("parquet map column key/value length mismatch");const a=G(s,l,o);return i&&k(a,n),e.delete(`${r}.${f}.key`),e.delete(`${r}.${f}.value`),void e.set(r,a)}if(t.children.length){const o="REQUIRED"===t.element.repetition_type?n:n+1,f={};for(const n of t.children){x(e,n,o);const t=e.get(n.path.join("."));if(!t)throw new Error("parquet struct missing child data");f[n.element.name]=t}for(const n of t.children)e.delete(n.path.join("."));const s=z(f,o);i&&k(s,n),e.set(r,s)}}function k(e,t){for(let n=0;n>a&u;for(a+=o;a>=8;)a-=8n,e.offset++,a&&(t|=BigInt(e.view.getUint8(e.offset))<>>1;Q(e,o,t,r,i),i+=o}}}function Q(e,t,n,r,i){const o=n+7>>3;let f=0;for(let t=0;t>1<<3;const f=(1<8?(a-=8,l-=8,s>>>=8):l-a>a&f),o--,a+=n);return i}function Z(e,t,n,r){const i=function(e,t){switch(e){case"INT32":case"FLOAT":return 4;case"INT64":case"DOUBLE":return 8;case"FIXED_LEN_BYTE_ARRAY":if(!t)throw new Error("parquet byteWidth missing type_length");return t;default:throw new Error(`parquet unsupported type: ${e}`)}}(n,r),o=new Uint8Array(t*i);for(let n=0;n1){const r=p(n);if(r){const n=new Array(t.num_values);return J(e,j(r),0,n),n}}return[]}(o,t,n),{definitionLevels:l,numNulls:a}=function(e,t,n){const r=w(n);if(!r)return{definitionLevels:[],numNulls:0};const i=new Array(t.num_values);J(e,j(r),0,i);let o=t.num_values;for(const e of i)e===r&&o--;0===o&&(i.length=0);return{definitionLevels:i,numNulls:o}}(o,t,n),c=t.num_values-a;if("PLAIN"===t.encoding){const{type_length:e}=n[n.length-1].element;f=H(o,r,c,e)}else if("PLAIN_DICTIONARY"===t.encoding||"RLE_DICTIONARY"===t.encoding||"RLE"===t.encoding){const e="BOOLEAN"===r?1:i.getUint8(o.offset++);e?(f=new Array(c),J(o,e,i.byteLength-o.offset,f)):f=new Uint8Array(c)}else{if("BYTE_STREAM_SPLIT"!==t.encoding)throw new Error(`parquet unsupported encoding: ${t.encoding}`);{const{type_length:e}=n[n.length-1].element;f=Z(o,c,r,e)}}return{definitionLevels:l,repetitionLevels:s,dataPage:f}}function re(e,t,n,r){return H({view:new DataView(e.buffer,e.byteOffset,e.byteLength),offset:0},n.type,t.num_values,r)}function ie(e,t,n,r){let i;const o=r?.[n];if("UNCOMPRESSED"===n)i=e;else if(o)i=o(e,t);else{if("SNAPPY"!==n)throw new Error(`parquet unsupported compression codec: ${n}`);i=new Uint8Array(t),function(e,t){const n=e.byteLength,r=t.byteLength;let i=0,o=0;for(;i=n)throw new Error("invalid snappy length header");for(;i=n)throw new Error("missing eof marker");if(3&r){let s=0;switch(3&r){case 1:f=4+(r>>>2&7),s=e[i]+(r>>>5<<8),i++;break;case 2:if(n<=i+1)throw new Error("snappy error end of input");f=1+(r>>>2),s=e[i]+(e[i+1]<<8),i+=2;break;case 3:if(n<=i+3)throw new Error("snappy error end of input");f=1+(r>>>2),s=e[i]+(e[i+1]<<8)+(e[i+2]<<16)+(e[i+3]<<24),i+=4}if(0===s||isNaN(s))throw new Error(`invalid offset ${s} pos ${i} inputLength ${n}`);if(s>o)throw new Error("cannot copy from before start of buffer");te(t,o,s,f),o+=f}else{let f=1+(r>>>2);if(f>60){if(i+3>=n)throw new Error("snappy error literal pos + 3 >= inputLength");const t=f-60;f=e[i]+(e[i+1]<<8)+(e[i+2]<<16)+(e[i+3]<<24),f=1+(f&W[t]),i+=t}if(i+f>n)throw new Error("snappy error literal exceeds input length");ee(e,i,t,o,f),i+=f,o+=f}}if(o!==r)throw new Error("premature end of input")}(e,i)}if(i?.length!==t)throw new Error(`parquet decompressed page length ${i?.length} does not match header ${t}`);return i}function oe(e,t,n,r,i){const o={view:new DataView(e.buffer,e.byteOffset,e.byteLength),offset:0},{codec:f,type:s}=r,l=t.data_page_header_v2;if(!l)throw new Error("parquet data page header v2 is undefined");const a=function(e,t,n){const r=p(n);if(!r)return[];const i=new Array(t.num_values);return J(e,j(r),t.repetition_levels_byte_length,i),i}(o,l,n);o.offset=l.repetition_levels_byte_length;const c=function(e,t,n){const r=w(n);if(r){const n=new Array(t.num_values);return J(e,j(r),t.definition_levels_byte_length,n),n}}(o,l,n),u=t.uncompressed_page_size-l.definition_levels_byte_length-l.repetition_levels_byte_length;let d=e.subarray(o.offset);!1!==l.is_compressed&&(d=ie(d,u,f,i));const _=new DataView(d.buffer,d.byteOffset,d.byteLength),g={view:_,offset:0};let h;const y=l.num_values-l.num_nulls;if("PLAIN"===l.encoding){const{type_length:e}=n[n.length-1].element;h=H(g,s,y,e)}else if("RLE"===l.encoding)h=new Array(y),J(g,1,0,h),h=h.map((e=>!!e));else if("PLAIN_DICTIONARY"===l.encoding||"RLE_DICTIONARY"===l.encoding){const e=_.getUint8(g.offset++);h=new Array(y),J(g,e,u-1,h)}else if("DELTA_BINARY_PACKED"===l.encoding){h="INT32"===s?new Int32Array(y):new BigInt64Array(y),V(g,y,h)}else if("DELTA_LENGTH_BYTE_ARRAY"===l.encoding)h=new Array(y),function(e,t,n){const r=new Int32Array(t);V(e,t,r);for(let i=0;i{if(!e.ok)throw new Error(`fetch head failed ${e.status}`);const t=e.headers.get("Content-Length");if(!t)throw new Error("missing content length");return parseInt(t)}))}(e,n);const r=n||{};return{byteLength:t,async slice(t,n){const i=new Headers(r.headers),o=void 0===n?"":n-1;i.set("Range",`bytes=${t}-${o}`);const f=await fetch(e,{...r,headers:i});if(!f.ok||!f.body)throw new Error(`fetch failed ${f.status}`);return f.arrayBuffer()}}}function le(e,t,n,r,{compressors:i,utf8:o}){const{element:f}=r[r.length-1];let l;const a=[],c=void 0!==t&&t>=0&&isFinite(t);for(;(!c||a.length=e.view.byteLength-1);){const t=ce(e),c=new Uint8Array(e.view.buffer,e.view.byteOffset+e.offset,t.compressed_page_size);let u;if("DATA_PAGE"===t.type){const e=t.data_page_header;if(!e)throw new Error("parquet data page header is undefined");const d=ie(c,Number(t.uncompressed_page_size),n.codec,i),{definitionLevels:_,repetitionLevels:p,dataPage:g}=ne(d,e,r,n);if(u=s(g,l,f,e.encoding,o),p.length||_?.length){const e=w(r),t=r.map((({element:e})=>e.repetition_type));C(a,_,p,u,t,e)}else{for(let e=2;e[e])));fe(a,u)}}else if("DATA_PAGE_V2"===t.type){const e=t.data_page_header_v2;if(!e)throw new Error("parquet data page header v2 is undefined");const{definitionLevels:d,repetitionLevels:_,dataPage:p}=oe(c,t,r,n,i);if(u=s(p,l,f,e.encoding,o),_.length||d?.length){const e=w(r),t=r.map((({element:e})=>e.repetition_type));C(a,d,_,u,t,e)}else fe(a,u)}else{if("DICTIONARY_PAGE"!==t.type)throw new Error(`parquet unsupported page type: ${t.type}`);{const e=t.dictionary_page_header;if(!e)throw new Error("parquet dictionary page header is undefined");l=re(ie(c,Number(t.uncompressed_page_size),n.codec,i),e,n,f.type_length)}}e.offset+=t.compressed_page_size}if(c){if(a.lengtht&&(a.length=t)}return a}function ae({dictionary_page_offset:e,data_page_offset:t,total_compressed_size:n}){let r=e;return(!r||tt.num_rows)&&(r=Number(t.num_rows));let s,[l,a]=[i.byteLength,0];if(t.columns.forEach((({meta_data:e})=>{if(!e)throw new Error("parquet column metadata is undefined");if(f&&!f.includes(e.path_in_schema[0]))return;const[t,n]=ae(e).map(Number);l=Math.min(l,t),a=Math.max(a,n)})),l>=a&&f?.length)throw new Error(`parquet columns not found: ${f.join(", ")}`);a-l<=1<<25&&(s=await i.slice(l,a));const c=[],{children:u}=_(o.schema,[])[0],d=new Map(u.map((e=>[e.element.name,de(e)]))),p=new Map;for(let a=0;a1<<30){console.warn(`parquet skipping huge column "${u.path_in_schema}" ${y.toLocaleString()} bytes`);continue}let m,E=0;s?(m=Promise.resolve(s),E=g-l):m=Promise.resolve(i.slice(g,h)),c.push(m.then((t=>{const i=_(o.schema,u.path_in_schema);let f=le({view:new DataView(t),offset:E},r,u,i,e);const s=u.path_in_schema.join(".");p.set(s,f),f=void 0;const l=d.get(w);if(l?.every((e=>p.has(e)))&&(x(p,i[1]),f=p.get(w),!f))throw new Error(`parquet column data not assembled: ${w}`);f&&e.onChunk?.({columnName:w,columnData:f,rowStart:n,rowEnd:n+f.length})})))}if(await Promise.all(c),e.onComplete){const t=new Array(r),n=u.map((e=>e.element.name)).filter((e=>!f||f.includes(e))),i=f||n,o=i.map((e=>n.includes(e)?p.get(e):void 0));for(let n=0;n{e[t]=o[r]?.[n]})),t[n]=e}else t[n]=o.map((e=>e?.[n]));return t}return[]}function de(e,t=[]){if(e.children.length)for(const n of e.children)de(n,t);else t.push(e.path.join("."));return t}async function _e(e){const{file:t,rowStart:n,rowEnd:r,orderBy:i}=e;if(e.metadata||=await S(t),"string"==typeof i){const t=await pe({...e,rowStart:void 0,rowEnd:void 0,columns:[i]}),o=Array.from(t,((e,t)=>t)).sort(((e,n)=>function(e,t){return eo+=Number(e.num_rows)));for(const e of n){i[f.findIndex((t=>ef[e]))}return await pe(e)}function pe(e){return new Promise(((t,n)=>{(async function(e){if(!e.file)throw new Error("parquet file is required");if(e.metadata||=await S(e.file),!e.metadata)throw new Error("parquet metadata not found");const{metadata:t,onComplete:n,rowEnd:r}=e,i=e.rowStart||0,o=[];let f=0;for(const s of t.row_groups){const t=Number(s.num_rows);if(f+t>=i&&(void 0===r||f"geo"===e.key));if(!r)throw new Error('Invalid GeoParquet file: missing "geo" metadata');const i=JSON.parse(r.value||"{}"),o=await _e({file:e,utf8:!1,compressors:t}),f=[],s=i.primary_column||"geometry";for(const e of o){const t=e[s];if(!t)continue;const n=we(t),r={};for(const t of Object.keys(e)){let n=e[t];if(t!==s&&null!==n){try{n=JSON.parse(n)}catch(e){}r[t]=n}}const i={type:"Feature",geometry:n,properties:r};f.push(i)}return{type:"FeatureCollection",features:f}}({file:e});console.log("GeoJSON:",n),t.data.addGeoJson(n)}catch(e){console.error("Error loading or parsing GeoParquet file:",e)}}(); 2 | //# sourceMappingURL=bundle.min.js.map 3 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | /* Full-screen map styling */ 2 | html, body { 3 | height: 100%; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | #map { 9 | height: 100%; 10 | width: 100%; 11 | } 12 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | import { asyncBufferFromUrl } from 'hyparquet' 2 | import { toGeoJson } from '../src/index.js' 3 | 4 | async function initMap() { 5 | // @ts-expect-error MapsLibrary 6 | const { Map } = await google.maps.importLibrary('maps') 7 | const div = /** @type {HTMLElement} */(document.getElementById('map')) 8 | // Create a new map 9 | const map = new Map(div, { 10 | center: { lat: 39, lng: -98 }, 11 | zoom: 4, 12 | }) 13 | 14 | // URL or path to your GeoParquet file 15 | const parquetUrl = 'demo/polys.parquet' 16 | 17 | try { 18 | // Read the GeoParquet file and convert to GeoJSON 19 | const file = await asyncBufferFromUrl({ url: parquetUrl }) 20 | const geojson = await toGeoJson({ file }) 21 | 22 | console.log('GeoJSON:', geojson) 23 | 24 | // Add the GeoJSON data to the map 25 | map.data.addGeoJson(geojson) 26 | } catch (error) { 27 | console.error('Error loading or parsing GeoParquet file:', error) 28 | } 29 | } 30 | initMap() 31 | -------------------------------------------------------------------------------- /demo/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/demo/favicon.png -------------------------------------------------------------------------------- /demo/polys.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/demo/polys.parquet -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import javascript from '@eslint/js' 2 | 3 | export default [ 4 | { 5 | ignores: ['coverage/', 'demo/bundle.min.js'], 6 | }, 7 | { 8 | files: ['**/*.js'], 9 | 10 | languageOptions: { 11 | globals: { 12 | // for demo: 13 | console: 'readonly', 14 | document: 'readonly', 15 | google: 'readonly', 16 | window: 'readonly', 17 | }, 18 | }, 19 | 20 | rules: { 21 | ...javascript.configs.recommended.rules, 22 | 'arrow-spacing': 'error', 23 | camelcase: 'off', 24 | 'comma-spacing': 'error', 25 | 'comma-dangle': ['error', { 26 | arrays: 'always-multiline', 27 | objects: 'always-multiline', 28 | imports: 'always-multiline', 29 | exports: 'always-multiline', 30 | functions: 'never', 31 | }], 32 | 'eol-last': 'error', 33 | eqeqeq: 'error', 34 | 'func-style': ['error', 'declaration'], 35 | indent: ['error', 2], 36 | 'no-constant-condition': 'off', 37 | 'no-extra-parens': 'warn', 38 | 'no-multi-spaces': 'error', 39 | 'no-trailing-spaces': 'error', 40 | 'no-undef': 'error', 41 | 'no-unused-vars': 'off', 42 | 'no-useless-concat': 'error', 43 | 'no-useless-rename': 'error', 44 | 'no-useless-return': 'error', 45 | 'no-var': 'error', 46 | 'object-curly-spacing': ['error', 'always'], 47 | 'prefer-const': 'warn', 48 | 'prefer-destructuring': ['warn', { 49 | object: true, 50 | array: false, 51 | }], 52 | 'prefer-promise-reject-errors': 'error', 53 | quotes: ['error', 'single'], 54 | 'require-await': 'warn', 55 | semi: ['error', 'never'], 56 | 57 | 'sort-imports': ['error', { 58 | ignoreDeclarationSort: true, 59 | ignoreMemberSort: false, 60 | memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], 61 | }], 62 | 63 | 'space-infix-ops': 'error', 64 | }, 65 | }, 66 | ] 67 | -------------------------------------------------------------------------------- /geoparquet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/geoparquet.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GeoParquet + Google Maps Demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geoparquet", 3 | "version": "0.3.0", 4 | "description": "A library for reading geospatial data in Apache Parquet format", 5 | "author": "Hyperparam", 6 | "homepage": "https://hyperparam.app", 7 | "keywords": [ 8 | "geo", 9 | "geojson", 10 | "geoparquet", 11 | "geospatial", 12 | "gis", 13 | "parquet" 14 | ], 15 | "license": "MIT", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/hyparam/geoparquet.git" 19 | }, 20 | "dependencies": { 21 | "hyparquet": "1.7.0" 22 | }, 23 | "devDependencies": { 24 | "@rollup/plugin-node-resolve": "16.0.0", 25 | "@rollup/plugin-terser": "0.4.4", 26 | "@types/google.maps": "3.58.1", 27 | "@types/node": "22.10.2", 28 | "@vitest/coverage-v8": "2.1.8", 29 | "eslint": "9.17.0", 30 | "http-server": "14.1.1", 31 | "hyparquet-compressors": "1.0.0", 32 | "rollup": "4.29.0", 33 | "typescript": "5.7.2", 34 | "vitest": "2.1.8" 35 | }, 36 | "files": [ 37 | "src" 38 | ], 39 | "main": "src/index.js", 40 | "scripts": { 41 | "build": "rollup -c", 42 | "coverage": "vitest run --coverage --coverage.include=src", 43 | "lint": "eslint", 44 | "start": "http-server", 45 | "test": "vitest run" 46 | }, 47 | "type": "module" 48 | } 49 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve' 2 | import terser from '@rollup/plugin-terser' 3 | 4 | export default { 5 | input: 'demo/demo.js', 6 | output: { 7 | file: 'demo/bundle.min.js', 8 | name: 'geoparquet', 9 | sourcemap: true, 10 | }, 11 | plugins: [ 12 | resolve(), 13 | terser(), 14 | ], 15 | } 16 | -------------------------------------------------------------------------------- /src/geojson.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The valid values for the "type" property of any GeoJSON object. 3 | */ 4 | export type GeoJsonTypes = 5 | | "FeatureCollection" 6 | | "Feature" 7 | | "Point" 8 | | "MultiPoint" 9 | | "LineString" 10 | | "MultiLineString" 11 | | "Polygon" 12 | | "MultiPolygon" 13 | | "GeometryCollection" 14 | 15 | /** 16 | * A Position is an array of numbers. There must be at least two elements, 17 | * and the order should be [longitude, latitude] with optional additional 18 | * elements representing altitude or other properties. 19 | */ 20 | export type Position = [number, number] | [number, number, number] | [number, number, number, number] | number[] 21 | 22 | /** 23 | * Bounding box array: [west, south, east, north], or [west, south, east, north, min elevation, max elevation]. 24 | * Note: RFC 7946 recommends the order [minLon, minLat, maxLon, maxLat]. 25 | */ 26 | export type BBox = [number, number, number, number] | [number, number, number, number, number, number] 27 | 28 | /** 29 | * GeoJSON objects may contain a "bbox" member, which is an array describing the bounding box of the feature. 30 | * They may also contain a "crs" member, though CRS is deprecated in RFC 7946. 31 | */ 32 | export interface GeoJsonObject { 33 | type: GeoJsonTypes 34 | bbox?: BBox 35 | } 36 | 37 | /** 38 | * A GeometryObject represents a geometry type in GeoJSON: 39 | * Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, or GeometryCollection. 40 | */ 41 | export type Geometry = 42 | | Point 43 | | MultiPoint 44 | | LineString 45 | | MultiLineString 46 | | Polygon 47 | | MultiPolygon 48 | | GeometryCollection 49 | 50 | /** 51 | * A Point geometry: { type: "Point", coordinates: Position } 52 | */ 53 | export interface Point extends GeoJsonObject { 54 | type: "Point" 55 | coordinates: Position 56 | } 57 | 58 | /** 59 | * A MultiPoint geometry: { type: "MultiPoint", coordinates: Position[] } 60 | */ 61 | export interface MultiPoint extends GeoJsonObject { 62 | type: "MultiPoint" 63 | coordinates: Position[] 64 | } 65 | 66 | /** 67 | * A LineString geometry: { type: "LineString", coordinates: Position[] } 68 | * According to RFC 7946, a LineString must have two or more positions. 69 | */ 70 | export interface LineString extends GeoJsonObject { 71 | type: "LineString" 72 | coordinates: Position[] 73 | } 74 | 75 | /** 76 | * A MultiLineString geometry: { type: "MultiLineString", coordinates: Position[][] } 77 | * Each element in coordinates represents one LineString. 78 | */ 79 | export interface MultiLineString extends GeoJsonObject { 80 | type: "MultiLineString" 81 | coordinates: Position[][] 82 | } 83 | 84 | /** 85 | * A Polygon geometry: { type: "Polygon", coordinates: Position[][] } 86 | * Each element in coordinates represents a linear ring, which must have at least 4 positions forming a closed ring. 87 | */ 88 | export interface Polygon extends GeoJsonObject { 89 | type: "Polygon" 90 | coordinates: Position[][] 91 | } 92 | 93 | /** 94 | * A MultiPolygon geometry: { type: "MultiPolygon", coordinates: Position[][][] } 95 | * Each element in coordinates represents one Polygon. 96 | */ 97 | export interface MultiPolygon extends GeoJsonObject { 98 | type: "MultiPolygon" 99 | coordinates: Position[][][] 100 | } 101 | 102 | /** 103 | * A GeometryCollection geometry: { type: "GeometryCollection", geometries: Geometry[] } 104 | * Contains multiple geometries. 105 | */ 106 | export interface GeometryCollection extends GeoJsonObject { 107 | type: "GeometryCollection" 108 | geometries: Geometry[] 109 | } 110 | 111 | /** 112 | * A Feature represents a spatially bounded entity. 113 | * It must have a "geometry" (or null) and a "properties" object. 114 | */ 115 | export interface Feature> extends GeoJsonObject { 116 | type: "Feature" 117 | geometry: G | null 118 | properties: P | null 119 | id?: string | number 120 | } 121 | 122 | /** 123 | * A FeatureCollection is a set of Features. 124 | */ 125 | export interface FeatureCollection> extends GeoJsonObject { 126 | type: "FeatureCollection" 127 | features: Array> 128 | } 129 | 130 | /** 131 | * A type union that includes any valid GeoJSON object type. 132 | */ 133 | export type GeoJSON = 134 | | Feature 135 | | FeatureCollection 136 | | Geometry 137 | | Point 138 | | MultiPoint 139 | | LineString 140 | | MultiLineString 141 | | Polygon 142 | | MultiPolygon 143 | | GeometryCollection 144 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { asyncBufferFromFile, asyncBufferFromUrl } from 'hyparquet' 2 | export { toGeoJson } from './toGeoJson.js' 3 | -------------------------------------------------------------------------------- /src/toGeoJson.js: -------------------------------------------------------------------------------- 1 | import { parquetMetadataAsync, parquetQuery } from 'hyparquet' 2 | import { decodeWKB } from './wkb.js' 3 | 4 | /** 5 | * Convert a GeoParquet file to GeoJSON. 6 | * Input is an AsyncBuffer representing a GeoParquet file. 7 | * An AsyncBuffer is a buffer-like object that can be read asynchronously. 8 | * 9 | * @import { AsyncBuffer, Compressors } from 'hyparquet' 10 | * @import { Feature, GeoJSON } from './geojson.js' 11 | * @param {Object} options 12 | * @param {AsyncBuffer} options.file 13 | * @param {Compressors} [options.compressors] 14 | * @returns {Promise} 15 | */ 16 | export async function toGeoJson({ file, compressors }) { 17 | const metadata = await parquetMetadataAsync(file) 18 | const geoMetadata = metadata.key_value_metadata?.find(kv => kv.key === 'geo') 19 | if (!geoMetadata) { 20 | throw new Error('Invalid GeoParquet file: missing "geo" metadata') 21 | } 22 | 23 | // Geoparquet metadata 24 | const geoSchema = JSON.parse(geoMetadata.value || '{}') 25 | 26 | // Read all parquet data 27 | const data = await parquetQuery({ file, utf8: false, compressors }) 28 | 29 | /** @type {Feature[]} */ 30 | const features = [] 31 | const primaryColumn = geoSchema.primary_column || 'geometry' 32 | for (const row of data) { 33 | const wkb = row[primaryColumn] 34 | if (!wkb) { 35 | // No geometry 36 | continue 37 | } 38 | 39 | const geometry = decodeWKB(wkb) 40 | 41 | // Extract properties (all fields except geometry) 42 | /** @type {Record} */ 43 | const properties = {} 44 | for (const key of Object.keys(row)) { 45 | let value = row[key] 46 | if (key !== primaryColumn && value !== null) { 47 | try { 48 | // Try to parse JSON 49 | value = JSON.parse(value) 50 | } catch (error) { 51 | // Ignore 52 | } 53 | properties[key] = value 54 | } 55 | } 56 | 57 | /** @type {Feature} */ 58 | const feature = { 59 | type: 'Feature', 60 | geometry, 61 | properties, 62 | } 63 | 64 | features.push(feature) 65 | } 66 | 67 | return { 68 | type: 'FeatureCollection', 69 | features, 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/wkb.js: -------------------------------------------------------------------------------- 1 | 2 | const geometryTypePoint = 1 3 | const geometryTypeLineString = 2 4 | const geometryTypePolygon = 3 5 | const geometryTypeMultiPoint = 4 6 | const geometryTypeMultiLineString = 5 7 | const geometryTypeMultiPolygon = 6 8 | const geometryTypeGeometryCollection = 7 9 | const geometryTypeCircularString = 8 10 | const geometryTypeCompoundCurve = 9 11 | const geometryTypeCurvePolygon = 10 12 | const geometryTypeMultiCurve = 11 13 | const geometryTypeMultiSurface = 12 14 | const geometryTypeCurve = 13 15 | const geometryTypeSurface = 14 16 | const geometryTypePolyhedralSurface = 15 17 | const geometryTypeTIN = 16 18 | const geometryTypeTriangle = 17 19 | const geometryTypeCircle = 18 20 | const geometryTypeGeodesicString = 19 21 | const geometryTypeEllipticalCurve = 20 22 | const geometryTypeNurbsCurve = 21 23 | const geometryTypeClothoid = 22 24 | const geometryTypeSpiralCurve = 23 25 | const geometryTypeCompoundSurface = 24 26 | 27 | /** 28 | * WKB (Well Known Binary) decoder for geometry objects. 29 | * 30 | * @import { Geometry } from './geojson.js' 31 | * @param {Uint8Array} wkb 32 | * @returns {Geometry} GeoJSON geometry object 33 | */ 34 | export function decodeWKB(wkb) { 35 | const dv = new DataView(wkb.buffer, wkb.byteOffset, wkb.byteLength) 36 | let offset = 0 37 | 38 | // Byte order: 0 = big-endian, 1 = little-endian 39 | const byteOrder = wkb[offset]; offset += 1 40 | const isLittleEndian = byteOrder === 1 41 | 42 | // Read geometry type 43 | const geometryType = dv.getUint32(offset, isLittleEndian) 44 | offset += 4 45 | 46 | // WKB geometry types (OGC): 47 | if (geometryType === geometryTypePoint) { 48 | // Point 49 | const x = dv.getFloat64(offset, isLittleEndian); offset += 8 50 | const y = dv.getFloat64(offset, isLittleEndian); offset += 8 51 | return { type: 'Point', coordinates: [x, y] } 52 | } else if (geometryType === geometryTypeLineString) { 53 | // LineString 54 | const numPoints = dv.getUint32(offset, isLittleEndian); offset += 4 55 | const coords = [] 56 | for (let i = 0; i < numPoints; i++) { 57 | const x = dv.getFloat64(offset, isLittleEndian); offset += 8 58 | const y = dv.getFloat64(offset, isLittleEndian); offset += 8 59 | coords.push([x, y]) 60 | } 61 | return { type: 'LineString', coordinates: coords } 62 | } else if (geometryType === geometryTypePolygon) { 63 | // Polygon 64 | const numRings = dv.getUint32(offset, isLittleEndian); offset += 4 65 | const coords = [] 66 | for (let r = 0; r < numRings; r++) { 67 | const numPoints = dv.getUint32(offset, isLittleEndian); offset += 4 68 | const ring = [] 69 | for (let p = 0; p < numPoints; p++) { 70 | const x = dv.getFloat64(offset, isLittleEndian); offset += 8 71 | const y = dv.getFloat64(offset, isLittleEndian); offset += 8 72 | ring.push([x, y]) 73 | } 74 | coords.push(ring) 75 | } 76 | return { type: 'Polygon', coordinates: coords } 77 | } else if (geometryType === geometryTypeMultiPolygon) { 78 | // MultiPolygon 79 | const numPolygons = dv.getUint32(offset, isLittleEndian); offset += 4 80 | const polygons = [] 81 | for (let i = 0; i < numPolygons; i++) { 82 | // Each polygon has its own byte order & geometry type 83 | const polyIsLittleEndian = wkb[offset] === 1; offset += 1 84 | const polyType = dv.getUint32(offset, polyIsLittleEndian); offset += 4 85 | if (polyType !== geometryTypePolygon) { 86 | throw new Error(`Expected Polygon in MultiPolygon, got ${polyType}`) 87 | } 88 | const numRings = dv.getUint32(offset, polyIsLittleEndian); offset += 4 89 | 90 | const pgCoords = [] 91 | for (let r = 0; r < numRings; r++) { 92 | const numPoints = dv.getUint32(offset, polyIsLittleEndian); offset += 4 93 | const ring = [] 94 | for (let p = 0; p < numPoints; p++) { 95 | const x = dv.getFloat64(offset, polyIsLittleEndian); offset += 8 96 | const y = dv.getFloat64(offset, polyIsLittleEndian); offset += 8 97 | ring.push([x, y]) 98 | } 99 | pgCoords.push(ring) 100 | } 101 | polygons.push(pgCoords) 102 | } 103 | return { type: 'MultiPolygon', coordinates: polygons } 104 | } else if (geometryType === geometryTypeMultiPoint) { 105 | // MultiPoint 106 | const numPoints = dv.getUint32(offset, isLittleEndian); offset += 4 107 | const points = [] 108 | for (let i = 0; i < numPoints; i++) { 109 | // Each point has its own byte order & geometry type 110 | const pointIsLittleEndian = wkb[offset] === 1; offset += 1 111 | const pointType = dv.getUint32(offset, pointIsLittleEndian); offset += 4 112 | if (pointType !== geometryTypePoint) { 113 | throw new Error(`Expected Point in MultiPoint, got ${pointType}`) 114 | } 115 | const x = dv.getFloat64(offset, pointIsLittleEndian); offset += 8 116 | const y = dv.getFloat64(offset, pointIsLittleEndian); offset += 8 117 | points.push([x, y]) 118 | } 119 | return { type: 'MultiPoint', coordinates: points } 120 | } else if (geometryType === geometryTypeMultiLineString) { 121 | // MultiLineString 122 | const numLineStrings = dv.getUint32(offset, isLittleEndian); offset += 4 123 | const lineStrings = [] 124 | for (let i = 0; i < numLineStrings; i++) { 125 | // Each line has its own byte order & geometry type 126 | const lineIsLittleEndian = wkb[offset] === 1; offset += 1 127 | const lineType = dv.getUint32(offset, lineIsLittleEndian); offset += 4 128 | if (lineType !== geometryTypeLineString) { 129 | throw new Error(`Expected LineString in MultiLineString, got ${lineType}`) 130 | } 131 | const numPoints = dv.getUint32(offset, isLittleEndian); offset += 4 132 | const coords = [] 133 | for (let p = 0; p < numPoints; p++) { 134 | const x = dv.getFloat64(offset, lineIsLittleEndian); offset += 8 135 | const y = dv.getFloat64(offset, lineIsLittleEndian); offset += 8 136 | coords.push([x, y]) 137 | } 138 | lineStrings.push(coords) 139 | } 140 | return { type: 'MultiLineString', coordinates: lineStrings } 141 | } else { 142 | throw new Error(`Unsupported geometry type: ${geometryType}`) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /test/files/compressed/example.brotli.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/compressed/example.brotli.parquet -------------------------------------------------------------------------------- /test/files/compressed/example.gzip.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/compressed/example.gzip.parquet -------------------------------------------------------------------------------- /test/files/compressed/example.snappy.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/compressed/example.snappy.parquet -------------------------------------------------------------------------------- /test/files/compressed/example.uncompressed.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/compressed/example.uncompressed.parquet -------------------------------------------------------------------------------- /test/files/compressed/example.zstd.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/compressed/example.zstd.parquet -------------------------------------------------------------------------------- /test/files/data-linestring-encoding_wkb.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "features": [ 4 | { 5 | "geometry": { 6 | "coordinates": [ 7 | [ 8 | 30, 9 | 10 10 | ], 11 | [ 12 | 10, 13 | 30 14 | ], 15 | [ 16 | 40, 17 | 40 18 | ] 19 | ], 20 | "type": "LineString" 21 | }, 22 | "properties": { 23 | "col": 0 24 | }, 25 | "type": "Feature" 26 | }, 27 | { 28 | "geometry": { 29 | "coordinates": [], 30 | "type": "LineString" 31 | }, 32 | "properties": { 33 | "col": 1 34 | }, 35 | "type": "Feature" 36 | } 37 | ], 38 | "type": "FeatureCollection" 39 | } 40 | -------------------------------------------------------------------------------- /test/files/data-linestring-encoding_wkb.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/data-linestring-encoding_wkb.parquet -------------------------------------------------------------------------------- /test/files/data-multilinestring-encoding_wkb.json: -------------------------------------------------------------------------------- 1 | { 2 | "features": [ 3 | { 4 | "geometry": { 5 | "coordinates": [ 6 | [ 7 | [ 30, 10 ], 8 | [ 10, 30 ], 9 | [ 40, 40 ] 10 | ] 11 | ], 12 | "type": "MultiLineString" 13 | }, 14 | "properties": { 15 | "col": 0 16 | }, 17 | "type": "Feature" 18 | }, 19 | { 20 | "geometry": { 21 | "coordinates": [ 22 | [ 23 | [ 10, 10 ], 24 | [ 20, 20 ], 25 | [ 10, 40 ] 26 | ], 27 | [ 28 | [ 40, 40 ], 29 | [ 30, 30 ], 30 | [ 40, 20 ], 31 | [ 30, 10 ] 32 | ] 33 | ], 34 | "type": "MultiLineString" 35 | }, 36 | "properties": { 37 | "col": 1 38 | }, 39 | "type": "Feature" 40 | }, 41 | { 42 | "geometry": { 43 | "coordinates": [], 44 | "type": "MultiLineString" 45 | }, 46 | "properties": { 47 | "col": 2 48 | }, 49 | "type": "Feature" 50 | } 51 | ], 52 | "type": "FeatureCollection" 53 | } -------------------------------------------------------------------------------- /test/files/data-multilinestring-encoding_wkb.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/data-multilinestring-encoding_wkb.parquet -------------------------------------------------------------------------------- /test/files/data-multipoint-encoding_wkb.json: -------------------------------------------------------------------------------- 1 | { 2 | "features": [ 3 | { 4 | "geometry": { 5 | "coordinates": [ 6 | [ 30, 10 ] 7 | ], 8 | "type": "MultiPoint" 9 | }, 10 | "properties": { 11 | "col": 0 12 | }, 13 | "type": "Feature" 14 | }, 15 | { 16 | "geometry": { 17 | "coordinates": [ 18 | [ 10, 40 ], 19 | [ 40, 30 ], 20 | [ 20, 20 ], 21 | [ 30, 10 ] 22 | ], 23 | "type": "MultiPoint" 24 | }, 25 | "properties": { 26 | "col": 1 27 | }, 28 | "type": "Feature" 29 | }, 30 | { 31 | "geometry": { 32 | "coordinates": [], 33 | "type": "MultiPoint" 34 | }, 35 | "properties": { 36 | "col": 2 37 | }, 38 | "type": "Feature" 39 | } 40 | ], 41 | "type": "FeatureCollection" 42 | } 43 | -------------------------------------------------------------------------------- /test/files/data-multipoint-encoding_wkb.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/data-multipoint-encoding_wkb.parquet -------------------------------------------------------------------------------- /test/files/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "Point", 8 | "coordinates": [102, 0.5] 9 | }, 10 | "properties": { 11 | "prop0": "value0" 12 | } 13 | }, 14 | { 15 | "type": "Feature", 16 | "geometry": { 17 | "type": "LineString", 18 | "coordinates": [ 19 | [102, 0], [103, 1], [104.0, 0], [105, 1] 20 | ] 21 | }, 22 | "properties": { 23 | "prop0": "value0", 24 | "prop1": 0 25 | } 26 | }, 27 | { 28 | "type": "Feature", 29 | "geometry": { 30 | "type": "Polygon", 31 | "coordinates": [ 32 | [ 33 | [100, 0], [101, 0], [101, 1], [100, 1], [100, 0] 34 | ] 35 | ] 36 | }, 37 | "properties": { 38 | "prop0": "value0", 39 | "prop1": { 40 | "this": "that" 41 | } 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /test/files/example.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/example.parquet -------------------------------------------------------------------------------- /test/files/polys.json: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"geometry":{"type":"MultiPolygon","coordinates":[[[[180,-16.067132663642447],[180,-16.555216566639196],[179.36414266196414,-16.801354076946883],[178.72505936299711,-17.01204167436804],[178.59683859511713,-16.639150000000004],[179.0966093629971,-16.433984277547403],[179.4135093629971,-16.379054277547404],[180,-16.067132663642447]]],[[[178.12557,-17.50481],[178.3736,-17.33992],[178.71806,-17.62846],[178.55271,-18.15059],[177.93266000000003,-18.28799],[177.38146,-18.16432],[177.28504,-17.72465],[177.67087,-17.381140000000002],[178.12557,-17.50481]]],[[[-179.79332010904864,-16.020882256741224],[-179.9173693847653,-16.501783135649397],[-180,-16.555216566639196],[-180,-16.067132663642447],[-179.79332010904864,-16.020882256741224]]]]},"properties":{"bbox":{"xmax":180,"xmin":-180,"ymax":-16.020882256741224,"ymin":-18.28799},"continent":"Oceania","gdp_md_est":5496,"iso_a3":"FJI","name":"Fiji","pop_est":889953},"type":"Feature"} 2 | ,{"geometry":{"type":"Polygon","coordinates":[[[33.90371119710453,-0.9500000000000001],[34.07261999999997,-1.0598199999999451],[37.69868999999994,-3.0969899999999484],[37.7669,-3.6771200000000004],[39.20222,-4.67677],[38.74053999999995,-5.9089499999999475],[38.79977000000008,-6.475660000000005],[39.44,-6.839999999999861],[39.47000000000014,-7.099999999999966],[39.19468999999998,-7.703899999999976],[39.25203000000005,-8.00780999999995],[39.18652000000009,-8.48550999999992],[39.53574000000009,-9.112369999999885],[39.94960000000003,-10.098400000000026],[40.316586229110854,-10.317097752817492],[40.31659000000002,-10.317099999999868],[39.52099999999996,-10.89688000000001],[38.42755659358775,-11.285202325081656],[37.827639999999974,-11.26878999999991],[37.471289999999954,-11.568759999999997],[36.775150994622805,-11.594537448780805],[36.51408165868426,-11.720938002166735],[35.31239790216904,-11.439146416879147],[34.55998904799935,-11.520020033415925],[34.27999999999997,-10.160000000000025],[33.940837724096525,-9.693673841980285],[33.73972000000009,-9.417149999999992],[32.75937544122132,-9.23059905358906],[32.19186486179194,-8.930358981973257],[31.556348097466497,-8.762048841998642],[31.15775133695005,-8.594578747317366],[30.740009731422095,-8.34000593035372],[30.74001549655179,-8.340007419470915],[30.199996779101696,-7.079980970898163],[29.620032179490014,-6.520015150583426],[29.419992710088167,-5.939998874539434],[29.519986606572928,-5.419978936386315],[29.339997592900346,-4.4999834122940925],[29.753512404099865,-4.452389418153302],[30.11632000000003,-4.090120000000013],[30.505539999999996,-3.5685799999999404],[30.752240000000086,-3.3593099999999936],[30.743010000000027,-3.034309999999948],[30.527660000000026,-2.807619999999986],[30.469673645761223,-2.41385475710134],[30.469670000000008,-2.4138299999999617],[30.75830895358311,-2.2872502579883687],[30.816134881317712,-1.6989140763453887],[30.419104852019245,-1.1346591121504161],[30.769860000000108,-1.0145499999999856],[31.866170000000068,-1.0273599999999306],[33.90371119710453,-0.9500000000000001]]]},"properties":{"bbox":{"xmax":40.31659000000002,"xmin":29.339997592900346,"ymax":-0.9500000000000001,"ymin":-11.720938002166735},"continent":"Africa","gdp_md_est":63177,"iso_a3":"TZA","name":"Tanzania","pop_est":58005463},"type":"Feature"} 3 | ,{"geometry":{"type":"Polygon","coordinates":[[[-8.665589565454809,27.656425889592356],[-8.665124477564191,27.589479071558227],[-8.684399786809053,27.395744126896005],[-8.6872936670174,25.881056219988906],[-11.96941891117116,25.933352769468268],[-11.937224493853321,23.374594224536168],[-12.874221564169575,23.284832261645178],[-13.118754441774712,22.771220201096256],[-12.929101935263532,21.327070624267563],[-16.845193650773993,21.33332347257488],[-17.06342322434257,20.999752102130827],[-17.02042843267577,21.422310288981578],[-17.00296179856109,21.420734157796577],[-14.750954555713534,21.500600083903663],[-14.630832688851072,21.860939846274903],[-14.221167771857253,22.31016307218816],[-13.891110398809047,23.691009019459305],[-12.50096269372537,24.7701162785782],[-12.03075883630163,26.030866197203068],[-11.718219773800357,26.104091701760623],[-11.392554897497007,26.883423977154393],[-10.551262579785273,26.990807603456886],[-10.189424200877582,26.860944729107405],[-9.735343390328879,26.860944729107405],[-9.41303748212448,27.088476060488574],[-8.794883999049077,27.120696316022507],[-8.817828334986672,27.656425889592356],[-8.665589565454809,27.656425889592356]]]},"properties":{"bbox":{"xmax":-8.665124477564191,"xmin":-17.06342322434257,"ymax":27.656425889592356,"ymin":20.999752102130827},"continent":"Africa","gdp_md_est":907,"iso_a3":"ESH","name":"W. Sahara","pop_est":603253},"type":"Feature"} 4 | ,{"geometry":{"type":"MultiPolygon","coordinates":[[[[-122.84000000000003,49.000000000000114],[-122.97421000000001,49.00253777777778],[-124.91024,49.98456],[-125.62461,50.416560000000004],[-127.43561000000001,50.83061],[-127.99276,51.71583],[-127.85032,52.32961],[-129.12979,52.75538],[-129.30523,53.561589999999995],[-130.51497,54.28757],[-130.53610895273684,54.80275447679924],[-130.53611,54.802780000000006],[-129.98,55.285000000000004],[-130.00778000000003,55.915830000000085],[-131.70781,56.55212],[-132.73042,57.692890000000006],[-133.35556000000003,58.41028000000001],[-134.27111000000002,58.86111000000005],[-134.94500000000005,59.2705600000001],[-135.47583,59.787780000000005],[-136.47972000000004,59.46389000000005],[-137.4525,58.905],[-138.34089,59.562110000000004],[-139.03900000000002,60],[-140.013,60.27682000000001],[-140.99778,60.30639000000001],[-140.9925,66.00003000000001],[-140.986,69.712],[-140.98598761037601,69.71199839952635],[-139.12052,69.47102],[-137.54636000000002,68.99002],[-136.50358,68.89804],[-135.62576,69.31512000000001],[-134.41464000000002,69.62743],[-132.92925000000002,69.50534],[-131.43135999999998,69.94451],[-129.79471,70.19369],[-129.10773,69.77927000000001],[-128.36156,70.01286],[-128.13817,70.48384],[-127.44712000000001,70.37721],[-125.75632000000002,69.48058],[-124.42483,70.1584],[-124.28968,69.39968999999999],[-123.06108,69.56372],[-122.6835,69.85553],[-121.47226,69.79778],[-119.94288,69.37786],[-117.60268,69.01128],[-116.22643,68.84151],[-115.24690000000001,68.90591],[-113.89793999999999,68.3989],[-115.30489,67.90261000000001],[-113.49727,67.68815000000001],[-110.798,67.80611999999999],[-109.94619,67.98104000000001],[-108.8802,67.38144],[-107.79239,67.88736],[-108.81299,68.31164],[-108.16721000000001,68.65392],[-106.95,68.7],[-106.15,68.8],[-105.34282000000002,68.56122],[-104.33791000000001,68.018],[-103.22115000000001,68.09775],[-101.45433,67.64689],[-99.90195,67.80566],[-98.4432,67.78165],[-98.5586,68.40394],[-97.66948000000001,68.57864000000001],[-96.11991,68.23939],[-96.12588,67.29338],[-95.48943,68.0907],[-94.685,68.06383],[-94.23282000000002,69.06903000000001],[-95.30408,69.68571],[-96.47131,70.08976],[-96.39115,71.19482],[-95.2088,71.92053],[-93.88997,71.76015],[-92.87818,71.31869],[-91.51964000000001,70.19129000000001],[-92.40692000000001,69.69997000000001],[-90.5471,69.49766],[-90.55151000000001,68.47499],[-89.21515,69.25873],[-88.01966,68.61508],[-88.31748999999999,67.87338000000001],[-87.35017,67.19872],[-86.30606999999999,67.92146],[-85.57664,68.78456],[-85.52197,69.88211],[-84.10081000000001,69.80539],[-82.62258,69.65826],[-81.28043000000001,69.16202000000001],[-81.22019999999999,68.66567],[-81.96436000000001,68.13253],[-81.25928,67.59716],[-81.38653000000001,67.11078],[-83.34456,66.41154],[-84.73542,66.2573],[-85.76943,66.55833],[-86.06760000000001,66.05625],[-87.03143,65.21297],[-87.32324,64.77563],[-88.48296,64.09897000000001],[-89.91444,64.03273],[-90.70398,63.610170000000004],[-90.77004000000001,62.960210000000004],[-91.93342,62.83508],[-93.15698,62.02469000000001],[-94.24153,60.89865],[-94.62930999999999,60.11021],[-94.6846,58.94882],[-93.21502000000001,58.78212],[-92.76462000000001,57.84571],[-92.29702999999999,57.08709],[-90.89769,57.28468],[-89.03953,56.85172],[-88.03978000000001,56.47162],[-87.32421,55.999140000000004],[-86.07121,55.72383],[-85.01181000000001,55.302600000000005],[-83.36055,55.24489],[-82.27285,55.14832],[-82.43620000000001,54.282270000000004],[-82.12502,53.27703],[-81.40075,52.157880000000006],[-79.91289,51.208420000000004],[-79.14301,51.533930000000005],[-78.60191,52.56208],[-79.12421,54.14145],[-79.82958,54.66772],[-78.22874,55.136449999999996],[-77.0956,55.83741],[-76.54137,56.53423000000001],[-76.62319000000001,57.20263],[-77.30226,58.05209],[-78.51688,58.80458],[-77.33676,59.852610000000006],[-77.77272,60.75788000000001],[-78.10687,62.31964000000001],[-77.41067,62.55053],[-75.69621000000001,62.2784],[-74.6682,62.181110000000004],[-73.83988000000001,62.4438],[-72.90853,62.10507],[-71.67708,61.52535],[-71.37369000000001,61.137170000000005],[-69.59042,61.06141],[-69.62033,60.221250000000005],[-69.28790000000001,58.95736],[-68.37455,58.80106],[-67.64976,58.21206],[-66.20178,58.76731],[-65.24517,59.87071],[-64.58352000000001,60.33558],[-63.804750000000006,59.442600000000006],[-62.502359999999996,58.16708],[-61.396550000000005,56.96745000000001],[-61.798660000000005,56.33945],[-60.46853,55.775479999999995],[-59.56962,55.20407],[-57.97508,54.94549000000001],[-57.3332,54.6265],[-56.93689,53.780319999999996],[-56.15811,53.647490000000005],[-55.75632,53.27036],[-55.68338,52.146640000000005],[-56.40916000000001,51.770700000000005],[-57.12691,51.419720000000005],[-58.77482,51.0643],[-60.03309000000001,50.24277],[-61.72366,50.08046],[-63.86251,50.29099],[-65.36331,50.2982],[-66.39905,50.228970000000004],[-67.23631,49.511559999999996],[-68.51114,49.068360000000006],[-69.95362,47.74488],[-71.10458,46.82171],[-70.25522,46.986059999999995],[-68.65,48.3],[-66.55243,49.1331],[-65.05626,49.232780000000005],[-64.17099,48.74248],[-65.11545000000001,48.07085],[-64.79854,46.99297],[-64.47219,46.238490000000006],[-63.17329000000001,45.73902],[-61.520720000000004,45.883770000000005],[-60.518150000000006,47.00793],[-60.448600000000006,46.28264],[-59.80287,45.9204],[-61.03988,45.265249999999995],[-63.254709999999996,44.67014],[-64.24656,44.265530000000005],[-65.36406000000001,43.54523],[-66.1234,43.61867],[-66.16173,44.46512],[-64.42549,45.29204],[-66.02605000000001,45.25931],[-67.13741,45.13753],[-67.79134,45.70281000000001],[-67.79046000000001,47.066359999999996],[-68.23444,47.354859999999974],[-68.90500000000003,47.18500000000006],[-69.237216,47.447781],[-69.99997,46.69307],[-70.305,45.915],[-70.66,45.46],[-71.08482000000004,45.30524000000014],[-71.405,45.254999999999995],[-71.50506,45.0082],[-73.34783,45.00738],[-74.86700000000002,45.000480000000096],[-75.31821000000001,44.81645],[-76.375,44.09631],[-76.50000000000001,44.01845889375865],[-76.82003414580558,43.628784288093755],[-77.7378850979577,43.62905558936328],[-78.72027991404235,43.62508942318493],[-79.17167355011186,43.46633942318426],[-79.01,43.27],[-78.92,42.964999999999996],[-78.93936214874375,42.86361135514798],[-80.24744767934794,42.36619985612255],[-81.27774654816716,42.209025987306816],[-82.4392777167916,41.675105088867326],[-82.69008928092023,41.675105088867326],[-83.029810146807,41.83279572200598],[-83.14199968131264,41.975681057292874],[-83.12,42.08],[-82.9,42.43],[-82.42999999999999,42.980000000000004],[-82.13764238150395,43.57108755143997],[-82.33776312543114,44.440000000000055],[-82.55092464875821,45.34751658790543],[-83.59285071484311,45.81689362241252],[-83.46955074739469,45.994686387712534],[-83.61613094759059,46.116926988299014],[-83.89076534700574,46.116926988299014],[-84.0918512641615,46.27541860613826],[-84.1421195136734,46.51222585711571],[-84.33670000000001,46.408770000000004],[-84.60490000000004,46.439599999999984],[-84.54374874544584,46.538684190449146],[-84.77923824739992,46.63710195574902],[-84.8760798815149,46.90008331968238],[-85.65236324740341,47.22021881773051],[-86.46199083122826,47.553338019392],[-87.43979262330028,47.94],[-88.37811418328671,48.302917588893706],[-89.27291744663665,48.01980825458281],[-89.60000000000002,48.010000000000105],[-90.83,48.27],[-91.64,48.14],[-92.61000000000001,48.44999999999993],[-93.63087000000002,48.609260000000006],[-94.32914000000001,48.67074],[-94.64,48.84],[-94.81758000000002,49.38905],[-95.15609,49.38425000000001],[-95.15906950917206,49],[-97.2287200000048,49.0007],[-100.65000000000003,49.000000000000114],[-104.04826000000003,48.99986000000007],[-107.05000000000001,49],[-110.05000000000001,49],[-113,49],[-116.04818,49],[-117.03121,49],[-120,49.000000000000114],[-122.84000000000003,49.000000000000114]]],[[[-83.99367000000001,62.452799999999996],[-83.25048,62.91409],[-81.87699,62.90458],[-81.89825,62.7108],[-83.06857000000001,62.159220000000005],[-83.77462000000001,62.18231],[-83.99367000000001,62.452799999999996]]],[[[-79.77583312988281,72.8029022216797],[-80.87609863281251,73.33318328857422],[-80.83388519287111,73.69318389892578],[-80.35305786132812,73.75971984863281],[-78.06443786621094,73.65193176269531],[-76.34,73.10268498995305],[-76.25140380859375,72.82638549804688],[-77.31443786621094,72.85554504394531],[-78.39167022705078,72.87665557861328],[-79.4862518310547,72.74220275878906],[-79.77583312988281,72.8029022216797]]],[[[-80.315395,62.08556500000001],[-79.92939,62.3856],[-79.52002,62.363710000000005],[-79.26582,62.158674999999995],[-79.65752,61.63308],[-80.09956000000001,61.71810000000001],[-80.36215,62.016490000000005],[-80.315395,62.08556500000001]]],[[[-93.61275590694046,74.97999726022438],[-94.15690873897391,74.59234650338688],[-95.60868058956564,74.66686391875176],[-96.82093217648455,74.92762319609658],[-96.28858740922982,75.37782827422338],[-94.85081987178917,75.64721751576089],[-93.97774654821797,75.29648956979595],[-93.61275590694046,74.97999726022438]]],[[[-93.84000301794399,77.51999726023455],[-94.29560828324529,77.49134267852868],[-96.16965410031007,77.55511139597685],[-96.43630449093614,77.83462921824362],[-94.42257727738641,77.820004787905],[-93.7206562975659,77.63433136668031],[-93.84000301794399,77.51999726023455]]],[[[-96.75439876990876,78.76581268992702],[-95.5592779202946,78.41831452098033],[-95.83029496944934,78.05694122996324],[-97.30984290239799,77.85059723582181],[-98.12428931353404,78.08285696075761],[-98.55286780474668,78.45810537384507],[-98.63198442258553,78.87193024363837],[-97.33723141151266,78.83198436147676],[-96.75439876990876,78.76581268992702]]],[[[-88.15035030796028,74.39230703398503],[-89.7647220527584,74.51555532500116],[-92.42244096552946,74.83775788034099],[-92.76828548864282,75.38681997344214],[-92.88990597204175,75.88265534128267],[-93.89382402217599,76.31924367950056],[-95.9624574450358,76.4413809272224],[-97.1213789538295,76.7510777859476],[-96.74512285031237,77.16138865834507],[-94.68408586299944,77.09787832305837],[-93.57392106807313,76.77629588490605],[-91.6050231595366,76.7785179714946],[-90.7418458727493,76.44959747995681],[-90.96966142450802,76.07401317005947],[-89.82223792189926,75.84777374948565],[-89.18708289259985,75.61016551380762],[-87.83827633334965,75.56618886992725],[-86.37919226758864,75.4824213731821],[-84.78962521029058,75.69920400664653],[-82.75344458691006,75.78431509063124],[-81.12853084992436,75.71398346628199],[-80.05751095245915,75.33684886341591],[-79.83393286814837,74.92312734648716],[-80.45777075877587,74.65730377877777],[-81.94884253612557,74.44245901152432],[-83.22889360221143,74.56402781849094],[-86.09745235873332,74.41003205026117],[-88.15035030796028,74.39230703398503]]],[[[-111.26444332563088,78.15295604116154],[-109.85445187054711,77.99632477488488],[-110.18693803591302,77.69701487905034],[-112.0511911690585,77.4092288276169],[-113.53427893761912,77.73220652944111],[-112.7245867582539,78.05105011668196],[-111.26444332563088,78.15295604116154]]],[[[-110.96366065147602,78.8044408230652],[-109.6631457182026,78.60197256134565],[-110.88131425661892,78.40691986765997],[-112.54209143761516,78.4079017198735],[-112.52589087609164,78.55055451121522],[-111.5000103422334,78.8499935981305],[-110.96366065147602,78.8044408230652]]],[[[-55.600218268442056,51.31707469339794],[-56.13403581401709,50.68700979267928],[-56.795881720595276,49.81230866149089],[-56.14310502788433,50.15011749938286],[-55.471492275603,49.93581533466846],[-55.82240108908096,49.58712860777905],[-54.935142584845636,49.3130109726868],[-54.473775397343786,49.556691189159125],[-53.47654944519137,49.24913890237404],[-53.786013759971254,48.516780503933624],[-53.08613399922626,48.68780365660358],[-52.958648240762216,48.15716421161447],[-52.64809872090421,47.53554840757552],[-53.069158291218386,46.65549876564492],[-53.521456264853,46.61829173439477],[-54.17893551290251,46.80706574155698],[-53.9618686590605,47.62520701760193],[-54.24048214376214,47.752279364607645],[-55.40077307801157,46.884993801453135],[-55.99748084168583,46.919720363953275],[-55.29121904155279,47.38956248635099],[-56.250798712780586,47.632545070987376],[-57.32522925477708,47.57280711525797],[-59.26601518414682,47.60334788674247],[-59.419494188053676,47.899453843774886],[-58.79658647320744,48.25152537697942],[-59.23162451845657,48.52318838153781],[-58.3918049790652,49.12558055276418],[-57.35868974468606,50.71827403421587],[-56.738650071832026,51.28743825947855],[-55.87097693543532,51.63209422464921],[-55.40697424988659,51.5882726100657],[-55.600218268442056,51.31707469339794]]],[[[-83.88262630891977,65.10961782496354],[-82.78757687043883,64.76669302027467],[-81.6420137193926,64.45513580998697],[-81.55344031444432,63.97960928003714],[-80.81736121287886,64.057485663501],[-80.10345130076664,63.72598135034862],[-80.99101986359572,63.41124603947496],[-82.54717810741704,63.65172231714521],[-83.10879757356511,64.10187571883971],[-84.10041663281388,63.569711819098],[-85.52340471061905,63.052379055424055],[-85.8667687649824,63.63725291610349],[-87.22198320183678,63.54123810490519],[-86.35275977247133,64.0358332383707],[-86.2248864407651,64.82291697860823],[-85.88384782585486,65.7387783881171],[-85.1613079495499,65.6572846543928],[-84.97576371940592,65.21751821558898],[-84.4640120104195,65.37177236598022],[-83.88262630891977,65.10961782496354]]],[[[-78.77063859731078,72.35217316353418],[-77.8246239895596,72.74961660429098],[-75.60584469267573,72.2436784939374],[-74.228616095665,71.76714427355789],[-74.09914079455771,71.33084015571758],[-72.24222571479768,71.55692454699452],[-71.20001542833518,70.92001251899718],[-68.7860542466849,70.52502370877427],[-67.91497046575694,70.12194753689765],[-66.9690333726542,69.18608734809182],[-68.8051228502006,68.72019847276444],[-66.4498660956339,68.06716339789203],[-64.86231441919524,67.84753856065159],[-63.424934454996794,66.92847321234059],[-61.851981370680605,66.86212067327783],[-62.16317684594226,66.16025136988962],[-63.918444383384184,64.9986685248329],[-65.14886023625368,65.42603261988667],[-66.72121904159852,66.38804108343219],[-68.015016038674,66.26272573512439],[-68.1412874009792,65.68978913030439],[-67.08964616562342,65.10845510523696],[-65.73208045109976,64.64840566675856],[-65.32016760930125,64.38273712834605],[-64.66940629744968,63.392926744227495],[-65.01380388045888,62.67418508569598],[-66.27504472519048,62.94509878198612],[-68.7831862046927,63.74567007105183],[-67.36968075221309,62.88396556258484],[-66.32829728866726,62.28007477482201],[-66.16556820338015,61.93089712182582],[-68.87736650254465,62.330149237712824],[-71.02343705919385,62.91070811629588],[-72.23537858751902,63.39783600529522],[-71.88627844917127,63.67998932560887],[-73.37830624051838,64.19396312118384],[-74.83441891142263,64.6790756293238],[-74.81850257027673,64.38909332951793],[-77.70997982452008,64.22954234481678],[-78.5559488593542,64.57290639918013],[-77.89728105336198,65.30919220647475],[-76.01827429879717,65.32696889918314],[-73.95979529488268,65.45476471624094],[-74.29388342964964,65.81177134872938],[-73.94491248238262,66.31057811142666],[-72.65116716173942,67.28457550726391],[-72.92605994331605,67.72692576768235],[-73.31161780464572,68.06943716091287],[-74.84330725777684,68.55462718370127],[-76.86910091826672,68.89473562283025],[-76.22864905465738,69.14776927354741],[-77.28736996123715,69.76954010688321],[-78.1686339993266,69.82648753526887],[-78.95724219431673,70.16688019477543],[-79.49245500356366,69.87180776638884],[-81.30547095409176,69.74318512641436],[-84.94470618359851,69.96663401964442],[-87.06000342481789,70.26000112576538],[-88.68171322300148,70.4107412787608],[-89.51341956252303,70.76203766548095],[-88.46772111688082,71.21818553332132],[-89.88815121128755,71.22255219184997],[-90.20516028518205,72.23507436796079],[-89.436576707705,73.12946421985238],[-88.40824154331287,73.53788890247121],[-85.82615108920098,73.80381582304518],[-86.56217851433412,73.15744700793844],[-85.77437130404454,72.53412588163387],[-84.85011247428822,73.34027822538708],[-82.31559017610101,73.7509508328106],[-80.60008765330768,72.71654368762417],[-80.74894161652443,72.06190664335072],[-78.77063859731078,72.35217316353418]]],[[[-94.50365759965237,74.13490672473922],[-92.42001217321173,74.1000251329422],[-90.50979285354263,73.85673248971206],[-92.00396521682987,72.96624420845852],[-93.19629553910026,72.77199249947334],[-94.26904659704726,72.02459625923599],[-95.40985551632266,72.06188080513458],[-96.03374508338244,72.94027680123183],[-96.01826799191102,73.43742991809582],[-95.49579342322404,73.86241689726417],[-94.50365759965237,74.13490672473922]]],[[[-122.85492448615902,76.11654287383568],[-122.85492529360326,76.11654287383568],[-121.15753536032824,76.86450755482828],[-119.1039389718211,77.51221995717462],[-117.570130784966,77.4983189968881],[-116.19858659550738,77.6452867703262],[-116.33581336145845,76.87696157501061],[-117.10605058476882,76.53003184681911],[-118.04041215703819,76.48117178008714],[-119.89931758688572,76.053213406062],[-121.49999507712648,75.90001862253276],[-122.85492448615902,76.11654287383568]]],[[[-132.71000788443126,54.04000931542356],[-131.74998958400334,54.12000438090922],[-132.049480347351,52.98462148702447],[-131.1790425218266,52.180432847698285],[-131.57782954982298,52.18237071390928],[-132.18042842677852,52.639707139692405],[-132.54999243231384,53.100014960332146],[-133.05461117875552,53.411468817755406],[-133.2396644827927,53.851080227262344],[-133.1800040417117,54.169975490935315],[-132.71000788443126,54.04000931542356]]],[[[-105.4922891914932,79.30159393992916],[-103.52928239623795,79.16534902619163],[-100.8251580472688,78.80046173777872],[-100.0601918200522,78.32475434031589],[-99.67093909381364,77.90754466420744],[-101.30394019245301,78.01898489044486],[-102.94980872273302,78.34322866486023],[-105.17613277873151,78.3803323432458],[-104.21042945027713,78.67742015249176],[-105.41958045125853,78.91833567983649],[-105.4922891914932,79.30159393992916]]],[[[-123.51000158755119,48.51001089130341],[-124.01289078839955,48.37084625914139],[-125.65501277733838,48.8250045843385],[-125.95499446679275,49.17999583596759],[-126.85000443587185,49.53000031188043],[-127.02999344954443,49.81499583597008],[-128.0593363043662,49.9949590114266],[-128.44458410710214,50.539137681676095],[-128.35841365625546,50.77064809834371],[-127.30858109602994,50.552573554071955],[-126.69500097721235,50.400903225295394],[-125.7550066738232,50.29501821552935],[-125.4150015875588,49.95000051533259],[-124.92076818911934,49.475274970083376],[-123.92250870832106,49.06248362893581],[-123.51000158755119,48.51001089130341]]],[[[-121.53787999999997,74.44893000000002],[-120.10978,74.24135000000001],[-117.55563999999993,74.18576999999993],[-116.58442000000002,73.89607000000007],[-115.51080999999999,73.47519],[-116.76793999999995,73.22291999999999],[-119.22000000000003,72.51999999999998],[-120.45999999999998,71.82000000000005],[-120.45999999999998,71.38360179308756],[-123.09218999999996,70.90164000000004],[-123.62,71.34000000000009],[-125.92894873747338,71.86868846301138],[-125.49999999999994,72.29226081179502],[-124.80729000000002,73.02255999999994],[-123.93999999999994,73.68000000000012],[-124.91774999999996,74.29275000000013],[-121.53787999999997,74.44893000000002]]],[[[-107.81943000000001,75.84552000000001],[-106.92893000000001,76.01282],[-105.881,75.96940000000001],[-105.70498,75.47951],[-106.31347000000001,75.00527],[-109.70000000000002,74.85000000000001],[-112.22306999999999,74.41696],[-113.74381,74.39427],[-113.87135,74.72029],[-111.79420999999999,75.16250000000001],[-116.31221,75.04343],[-117.7104,75.2222],[-116.34602000000001,76.19903000000001],[-115.40487,76.47887],[-112.59056000000001,76.14134],[-110.81422,75.54919],[-109.06710000000001,75.47321000000001],[-110.49726000000001,76.42982],[-109.58109999999999,76.79417],[-108.54858999999999,76.67832000000001],[-108.21141,76.20168000000001],[-107.81943000000001,75.84552000000001]]],[[[-106.52258999999992,73.07601],[-105.40245999999996,72.67259000000007],[-104.77484000000004,71.6984000000001],[-104.4647599999999,70.99297000000007],[-102.78537,70.49776000000003],[-100.98077999999992,70.02431999999999],[-101.08928999999995,69.58447000000012],[-102.73115999999993,69.50402000000003],[-102.09329000000002,69.11962000000011],[-102.43024000000003,68.75281999999999],[-104.24000000000001,68.91000000000008],[-105.96000000000004,69.18000000000012],[-107.12254000000001,69.11922000000004],[-108.99999999999994,68.78000000000003],[-111.53414887520017,68.63005915681794],[-113.31320000000005,68.53553999999997],[-113.85495999999989,69.00744000000009],[-115.22000000000003,69.28000000000009],[-116.10793999999999,69.16821000000004],[-117.34000000000003,69.9600000000001],[-116.67472999999995,70.06655],[-115.13112000000001,70.23730000000006],[-113.72140999999999,70.1923700000001],[-112.41610000000003,70.36637999999999],[-114.35000000000002,70.60000000000002],[-116.48684000000003,70.52044999999998],[-117.90480000000002,70.54056000000014],[-118.43238000000002,70.90920000000006],[-116.11311,71.30917999999997],[-117.65567999999996,71.29520000000002],[-119.40199000000001,71.55858999999998],[-118.56266999999997,72.30785000000003],[-117.86641999999995,72.70594000000006],[-115.18909000000002,73.31459000000012],[-114.16716999999994,73.1214500000001],[-114.66633999999999,72.65277000000009],[-112.44101999999992,72.95540000000011],[-111.05039,72.45040000000006],[-109.92034999999993,72.96113000000008],[-109.00653999999997,72.63335000000001],[-108.18834999999996,71.65089],[-107.68599,72.0654800000001],[-108.39639,73.08953000000008],[-107.51645000000002,73.23597999999998],[-106.52258999999992,73.07601]]],[[[-100.43836,72.70588000000001],[-101.54,73.36],[-100.35642000000001,73.84389],[-99.16387,73.63339],[-97.38,73.76],[-97.12,73.47],[-98.05359,72.99052],[-96.54,72.56],[-96.72000000000001,71.66],[-98.35966,71.27284999999999],[-99.32286,71.35639],[-100.01482,71.73827],[-102.5,72.51],[-102.48000000000002,72.83000000000001],[-100.43836,72.70588000000001]]],[[[-106.6,73.60000000000001],[-105.26,73.64],[-104.5,73.42],[-105.38000000000001,72.76],[-106.94,73.46000000000001],[-106.6,73.60000000000001]]],[[[-98.50000000000001,76.72],[-97.735585,76.25656000000001],[-97.70441500000001,75.74344],[-98.16000000000001,75],[-99.80874,74.89744],[-100.88365999999999,75.05736],[-100.86292000000002,75.64075],[-102.50209,75.5638],[-102.56552,76.3366],[-101.48973,76.30537],[-99.98349,76.64634],[-98.57699,76.58859],[-98.50000000000001,76.72]]],[[[-96.01644,80.60233000000001],[-95.32345000000001,80.90729],[-94.29843,80.97727],[-94.73542,81.20646000000002],[-92.40983999999999,81.25739000000003],[-91.13288999999999,80.72345000000003],[-89.45000000000002,80.50932203389831],[-87.81,80.32000000000001],[-87.02000000000001,79.66000000000001],[-85.81435,79.3369],[-87.18755999999999,79.0393],[-89.03535000000001,78.28723],[-90.80436,78.21533000000001],[-92.87669000000001,78.34333000000001],[-93.95116000000002,78.75099],[-93.93574,79.11373],[-93.14524,79.3801],[-94.974,79.37248],[-96.07614000000001,79.70502],[-96.70972,80.15777],[-96.01644,80.60233000000001]]],[[[-91.58702000000001,81.89429000000001],[-90.10000000000001,82.08500000000004],[-88.93227,82.11751000000001],[-86.97024,82.27961],[-85.5,82.65227345805702],[-84.260005,82.60000000000001],[-83.18,82.32],[-82.42,82.86000000000001],[-81.1,83.02],[-79.30664,83.13056],[-76.25,83.17205882352941],[-75.71878000000001,83.06404000000002],[-72.83153,83.23324000000001],[-70.66576500000001,83.16978075838284],[-68.50000000000001,83.10632151676572],[-65.82735,83.02801000000001],[-63.68,82.9],[-61.85,82.62860000000002],[-61.89388,82.36165000000001],[-64.334,81.92775000000002],[-66.75342,81.72527000000001],[-67.65755,81.50141],[-65.48031,81.50657000000002],[-67.84,80.90000000000003],[-69.4697,80.61683000000001],[-71.18,79.8],[-73.2428,79.63415],[-73.88000000000001,79.43016220480206],[-76.90773,79.32309000000001],[-75.52924,79.19766000000001],[-76.22046,79.01907],[-75.39345,78.52581],[-76.34354,78.18296000000001],[-77.88851000000001,77.89991],[-78.36269,77.50859000000001],[-79.75951,77.20967999999999],[-79.61965000000001,76.98336],[-77.91089000000001,77.022045],[-77.88911,76.777955],[-80.56125,76.17812],[-83.17439,76.45403],[-86.11184,76.29901000000001],[-87.60000000000001,76.42],[-89.49068,76.47239],[-89.6161,76.95213000000001],[-87.76739,77.17833],[-88.26,77.9],[-87.65,77.97022222222223],[-84.97634,77.53873],[-86.34,78.18],[-87.96191999999999,78.37181],[-87.15198000000001,78.75867],[-85.37868,78.99690000000001],[-85.09495,79.34543000000001],[-86.50734,79.73624],[-86.93179,80.25145],[-84.19844,80.20836],[-83.40869565217389,80.10000000000001],[-81.84823,80.46442],[-84.1,80.58],[-87.59895,80.51627],[-89.36663,80.85569000000001],[-90.2,81.26],[-91.36786000000001,81.5531],[-91.58702000000001,81.89429000000001]]],[[[-75.21597,67.44425],[-75.86588,67.14886],[-76.98687,67.09873],[-77.2364,67.58809000000001],[-76.81166,68.14856],[-75.89521,68.28721],[-75.11449999999999,68.01035999999999],[-75.10333,67.58202],[-75.21597,67.44425]]],[[[-96.25740120380055,69.49003035832177],[-95.64768120380054,69.10769035832178],[-96.26952120380055,68.75704035832177],[-97.61740120380055,69.06003035832177],[-98.43180120380055,68.95070035832177],[-99.79740120380055,69.40003035832177],[-98.91740120380055,69.71003035832177],[-98.21826120380055,70.14354035832177],[-97.15740120380055,69.86003035832177],[-96.55740120380055,69.68003035832177],[-96.25740120380055,69.49003035832177]]],[[[-64.51912,49.87304],[-64.17322,49.95718],[-62.858290000000004,49.70641],[-61.835584999999995,49.28855],[-61.806304999999995,49.10506000000001],[-62.29318,49.08717],[-63.589259999999996,49.400690000000004],[-64.51912,49.87304]]],[[[-64.01486,47.03601],[-63.6645,46.55001],[-62.9393,46.41587],[-62.012080000000005,46.44314],[-62.503910000000005,46.033390000000004],[-62.87433,45.968180000000004],[-64.14280000000001,46.39265],[-64.39261,46.72747],[-64.01486,47.03601]]]]},"properties":{"bbox":{"xmax":-52.64809872090421,"xmin":-140.99778,"ymax":83.23324000000001,"ymin":41.675105088867326},"continent":"North America","gdp_md_est":1736425,"iso_a3":"CAN","name":"Canada","pop_est":37589262},"type":"Feature"} 5 | ,{"geometry":{"type":"MultiPolygon","coordinates":[[[[-122.84000000000003,49.000000000000114],[-120,49.000000000000114],[-117.03121,49],[-116.04818,49],[-113,49],[-110.05000000000001,49],[-107.05000000000001,49],[-104.04826000000003,48.99986000000007],[-100.65000000000003,49.000000000000114],[-97.2287200000048,49.0007],[-95.15906950917206,49],[-95.15609,49.38425000000001],[-94.81758000000002,49.38905],[-94.64,48.84],[-94.32914000000001,48.67074],[-93.63087000000002,48.609260000000006],[-92.61000000000001,48.44999999999993],[-91.64,48.14],[-90.83,48.27],[-89.60000000000002,48.010000000000105],[-89.27291744663665,48.01980825458281],[-88.37811418328671,48.302917588893706],[-87.43979262330028,47.94],[-86.46199083122826,47.553338019392],[-85.65236324740341,47.22021881773051],[-84.8760798815149,46.90008331968238],[-84.77923824739992,46.63710195574902],[-84.54374874544584,46.538684190449146],[-84.60490000000004,46.439599999999984],[-84.33670000000001,46.408770000000004],[-84.1421195136734,46.51222585711571],[-84.0918512641615,46.27541860613826],[-83.89076534700574,46.116926988299014],[-83.61613094759059,46.116926988299014],[-83.46955074739469,45.994686387712534],[-83.59285071484311,45.81689362241252],[-82.55092464875821,45.34751658790543],[-82.33776312543114,44.440000000000055],[-82.13764238150395,43.57108755143997],[-82.42999999999999,42.980000000000004],[-82.9,42.43],[-83.12,42.08],[-83.14199968131264,41.975681057292874],[-83.029810146807,41.83279572200598],[-82.69008928092023,41.675105088867326],[-82.4392777167916,41.675105088867326],[-81.27774654816716,42.209025987306816],[-80.24744767934794,42.36619985612255],[-78.93936214874375,42.86361135514798],[-78.92,42.964999999999996],[-79.01,43.27],[-79.17167355011186,43.46633942318426],[-78.72027991404235,43.62508942318493],[-77.7378850979577,43.62905558936328],[-76.82003414580558,43.628784288093755],[-76.50000000000001,44.01845889375865],[-76.375,44.09631],[-75.31821000000001,44.81645],[-74.86700000000002,45.000480000000096],[-73.34783,45.00738],[-71.50506,45.0082],[-71.405,45.254999999999995],[-71.08482000000004,45.30524000000014],[-70.66,45.46],[-70.305,45.915],[-69.99997,46.69307],[-69.237216,47.447781],[-68.90500000000003,47.18500000000006],[-68.23444,47.354859999999974],[-67.79046000000001,47.066359999999996],[-67.79134,45.70281000000001],[-67.13741,45.13753],[-66.96465999999998,44.809700000000134],[-68.03251999999998,44.325199999999995],[-69.05999999999995,43.980000000000075],[-70.11616999999995,43.68405000000013],[-70.64547563341102,43.09023834896402],[-70.81488999999999,42.865299999999934],[-70.82499999999999,42.33499999999998],[-70.49499999999995,41.80500000000001],[-70.07999999999998,41.78000000000003],[-70.185,42.145000000000095],[-69.88496999999995,41.92283000000009],[-69.96502999999996,41.63717000000014],[-70.63999999999999,41.47500000000002],[-71.12039000000004,41.49445000000014],[-71.8599999999999,41.32000000000005],[-72.29500000000002,41.26999999999998],[-72.87643000000003,41.220650000000035],[-73.71000000000004,40.93110235165449],[-72.24125999999995,41.119480000000124],[-71.94499999999988,40.930000000000064],[-73.34499999999997,40.63000000000005],[-73.98200000000003,40.62799999999993],[-73.95232499999997,40.75075000000004],[-74.25671,40.47351000000003],[-73.96243999999996,40.42763000000002],[-74.17838,39.70925999999997],[-74.90603999999996,38.93954000000002],[-74.98041,39.19640000000004],[-75.20002,39.248450000000105],[-75.52805000000001,39.49850000000009],[-75.32,38.960000000000036],[-75.07183476478986,38.782032230179254],[-75.05672999999996,38.40412000000009],[-75.37746999999996,38.015510000000006],[-75.94022999999999,37.21689000000009],[-76.03126999999995,37.25659999999999],[-75.72204999999985,37.93705000000011],[-76.23286999999999,38.319214999999986],[-76.35000000000002,39.14999999999998],[-76.54272499999996,38.71761500000008],[-76.32933000000003,38.08326000000005],[-76.98999793161352,38.23999176691336],[-76.30161999999996,37.91794499999992],[-76.25873999999999,36.96640000000008],[-75.97179999999997,36.89726000000002],[-75.8680399999999,36.55125000000004],[-75.72748999999999,35.55074000000013],[-76.36318,34.80854000000011],[-77.39763499999992,34.512009999999975],[-78.05496,33.92547000000002],[-78.55434999999989,33.86133000000012],[-79.06067000000002,33.493949999999984],[-79.20357000000001,33.158390000000054],[-80.30132499999996,32.509355000000085],[-80.86498,32.033300000000054],[-81.33629000000002,31.44049000000001],[-81.49041999999997,30.7299900000001],[-81.31371000000001,30.035520000000076],[-80.97999999999996,29.18000000000012],[-80.53558499999991,28.472129999999993],[-80.52999999999986,28.040000000000077],[-80.05653928497759,26.88000000000011],[-80.08801499999998,26.205764999999985],[-80.13155999999992,25.816775000000064],[-80.38103000000001,25.20616000000001],[-80.67999999999995,25.08000000000004],[-81.17212999999998,25.201260000000104],[-81.33000000000004,25.639999999999986],[-81.70999999999987,25.870000000000005],[-82.23999999999995,26.730000000000132],[-82.70515,27.495040000000074],[-82.85525999999999,27.886240000000043],[-82.64999999999998,28.550000000000125],[-82.92999999999995,29.10000000000008],[-83.70958999999999,29.936560000000043],[-84.09999999999997,30.09000000000009],[-85.10881999999998,29.636150000000043],[-85.28784000000002,29.68612000000013],[-85.7731,30.152610000000095],[-86.39999999999992,30.40000000000009],[-87.53035999999992,30.27433000000002],[-88.41781999999995,30.384900000000016],[-89.1804899999999,30.315980000000025],[-89.5938311784198,30.159994004836847],[-89.41373499999997,29.89418999999998],[-89.43,29.488639999999975],[-89.21767,29.291080000000022],[-89.40822999999995,29.159610000000043],[-89.77927999999997,29.307140000000118],[-90.15463,29.11743000000007],[-90.88022499999994,29.148535000000095],[-91.62678499999993,29.677000000000135],[-92.49905999999999,29.552300000000002],[-93.22636999999997,29.783750000000055],[-93.84841999999998,29.71363000000008],[-94.69,29.480000000000132],[-95.60025999999999,28.738630000000057],[-96.59403999999995,28.307480000000055],[-97.13999999999987,27.83000000000004],[-97.36999999999995,27.380000000000052],[-97.37999999999994,26.690000000000055],[-97.32999999999998,26.210000000000093],[-97.13999999999987,25.870000000000005],[-97.52999999999992,25.84000000000009],[-98.23999999999995,26.06000000000006],[-99.01999999999992,26.37000000000006],[-99.30000000000001,26.840000000000032],[-99.51999999999992,27.54000000000002],[-100.10999999999996,28.110000000000127],[-100.45584000000002,28.69612000000012],[-100.95759999999996,29.380710000000136],[-101.66239999999999,29.77930000000009],[-102.48000000000002,29.75999999999999],[-103.11000000000001,28.970000000000027],[-103.94,29.27000000000004],[-104.4569699999999,29.571960000000047],[-104.70574999999997,30.121730000000014],[-105.03737000000001,30.644019999999955],[-105.63159000000002,31.08383000000009],[-106.1429,31.399950000000047],[-106.50758999999988,31.754520000000014],[-108.24000000000001,31.754853718166373],[-108.24193999999994,31.342220000000054],[-109.03500000000003,31.341940000000136],[-111.02361000000002,31.334719999999948],[-113.30498,32.03914000000009],[-114.815,32.52528000000001],[-114.72138999999993,32.72082999999992],[-115.99134999999995,32.61239000000012],[-117.12775999999985,32.53533999999996],[-117.29593769127393,33.04622461520387],[-117.94400000000002,33.621236431201396],[-118.41060227589753,33.74090922312445],[-118.51989482279976,34.02778157757575],[-119.08100000000002,34.07799999999992],[-119.43884064201671,34.34847717828427],[-120.36777999999998,34.447110000000066],[-120.62286,34.60854999999998],[-120.74432999999999,35.15686000000011],[-121.71456999999992,36.161529999999914],[-122.54746999999998,37.551760000000115],[-122.51201000000003,37.78339000000011],[-122.95319,38.11371000000008],[-123.72720000000004,38.95166000000012],[-123.86516999999998,39.76699000000008],[-124.39807000000002,40.313199999999995],[-124.17885999999999,41.142020000000116],[-124.21370000000002,41.99964000000011],[-124.53283999999996,42.7659900000001],[-124.14213999999998,43.708380000000034],[-124.020535,44.615894999999966],[-123.89892999999995,45.52341000000007],[-124.079635,46.864750000000015],[-124.39566999999994,47.72017000000011],[-124.68721008300781,48.18443298339855],[-124.56610107421875,48.37971496582037],[-123.12,48.04000000000002],[-122.58735999999993,47.09600000000006],[-122.34000000000003,47.360000000000014],[-122.5,48.180000000000064],[-122.84000000000003,49.000000000000114]]],[[[-155.40214,20.07975],[-155.22452,19.99302],[-155.06226,19.8591],[-154.80741,19.50871],[-154.83147,19.453280000000003],[-155.22217,19.23972],[-155.54211,19.08348],[-155.68817,18.91619],[-155.93665,19.05939],[-155.90806,19.33888],[-156.07347000000001,19.70294],[-156.02368,19.81422],[-155.85008000000002,19.97729],[-155.91907,20.17395],[-155.86108000000002,20.267210000000002],[-155.78505,20.2487],[-155.40214,20.07975]]],[[[-155.99566000000002,20.76404],[-156.07926,20.643970000000003],[-156.41445,20.57241],[-156.58673,20.783],[-156.70167,20.8643],[-156.71054999999998,20.92676],[-156.61258,21.01249],[-156.25711,20.917450000000002],[-155.99566000000002,20.76404]]],[[[-156.75824,21.176840000000002],[-156.78933,21.068730000000002],[-157.32521,21.097770000000004],[-157.25027,21.219579999999997],[-156.75824,21.176840000000002]]],[[[-158.0252,21.71696],[-157.94161,21.65272],[-157.65283000000002,21.322170000000003],[-157.70703,21.26442],[-157.7786,21.27729],[-158.12667000000002,21.31244],[-158.2538,21.53919],[-158.29265,21.57912],[-158.0252,21.71696]]],[[[-159.36569,22.21494],[-159.34512,21.982000000000003],[-159.46372,21.88299],[-159.80051,22.065330000000003],[-159.74877,22.1382],[-159.5962,22.236179999999997],[-159.36569,22.21494]]],[[[-166.46779212142462,60.384169826897754],[-165.67442969466364,60.29360687930625],[-165.57916419173358,59.90998688418753],[-166.19277014876727,59.75444082298899],[-166.84833736882197,59.941406155020985],[-167.45527706609008,60.21306915957936],[-166.46779212142462,60.384169826897754]]],[[[-153.22872941792113,57.96896841087248],[-152.56479061583514,57.901427313866996],[-152.1411472239064,57.591058661522],[-153.00631405333692,57.11584219016593],[-154.0050902984581,56.734676825581076],[-154.51640275777004,56.99274892844669],[-154.67099280497118,57.46119578717253],[-153.7627795074415,57.81657461204373],[-153.22872941792113,57.96896841087248]]],[[[-140.98598761037601,69.71199839952635],[-140.986,69.712],[-140.9925,66.00003000000001],[-140.99778,60.30639000000001],[-140.013,60.27682000000001],[-139.03900000000002,60],[-138.34089,59.562110000000004],[-137.4525,58.905],[-136.47972000000004,59.46389000000005],[-135.47583,59.787780000000005],[-134.94500000000005,59.2705600000001],[-134.27111000000002,58.86111000000005],[-133.35556000000003,58.41028000000001],[-132.73042,57.692890000000006],[-131.70781,56.55212],[-130.00778000000003,55.915830000000085],[-129.98,55.285000000000004],[-130.53611,54.802780000000006],[-130.53610895273684,54.80275447679924],[-130.5361101894673,54.8027534043494],[-131.08581823797215,55.17890615500204],[-131.9672114671423,55.497775580459006],[-132.2500107428595,56.3699962428974],[-133.53918108435641,57.17888743756214],[-134.07806292029608,58.12306753196691],[-135.0382110322791,58.18771474876394],[-136.62806230995471,58.21220937767043],[-137.800006279686,58.49999542910376],[-139.867787041413,59.53776154238915],[-140.825273817133,59.727517401765056],[-142.57444353556446,60.08444651960497],[-143.9588809948799,59.999180406323376],[-145.92555681682788,60.45860972761426],[-147.11437394914665,60.884656073644635],[-148.22430620012761,60.67298940697714],[-148.01806555885082,59.97832896589364],[-148.57082251686086,59.914172675203304],[-149.72785783587585,59.70565827090553],[-150.60824337461642,59.368211168039466],[-151.7163927886833,59.15582103131993],[-151.85943315326722,59.744984035879554],[-151.40971900124717,60.72580272077937],[-150.3469414947325,61.03358755150987],[-150.62111080625704,61.2844249538544],[-151.89583919981683,60.727197984451266],[-152.57832984109558,60.061657212964235],[-154.01917212625764,59.35027944603428],[-153.28751135965317,58.86472768821977],[-154.23249243875847,58.14637360293051],[-155.3074914215102,57.727794501366304],[-156.30833472392305,57.422774359763594],[-156.55609737854638,56.97998484967064],[-158.11721655986779,56.46360809999419],[-158.43332129619714,55.99415355083852],[-159.60332739971741,55.56668610292013],[-160.28971961163427,55.643580634170576],[-161.22304765525777,55.364734605523495],[-162.23776607974105,55.02418691672011],[-163.06944658104638,54.68973704692712],[-164.78556922102717,54.40417308208214],[-164.94222632552007,54.57222483989534],[-163.84833960676565,55.03943146424609],[-162.87000139061595,55.34804311789321],[-161.80417497459607,55.89498647727038],[-160.5636047027812,56.00805451112501],[-160.07055986228448,56.41805532492873],[-158.6844429189195,57.01667511659787],[-158.46109737855403,57.21692129172885],[-157.72277035218391,57.57000051536306],[-157.55027442119362,58.328326321030204],[-157.04167497457698,58.91888458926172],[-158.19473120830554,58.61580231386978],[-158.51721798402303,58.78778148053732],[-159.0586061269288,58.42418610293163],[-159.71166704001737,58.93139028587632],[-159.98128882550017,58.572549140041644],[-160.3552711659965,59.07112335879361],[-161.3550034251151,58.670837714260756],[-161.96889360252632,58.67166453717738],[-162.05498653872465,59.26692536074745],[-161.8741707021354,59.63362132429057],[-162.51805904849212,59.98972361921386],[-163.8183414378202,59.79805573184336],[-164.66221757714652,60.26748444278263],[-165.3463877024748,60.50749563256238],[-165.3508318756519,61.073895168697504],[-166.12137915755602,61.50001902937623],[-165.73445187077058,62.074996853271784],[-164.9191786367179,62.63307648380794],[-164.56250790103934,63.14637848576302],[-163.75333248599708,63.21944896102377],[-163.06722449445786,63.05945872664802],[-162.26055538638175,63.54193573674115],[-161.53444983624863,63.455816962326764],[-160.7725066803211,63.766108100023246],[-160.9583351308426,64.22279857040274],[-161.51806840721218,64.40278758407527],[-160.77777767641481,64.78860382756642],[-161.39192623598765,64.77723501246231],[-162.4530500966689,64.55944468856819],[-162.75778601789415,64.33860545516876],[-163.54639421288428,64.5591604681905],[-164.96082984114514,64.44694509546883],[-166.42528825586447,64.68667206487066],[-166.8450042389391,65.08889557561452],[-168.11056006576715,65.66999705673675],[-166.70527116602193,66.08831777613938],[-164.47470964257548,66.5766600612975],[-163.65251176659564,66.5766600612975],[-163.78860165103623,66.07720734319668],[-161.67777442121013,66.11611969671242],[-162.48971452538004,66.73556509059512],[-163.71971696679117,67.11639455837008],[-164.4309913808565,67.61633820257777],[-165.39028683170673,68.04277212185025],[-166.76444068099605,68.35887685817966],[-166.20470740462667,68.88303091091615],[-164.43081051334346,68.91553538682774],[-163.1686136546145,69.37111481391287],[-162.930566169262,69.85806183539927],[-161.90889726463556,70.33332998318764],[-160.93479651593367,70.44768992784958],[-159.03917578838713,70.89164215766891],[-158.11972286683394,70.82472117785102],[-156.58082455139808,71.35776357694175],[-155.06779029032427,71.14777639432367],[-154.3441652089412,70.69640859647018],[-153.9000062733926,70.88998851183567],[-152.21000606993528,70.82999217394485],[-152.27000240782613,70.60000621202983],[-150.73999243874448,70.43001658800569],[-149.7200030181675,70.53001048449045],[-147.61336157935705,70.2140349392418],[-145.68998980022533,70.12000967068673],[-144.9200109590764,69.98999176704046],[-143.58944618042523,70.15251414659832],[-142.07251034871348,69.85193817817265],[-140.98598752156073,69.71199839952635],[-140.98598761037601,69.71199839952635]]],[[[-171.73165686753944,63.782515367275934],[-171.1144335602453,63.59219106714495],[-170.4911124339407,63.694975490973505],[-169.6825054596536,63.43111562769119],[-168.6894394603007,63.297506212000556],[-168.77194088445466,63.18859813094544],[-169.5294398672051,62.97693146427792],[-170.29055620021595,63.194437567794424],[-170.67138566799093,63.3758218451389],[-171.55306311753873,63.317789211675105],[-171.79111060289122,63.40584585230046],[-171.73165686753944,63.782515367275934]]]]},"properties":{"bbox":{"xmax":-66.96465999999998,"xmin":-171.79111060289122,"ymax":71.35776357694175,"ymin":18.91619},"continent":"North America","gdp_md_est":21433226,"iso_a3":"USA","name":"United States of America","pop_est":328239523},"type":"Feature"} 6 | ]} -------------------------------------------------------------------------------- /test/files/polys.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyparam/geoparquet/a8e1b4b7647e2db7606fec1af4eecb5da2fd9a81/test/files/polys.parquet -------------------------------------------------------------------------------- /test/package.test.js: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import packageJson from '../package.json' with { type: 'json' } 3 | 4 | describe('package.json', () => { 5 | it('should have the correct name', () => { 6 | expect(packageJson.name).toBe('geoparquet') 7 | }) 8 | it('should have a valid version', () => { 9 | expect(packageJson.version).toMatch(/^\d+\.\d+\.\d+$/) 10 | }) 11 | it('should have MIT license', () => { 12 | expect(packageJson.license).toBe('MIT') 13 | }) 14 | it('should have precise dependency versions', () => { 15 | const { dependencies, devDependencies } = packageJson 16 | const allDependencies = { ...dependencies, ...devDependencies } 17 | Object.values(allDependencies).forEach(version => { 18 | expect(version).toMatch(/^\d+\.\d+\.\d+$/) 19 | }) 20 | }) 21 | it('should have no peer dependencies', () => { 22 | expect('peerDependencies' in packageJson).toBe(false) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/toGeoJson.test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { asyncBufferFromFile } from 'hyparquet' 3 | import { compressors } from 'hyparquet-compressors' 4 | import { describe, expect, it } from 'vitest' 5 | import { toGeoJson } from '../src/index.js' 6 | 7 | describe('toGeoJson parse test files', () => { 8 | const files = fs.readdirSync('test/files').filter(f => f.endsWith('.parquet')) 9 | 10 | files.forEach(filename => { 11 | it(`parse data from ${filename}`, async () => { 12 | const base = filename.replace('.parquet', '') 13 | const file = await asyncBufferFromFile(`test/files/${filename}`) 14 | const geojson = await toGeoJson({ file }) 15 | const expected = fileToJson(`test/files/${base}.json`) 16 | expect(geojson).toEqual(expected) 17 | }) 18 | }) 19 | 20 | // Parse compressed parquet files 21 | const compressedFiles = fs.readdirSync('test/files/compressed') 22 | compressedFiles.forEach(filename => { 23 | it(`parse data from compressed ${filename}`, async () => { 24 | const file = await asyncBufferFromFile(`test/files/compressed/${filename}`) 25 | const geojson = await toGeoJson({ file, compressors }) 26 | const expected = fileToJson('test/files/example.json') 27 | expect(geojson).toEqual(expected) 28 | }) 29 | }) 30 | }) 31 | 32 | /** 33 | * @param {string} filePath 34 | * @returns {any} 35 | */ 36 | function fileToJson(filePath) { 37 | const buffer = fs.readFileSync(filePath) 38 | return JSON.parse(buffer.toString()) 39 | } 40 | -------------------------------------------------------------------------------- /test/wkb.test.js: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { decodeWKB } from '../src/wkb.js' 3 | 4 | describe('WKB decoding', () => { 5 | it('should decoding well-known binary Point', () => { 6 | const buffer = new Uint8Array([ 7 | 1, // little endian 8 | 1, 0, 0, 0, // type Point 9 | 0, 0, 0, 0, 0, 128, 89, 64, // 102 10 | 0, 0, 0, 0, 0, 0, 224, 63, // 0.5 11 | ]) 12 | const json = decodeWKB(buffer) 13 | const expected = { 14 | type: 'Point', 15 | coordinates: [102, 0.5], 16 | } 17 | expect(json).toEqual(expected) 18 | }) 19 | 20 | it('should decoding well-known binary MultiLineString', () => { 21 | // from data-multilinestring-encoding_wkb.parquet 22 | const buffer = new Uint8Array([ 23 | 1, // little endian 24 | 5, 0, 0, 0, // type MultiLineString 25 | 2, 0, 0, 0, // num linestrings 26 | 1, 27 | 2, 0, 0, 0, 28 | 3, 0, 0, 0, // 3 points 29 | 0, 0, 0, 0, 0, 0, 36, 64, // 10 30 | 0, 0, 0, 0, 0, 0, 36, 64, // 10 31 | 0, 0, 0, 0, 0, 0, 52, 64, // 20 32 | 0, 0, 0, 0, 0, 0, 52, 64, // 20 33 | 0, 0, 0, 0, 0, 0, 36, 64, // 10 34 | 0, 0, 0, 0, 0, 0, 68, 64, // 40 35 | 1, 36 | 2, 0, 0, 0, 37 | 4, 0, 0, 0, // 4 points 38 | 0, 0, 0, 0, 0, 0, 68, 64, // 40 39 | 0, 0, 0, 0, 0, 0, 68, 64, // 40 40 | 0, 0, 0, 0, 0, 0, 62, 64, // 30 41 | 0, 0, 0, 0, 0, 0, 62, 64, // 30 42 | 0, 0, 0, 0, 0, 0, 68, 64, // 40 43 | 0, 0, 0, 0, 0, 0, 52, 64, // 20 44 | 0, 0, 0, 0, 0, 0, 62, 64, // 30 45 | 0, 0, 0, 0, 0, 0, 36, 64, // 10 46 | ]) 47 | const json = decodeWKB(buffer) 48 | const expected = { 49 | type: 'MultiLineString', 50 | coordinates: [ 51 | [[10, 10], [20, 20], [10, 40]], 52 | [[40, 40], [30, 30], [40, 20], [30, 10]], 53 | ], 54 | } 55 | expect(json).toEqual(expected) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "declaration": true, 6 | "module": "nodenext", 7 | "noEmit": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | }, 12 | "include": ["src", "demo", "test"], 13 | } 14 | --------------------------------------------------------------------------------