geojson-vt
Advanced tools
Comparing version 2.4.0 to 3.0.0
@@ -1,1 +0,1 @@ | ||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.geojsonvt=e()}}(function(){return function e(t,n,r){function o(s,u){if(!n[s]){if(!t[s]){var l="function"==typeof require&&require;if(!u&&l)return l(s,!0);if(i)return i(s,!0);var a=new Error("Cannot find module '"+s+"'");throw a.code="MODULE_NOT_FOUND",a}var f=n[s]={exports:{}};t[s][0].call(f.exports,function(e){var n=t[s][1][e];return o(n?n:e)},f,f.exports,e,t,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)o(r[s]);return o}({1:[function(e,t,n){"use strict";function r(e,t,n,r,s,l,a,f){if(n/=t,r/=t,a>=n&&r>=f)return e;if(a>r||n>f)return null;for(var h=[],p=0;p<e.length;p++){var m,c,d=e[p],g=d.geometry,v=d.type;if(m=d.min[s],c=d.max[s],m>=n&&r>=c)h.push(d);else if(!(m>r||n>c)){var x=1===v?o(g,n,r,s):i(g,n,r,s,l,3===v);x.length&&h.push(u(d.tags,v,x,d.id))}}return h.length?h:null}function o(e,t,n,r){for(var o=[],i=0;i<e.length;i++){var s=e[i],u=s[r];u>=t&&n>=u&&o.push(s)}return o}function i(e,t,n,r,o,i){for(var u=[],l=0;l<e.length;l++){var a,f,h,p=0,m=0,c=null,d=e[l],g=d.area,v=d.dist,x=d.outer,y=d.length,M=[];for(f=0;y-1>f;f++)a=c||d[f],c=d[f+1],p=m||a[r],m=c[r],t>p?m>n?(M.push(o(a,c,t),o(a,c,n)),i||(M=s(u,M,g,v,x))):m>=t&&M.push(o(a,c,t)):p>n?t>m?(M.push(o(a,c,n),o(a,c,t)),i||(M=s(u,M,g,v,x))):n>=m&&M.push(o(a,c,n)):(M.push(a),t>m?(M.push(o(a,c,t)),i||(M=s(u,M,g,v,x))):m>n&&(M.push(o(a,c,n)),i||(M=s(u,M,g,v,x))));a=d[y-1],p=a[r],p>=t&&n>=p&&M.push(a),h=M[M.length-1],i&&h&&(M[0][0]!==h[0]||M[0][1]!==h[1])&&M.push(M[0]),s(u,M,g,v,x)}return u}function s(e,t,n,r,o){return t.length&&(t.area=n,t.dist=r,void 0!==o&&(t.outer=o),e.push(t)),[]}t.exports=r;var u=e("./feature")},{"./feature":3}],2:[function(e,t,n){"use strict";function r(e,t){var n=[];if("FeatureCollection"===e.type)for(var r=0;r<e.features.length;r++)o(n,e.features[r],t);else"Feature"===e.type?o(n,e,t):o(n,{geometry:e},t);return n}function o(e,t,n){if(null!==t.geometry){var r,u,l,f,h=t.geometry,p=h.type,m=h.coordinates,c=t.properties,d=t.id;if("Point"===p)e.push(a(c,1,[s(m)],d));else if("MultiPoint"===p)e.push(a(c,1,i(m),d));else if("LineString"===p)e.push(a(c,2,[i(m,n)],d));else if("MultiLineString"===p||"Polygon"===p){for(l=[],r=0;r<m.length;r++)f=i(m[r],n),"Polygon"===p&&(f.outer=0===r),l.push(f);e.push(a(c,"Polygon"===p?3:2,l,d))}else if("MultiPolygon"===p){for(l=[],r=0;r<m.length;r++)for(u=0;u<m[r].length;u++)f=i(m[r][u],n),f.outer=0===u,l.push(f);e.push(a(c,3,l,d))}else{if("GeometryCollection"!==p)throw new Error("Input data is not a valid GeoJSON object.");for(r=0;r<h.geometries.length;r++)o(e,{geometry:h.geometries[r],properties:c},n)}}}function i(e,t){for(var n=[],r=0;r<e.length;r++)n.push(s(e[r]));return t&&(l(n,t),u(n)),n}function s(e){var t=Math.sin(e[1]*Math.PI/180),n=e[0]/360+.5,r=.5-.25*Math.log((1+t)/(1-t))/Math.PI;return r=0>r?0:r>1?1:r,[n,r,0]}function u(e){for(var t,n,r=0,o=0,i=0;i<e.length-1;i++)t=n||e[i],n=e[i+1],r+=t[0]*n[1]-n[0]*t[1],o+=Math.abs(n[0]-t[0])+Math.abs(n[1]-t[1]);e.area=Math.abs(r/2),e.dist=o}t.exports=r;var l=e("./simplify"),a=e("./feature")},{"./feature":3,"./simplify":5}],3:[function(e,t,n){"use strict";function r(e,t,n,r){var i={id:r||null,type:t,geometry:n,tags:e||null,min:[1/0,1/0],max:[-(1/0),-(1/0)]};return o(i),i}function o(e){var t=e.geometry,n=e.min,r=e.max;if(1===e.type)i(n,r,t);else for(var o=0;o<t.length;o++)i(n,r,t[o]);return e}function i(e,t,n){for(var r,o=0;o<n.length;o++)r=n[o],e[0]=Math.min(r[0],e[0]),t[0]=Math.max(r[0],t[0]),e[1]=Math.min(r[1],e[1]),t[1]=Math.max(r[1],t[1])}t.exports=r},{}],4:[function(e,t,n){"use strict";function r(e,t){return new o(e,t)}function o(e,t){t=this.options=l(Object.create(this.options),t);var n=t.debug;n&&console.time("preprocess data");var r=1<<t.maxZoom,o=f(e,t.tolerance/(r*t.extent));this.tiles={},this.tileCoords=[],n&&(console.timeEnd("preprocess data"),console.log("index: maxZoom: %d, maxPoints: %d",t.indexMaxZoom,t.indexMaxPoints),console.time("generate tiles"),this.stats={},this.total=0),o=m(o,t.buffer/t.extent,s),o.length&&this.splitTile(o,0,0,0),n&&(o.length&&console.log("features: %d, points: %d",this.tiles[0].numFeatures,this.tiles[0].numPoints),console.timeEnd("generate tiles"),console.log("tiles generated:",this.total,JSON.stringify(this.stats)))}function i(e,t,n){return 32*((1<<e)*n+t)+e}function s(e,t,n){return[n,(n-e[0])*(t[1]-e[1])/(t[0]-e[0])+e[1],1]}function u(e,t,n){return[(n-e[1])*(t[0]-e[0])/(t[1]-e[1])+e[0],n,1]}function l(e,t){for(var n in t)e[n]=t[n];return e}function a(e,t,n){var r=e.source;if(1!==r.length)return!1;var o=r[0];if(3!==o.type||o.geometry.length>1)return!1;var i=o.geometry[0].length;if(5!==i)return!1;for(var s=0;i>s;s++){var u=h.point(o.geometry[0][s],t,e.z2,e.x,e.y);if(u[0]!==-n&&u[0]!==t+n||u[1]!==-n&&u[1]!==t+n)return!1}return!0}t.exports=r;var f=e("./convert"),h=e("./transform"),p=e("./clip"),m=e("./wrap"),c=e("./tile");o.prototype.options={maxZoom:14,indexMaxZoom:5,indexMaxPoints:1e5,solidChildren:!1,tolerance:3,extent:4096,buffer:64,debug:0},o.prototype.splitTile=function(e,t,n,r,o,l,f){for(var h=[e,t,n,r],m=this.options,d=m.debug,g=null;h.length;){r=h.pop(),n=h.pop(),t=h.pop(),e=h.pop();var v=1<<t,x=i(t,n,r),y=this.tiles[x],M=t===m.maxZoom?0:m.tolerance/(v*m.extent);if(!y&&(d>1&&console.time("creation"),y=this.tiles[x]=c(e,v,n,r,M,t===m.maxZoom),this.tileCoords.push({z:t,x:n,y:r}),d)){d>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",t,n,r,y.numFeatures,y.numPoints,y.numSimplified),console.timeEnd("creation"));var P="z"+t;this.stats[P]=(this.stats[P]||0)+1,this.total++}if(y.source=e,o){if(t===m.maxZoom||t===o)continue;var b=1<<o-t;if(n!==Math.floor(l/b)||r!==Math.floor(f/b))continue}else if(t===m.indexMaxZoom||y.numPoints<=m.indexMaxPoints)continue;if(m.solidChildren||!a(y,m.extent,m.buffer)){y.source=null,d>1&&console.time("clipping");var w,Z,z,E,S,C,F=.5*m.buffer/m.extent,O=.5-F,T=.5+F,j=1+F;w=Z=z=E=null,S=p(e,v,n-F,n+T,0,s,y.min[0],y.max[0]),C=p(e,v,n+O,n+j,0,s,y.min[0],y.max[0]),S&&(w=p(S,v,r-F,r+T,1,u,y.min[1],y.max[1]),Z=p(S,v,r+O,r+j,1,u,y.min[1],y.max[1])),C&&(z=p(C,v,r-F,r+T,1,u,y.min[1],y.max[1]),E=p(C,v,r+O,r+j,1,u,y.min[1],y.max[1])),d>1&&console.timeEnd("clipping"),e.length&&(h.push(w||[],t+1,2*n,2*r),h.push(Z||[],t+1,2*n,2*r+1),h.push(z||[],t+1,2*n+1,2*r),h.push(E||[],t+1,2*n+1,2*r+1))}else o&&(g=t)}return g},o.prototype.getTile=function(e,t,n){var r=this.options,o=r.extent,s=r.debug,u=1<<e;t=(t%u+u)%u;var l=i(e,t,n);if(this.tiles[l])return h.tile(this.tiles[l],o);s>1&&console.log("drilling down to z%d-%d-%d",e,t,n);for(var f,p=e,m=t,c=n;!f&&p>0;)p--,m=Math.floor(m/2),c=Math.floor(c/2),f=this.tiles[i(p,m,c)];if(!f||!f.source)return null;if(s>1&&console.log("found parent tile z%d-%d-%d",p,m,c),a(f,o,r.buffer))return h.tile(f,o);s>1&&console.time("drilling down");var d=this.splitTile(f.source,p,m,c,e,t,n);if(s>1&&console.timeEnd("drilling down"),null!==d){var g=1<<e-d;l=i(d,Math.floor(t/g),Math.floor(n/g))}return this.tiles[l]?h.tile(this.tiles[l],o):null}},{"./clip":1,"./convert":2,"./tile":6,"./transform":7,"./wrap":8}],5:[function(e,t,n){"use strict";function r(e,t){var n,r,i,s,u=t*t,l=e.length,a=0,f=l-1,h=[];for(e[a][2]=1,e[f][2]=1;f;){for(r=0,n=a+1;f>n;n++)i=o(e[n],e[a],e[f]),i>r&&(s=n,r=i);r>u?(e[s][2]=r,h.push(a),h.push(s),a=s):(f=h.pop(),a=h.pop())}}function o(e,t,n){var r=t[0],o=t[1],i=n[0],s=n[1],u=e[0],l=e[1],a=i-r,f=s-o;if(0!==a||0!==f){var h=((u-r)*a+(l-o)*f)/(a*a+f*f);h>1?(r=i,o=s):h>0&&(r+=a*h,o+=f*h)}return a=u-r,f=l-o,a*a+f*f}t.exports=r},{}],6:[function(e,t,n){"use strict";function r(e,t,n,r,i,s){for(var u={features:[],numPoints:0,numSimplified:0,numFeatures:0,source:null,x:n,y:r,z2:t,transformed:!1,min:[2,1],max:[-1,0]},l=0;l<e.length;l++){u.numFeatures++,o(u,e[l],i,s);var a=e[l].min,f=e[l].max;a[0]<u.min[0]&&(u.min[0]=a[0]),a[1]<u.min[1]&&(u.min[1]=a[1]),f[0]>u.max[0]&&(u.max[0]=f[0]),f[1]>u.max[1]&&(u.max[1]=f[1])}return u}function o(e,t,n,r){var o,s,u,l,a=t.geometry,f=t.type,h=[],p=n*n;if(1===f)for(o=0;o<a.length;o++)h.push(a[o]),e.numPoints++,e.numSimplified++;else for(o=0;o<a.length;o++)if(u=a[o],r||!(2===f&&u.dist<n||3===f&&u.area<p)){var m=[];for(s=0;s<u.length;s++)l=u[s],(r||l[2]>p)&&(m.push(l),e.numSimplified++),e.numPoints++;3===f&&i(m,u.outer),h.push(m)}else e.numPoints+=u.length;if(h.length){var c={geometry:h,type:f,tags:t.tags||null};null!==t.id&&(c.id=t.id),e.features.push(c)}}function i(e,t){var n=s(e);0>n===t&&e.reverse()}function s(e){for(var t,n,r=0,o=0,i=e.length,s=i-1;i>o;s=o++)t=e[o],n=e[s],r+=(n[0]-t[0])*(t[1]+n[1]);return r}t.exports=r},{}],7:[function(e,t,n){"use strict";function r(e,t){if(e.transformed)return e;var n,r,i,s=e.z2,u=e.x,l=e.y;for(n=0;n<e.features.length;n++){var a=e.features[n],f=a.geometry,h=a.type;if(1===h)for(r=0;r<f.length;r++)f[r]=o(f[r],t,s,u,l);else for(r=0;r<f.length;r++){var p=f[r];for(i=0;i<p.length;i++)p[i]=o(p[i],t,s,u,l)}}return e.transformed=!0,e}function o(e,t,n,r,o){var i=Math.round(t*(e[0]*n-r)),s=Math.round(t*(e[1]*n-o));return[i,s]}n.tile=r,n.point=o},{}],8:[function(e,t,n){"use strict";function r(e,t,n){var r=e,i=s(e,1,-1-t,t,0,n,-1,2),u=s(e,1,1-t,2+t,0,n,-1,2);return(i||u)&&(r=s(e,1,-t,1+t,0,n,-1,2)||[],i&&(r=o(i,1).concat(r)),u&&(r=r.concat(o(u,-1)))),r}function o(e,t){for(var n=[],r=0;r<e.length;r++){var o,s=e[r],l=s.type;if(1===l)o=i(s.geometry,t);else{o=[];for(var a=0;a<s.geometry.length;a++)o.push(i(s.geometry[a],t))}n.push(u(s.tags,l,o,s.id))}return n}function i(e,t){var n=[];n.area=e.area,n.dist=e.dist;for(var r=0;r<e.length;r++)n.push([e[r][0]+t,e[r][1],e[r][2]]);return n}var s=e("./clip"),u=e("./feature");t.exports=r},{"./clip":1,"./feature":3}]},{},[4])(4)}); | ||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).geojsonvt=e()}}(function(){return function e(t,n,i){function o(s,u){if(!n[s]){if(!t[s]){var l="function"==typeof require&&require;if(!u&&l)return l(s,!0);if(r)return r(s,!0);var a=new Error("Cannot find module '"+s+"'");throw a.code="MODULE_NOT_FOUND",a}var f=n[s]={exports:{}};t[s][0].call(f.exports,function(e){var n=t[s][1][e];return o(n||e)},f,f.exports,e,t,n,i)}return n[s].exports}for(var r="function"==typeof require&&require,s=0;s<i.length;s++)o(i[s]);return o}({1:[function(e,t,n){"use strict";function i(e,t,n,i,o){for(var r=0;r<e.length;r+=3){var s=e[r+o];s>=n&&s<=i&&(t.push(e[r]),t.push(e[r+1]),t.push(e[r+2]))}}function o(e,t,n,i,o,r){for(var a=[],f=0===o?u:l,h=0;h<e.length-3;h+=3){var m=e[h],p=e[h+1],g=e[h+2],c=e[h+3],d=e[h+4],v=0===o?m:p,x=0===o?c:d,y=!1;v<n?x>=n&&f(a,m,p,c,d,n):v>i?x<=i&&f(a,m,p,c,d,i):s(a,m,p,g),x<n&&v>=n&&(f(a,m,p,c,d,n),y=!0),x>i&&v<=i&&(f(a,m,p,c,d,i),y=!0),!r&&y&&(a.size=e.size,t.push(a),a=[])}var M=e.length-3;m=e[M],p=e[M+1],g=e[M+2],(v=0===o?m:p)>=n&&v<=i&&s(a,m,p,g),M=a.length-3,r&&M>=3&&(a[M]!==a[0]||a[M+1]!==a[1])&&s(a,a[0],a[1],a[2]),a.length&&(a.size=e.size,t.push(a))}function r(e,t,n,i,r,s){for(var u=0;u<e.length;u++)o(e[u],t,n,i,r,s)}function s(e,t,n,i){e.push(t),e.push(n),e.push(i)}function u(e,t,n,i,o,r){e.push(r),e.push(n+(r-t)*(o-n)/(i-t)),e.push(1)}function l(e,t,n,i,o,r){e.push(t+(r-n)*(i-t)/(o-n)),e.push(r),e.push(1)}t.exports=function(e,t,n,s,u,l,f){if(n/=t,s/=t,l>=n&&f<=s)return e;if(l>s||f<n)return null;for(var h=[],m=0;m<e.length;m++){var p=e[m],g=p.geometry,c=p.type,d=0===u?p.minX:p.minY,v=0===u?p.maxX:p.maxY;if(d>=n&&v<=s)h.push(p);else if(!(d>s||v<n)){var x=[];if("Point"===c||"MultiPoint"===c)i(g,x,n,s,u);else if("LineString"===c)o(g,x,n,s,u,!1);else if("MultiLineString"===c)r(g,x,n,s,u,!1);else if("Polygon"===c)r(g,x,n,s,u,!0);else if("MultiPolygon"===c)for(var y=0;y<g.length;y++){var M=[];r(g[y],M,n,s,u,!0),M.length&&x.push(M)}x.length&&("LineString"!==c&&"MultiLineString"!==c||(1===x.length?(c="LineString",x=x[0]):c="MultiLineString"),"Point"!==c&&"MultiPoint"!==c||(c=3===x.length?"Point":"MultiPoint"),h.push(a(p.id,c,x,p.tags)))}}return h.length?h:null};var a=e("./feature")},{"./feature":3}],2:[function(e,t,n){"use strict";function i(e,t,n){if(t.geometry){var u=t.geometry.coordinates,l=t.geometry.type,a=n*n,h=[];if("Point"===l)o(u,h);else if("MultiPoint"===l)for(var m=0;m<u.length;m++)o(u[m],h);else if("LineString"===l)r(u,h,a,!1);else if("MultiLineString"===l)s(u,h,a,!1);else if("Polygon"===l)s(u,h,a,!0);else{if("MultiPolygon"!==l){if("GeometryCollection"===l){for(m=0;m<t.geometry.geometries.length;m++)i(e,{geometry:t.geometry.geometries[m],properties:t.properties},n);return}throw new Error("Input data is not a valid GeoJSON object.")}for(m=0;m<u.length;m++){var p=[];s(u[m],p,a,!0),h.push(p)}}e.push(f(t.id,l,h,t.properties))}}function o(e,t){t.push(u(e[0])),t.push(l(e[1])),t.push(0)}function r(e,t,n,i){for(var o,r,s=0,f=0;f<e.length;f++){var h=u(e[f][0]),m=l(e[f][1]);t.push(h),t.push(m),t.push(0),f>0&&(s+=i?(o*m-h*r)/2:Math.sqrt(Math.pow(h-o,2)+Math.pow(m-r,2))),o=h,r=m}var p=t.length-3;t[2]=1,a(t,0,p,n),t[p+2]=1,t.size=Math.abs(s)}function s(e,t,n,i){for(var o=0;o<e.length;o++){var s=[];r(e[o],s,n,i),t.push(s)}}function u(e){return e/360+.5}function l(e){var t=Math.sin(e*Math.PI/180),n=.5-.25*Math.log((1+t)/(1-t))/Math.PI;return n<0?0:n>1?1:n}t.exports=function(e,t){var n=[];if("FeatureCollection"===e.type)for(var o=0;o<e.features.length;o++)i(n,e.features[o],t);else"Feature"===e.type?i(n,e,t):i(n,{geometry:e},t);return n};var a=e("./simplify"),f=e("./feature")},{"./feature":3,"./simplify":5}],3:[function(e,t,n){"use strict";function i(e){var t=e.geometry,n=e.type;if("Point"===n||"MultiPoint"===n||"LineString"===n)o(e,t);else if("Polygon"===n||"MultiLineString"===n)for(var i=0;i<t.length;i++)o(e,t[i]);else if("MultiPolygon"===n)for(i=0;i<t.length;i++)for(var r=0;r<t[i].length;r++)o(e,t[i][r])}function o(e,t){for(var n=0;n<t.length;n+=3)e.minX=Math.min(e.minX,t[n]),e.minY=Math.min(e.minY,t[n+1]),e.maxX=Math.max(e.maxX,t[n]),e.maxY=Math.max(e.maxY,t[n+1])}t.exports=function(e,t,n,o){var r={id:e||null,type:t,geometry:n,tags:o,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0};return i(r),r}},{}],4:[function(e,t,n){"use strict";function i(e,t){var n=(t=this.options=r(Object.create(this.options),t)).debug;if(n&&console.time("preprocess data"),t.maxZoom<0||t.maxZoom>24)throw new Error("maxZoom should be in the 0-24 range");var i=1<<t.maxZoom,o=s(e,t.tolerance/(i*t.extent));this.tiles={},this.tileCoords=[],n&&(console.timeEnd("preprocess data"),console.log("index: maxZoom: %d, maxPoints: %d",t.indexMaxZoom,t.indexMaxPoints),console.time("generate tiles"),this.stats={},this.total=0),(o=a(o,t.buffer/t.extent)).length&&this.splitTile(o,0,0,0),n&&(o.length&&console.log("features: %d, points: %d",this.tiles[0].numFeatures,this.tiles[0].numPoints),console.timeEnd("generate tiles"),console.log("tiles generated:",this.total,JSON.stringify(this.stats)))}function o(e,t,n){return 32*((1<<e)*n+t)+e}function r(e,t){for(var n in t)e[n]=t[n];return e}t.exports=function(e,t){return new i(e,t)};var s=e("./convert"),u=e("./transform"),l=e("./clip"),a=e("./wrap"),f=e("./tile");i.prototype.options={maxZoom:14,indexMaxZoom:5,indexMaxPoints:1e5,tolerance:3,extent:4096,buffer:64,debug:0},i.prototype.splitTile=function(e,t,n,i,r,s,u){for(var a=[e,t,n,i],h=this.options,m=h.debug;a.length;){i=a.pop(),n=a.pop(),t=a.pop(),e=a.pop();var p=1<<t,g=o(t,n,i),c=this.tiles[g],d=t===h.maxZoom?0:h.tolerance/(p*h.extent);if(!c&&(m>1&&console.time("creation"),c=this.tiles[g]=f(e,p,n,i,d,t===h.maxZoom),this.tileCoords.push({z:t,x:n,y:i}),m)){m>1&&(console.log("tile z%d-%d-%d (features: %d, points: %d, simplified: %d)",t,n,i,c.numFeatures,c.numPoints,c.numSimplified),console.timeEnd("creation"));var v="z"+t;this.stats[v]=(this.stats[v]||0)+1,this.total++}if(c.source=e,r){if(t===h.maxZoom||t===r)continue;var x=1<<r-t;if(n!==Math.floor(s/x)||i!==Math.floor(u/x))continue}else if(t===h.indexMaxZoom||c.numPoints<=h.indexMaxPoints)continue;if(c.source=null,0!==e.length){m>1&&console.time("clipping");var y,M,P,Y,S,X,w=.5*h.buffer/h.extent,L=.5-w,z=.5+w,b=1+w;y=M=P=Y=null,S=l(e,p,n-w,n+z,0,c.minX,c.maxX),X=l(e,p,n+L,n+b,0,c.minX,c.maxX),e=null,S&&(y=l(S,p,i-w,i+z,1,c.minY,c.maxY),M=l(S,p,i+L,i+b,1,c.minY,c.maxY),S=null),X&&(P=l(X,p,i-w,i+z,1,c.minY,c.maxY),Y=l(X,p,i+L,i+b,1,c.minY,c.maxY),X=null),m>1&&console.timeEnd("clipping"),a.push(y||[],t+1,2*n,2*i),a.push(M||[],t+1,2*n,2*i+1),a.push(P||[],t+1,2*n+1,2*i),a.push(Y||[],t+1,2*n+1,2*i+1)}}},i.prototype.getTile=function(e,t,n){var i=this.options,r=i.extent,s=i.debug;if(e<0||e>24)return null;var l=1<<e,a=o(e,t=(t%l+l)%l,n);if(this.tiles[a])return u.tile(this.tiles[a],r);s>1&&console.log("drilling down to z%d-%d-%d",e,t,n);for(var f,h=e,m=t,p=n;!f&&h>0;)h--,m=Math.floor(m/2),p=Math.floor(p/2),f=this.tiles[o(h,m,p)];return f&&f.source?(s>1&&console.log("found parent tile z%d-%d-%d",h,m,p),s>1&&console.time("drilling down"),this.splitTile(f.source,h,m,p,e,t,n),s>1&&console.timeEnd("drilling down"),this.tiles[a]?u.tile(this.tiles[a],r):null):null}},{"./clip":1,"./convert":2,"./tile":6,"./transform":7,"./wrap":8}],5:[function(e,t,n){"use strict";function i(e,t,n,r){for(var s,u=r,l=e[t],a=e[t+1],f=e[n],h=e[n+1],m=t+3;m<n;m+=3){var p=o(e[m],e[m+1],l,a,f,h);p>u&&(s=m,u=p)}u>r&&(s-t>3&&i(e,t,s,r),e[s+2]=u,n-s>3&&i(e,s,n,r))}function o(e,t,n,i,o,r){var s=o-n,u=r-i;if(0!==s||0!==u){var l=((e-n)*s+(t-i)*u)/(s*s+u*u);l>1?(n=o,i=r):l>0&&(n+=s*l,i+=u*l)}return s=e-n,u=t-i,s*s+u*u}t.exports=i},{}],6:[function(e,t,n){"use strict";function i(e,t,n,i){var r=t.geometry,s=t.type,u=[];if("Point"===s||"MultiPoint"===s)for(var l=0;l<r.length;l+=3)u.push(r[l]),u.push(r[l+1]),e.numPoints++,e.numSimplified++;else if("LineString"===s)o(u,r,e,n,i,!1,!1);else if("MultiLineString"===s||"Polygon"===s)for(l=0;l<r.length;l++)o(u,r[l],e,n,i,"Polygon"===s,0===l);else if("MultiPolygon"===s)for(var a=0;a<r.length;a++){var f=r[a];for(l=0;l<f.length;l++)o(u,f[l],e,n,i,!0,0===l)}if(u.length){var h={geometry:u,type:"Polygon"===s||"MultiPolygon"===s?3:"LineString"===s||"MultiLineString"===s?2:1,tags:t.tags||null};null!==t.id&&(h.id=t.id),e.features.push(h)}}function o(e,t,n,i,o,s,u){var l=i*i;if(!o&&t.size<(s?l:i))n.numPoints+=t.length/3;else{for(var a=[],f=0;f<t.length;f+=3)(o||t[f+2]>l)&&(n.numSimplified++,a.push(t[f]),a.push(t[f+1])),n.numPoints++;s&&r(a,u),e.push(a)}}function r(e,t){for(var n=0,i=0,o=e.length,r=o-2;i<o;r=i,i+=2)n+=(e[i]-e[r])*(e[i+1]+e[r+1]);if(n>0===t)for(i=0,o=e.length;i<o/2;i+=2){var s=e[i],u=e[i+1];e[i]=e[o-2-i],e[i+1]=e[o-1-i],e[o-2-i]=s,e[o-1-i]=u}}t.exports=function(e,t,n,o,r,s){for(var u={features:[],numPoints:0,numSimplified:0,numFeatures:0,source:null,x:n,y:o,z2:t,transformed:!1,minX:2,minY:1,maxX:-1,maxY:0},l=0;l<e.length;l++){u.numFeatures++,i(u,e[l],r,s);var a=e[l].minX,f=e[l].minY,h=e[l].maxX,m=e[l].maxY;a<u.minX&&(u.minX=a),f<u.minY&&(u.minY=f),h>u.maxX&&(u.maxX=h),m>u.maxY&&(u.maxY=m)}return u}},{}],7:[function(e,t,n){"use strict";function i(e,t,n,i,o,r){return[Math.round(n*(e*i-o)),Math.round(n*(t*i-r))]}n.tile=function(e,t){if(e.transformed)return e;var n,o,r,s=e.z2,u=e.x,l=e.y;for(n=0;n<e.features.length;n++){var a=e.features[n],f=a.geometry,h=a.type;if(a.geometry=[],1===h)for(o=0;o<f.length;o+=2)a.geometry.push(i(f[o],f[o+1],t,s,u,l));else for(o=0;o<f.length;o++){var m=[];for(r=0;r<f[o].length;r+=2)m.push(i(f[o][r],f[o][r+1],t,s,u,l));a.geometry.push(m)}}return e.transformed=!0,e},n.point=i},{}],8:[function(e,t,n){"use strict";function i(e,t){for(var n=[],i=0;i<e.length;i++){var r,u=e[i],l=u.type;if("Point"===l||"MultiPoint"===l||"LineString"===l)r=o(u.geometry,t);else if("MultiLineString"===l||"Polygon"===l){r=[];for(var a=0;a<u.geometry.length;a++)r.push(o(u.geometry[a],t))}else if("MultiPolygon"===l)for(r=[],a=0;a<u.geometry.length;a++){for(var f=[],h=0;h<u.geometry[a].length;h++)f.push(o(u.geometry[a][h],t));r.push(f)}n.push(s(u.id,l,r,u.tags))}return n}function o(e,t){var n=[];n.size=e.size;for(var i=0;i<e.length;i+=3)n.push(e[i]+t,e[i+1],e[i+2]);return n}var r=e("./clip"),s=e("./feature");t.exports=function(e,t){var n=e,o=r(e,1,-1-t,t,0,-1,2),s=r(e,1,1-t,2+t,0,-1,2);return(o||s)&&(n=r(e,1,-t,1+t,0,-1,2)||[],o&&(n=i(o,1).concat(n)),s&&(n=n.concat(i(s,-1)))),n}},{"./clip":1,"./feature":3}]},{},[4])(4)}); |
{ | ||
"name": "geojson-vt", | ||
"version": "2.4.0", | ||
"version": "3.0.0", | ||
"description": "Slice GeoJSON data into vector tiles efficiently", | ||
@@ -19,15 +19,18 @@ "homepage": "https://github.com/mapbox/geojson-vt", | ||
"devDependencies": { | ||
"benchmark": "^2.1.0", | ||
"browserify": "^13.0.1", | ||
"coveralls": "^2.11.9", | ||
"eslint": "^2.10.2", | ||
"benchmark": "^2.1.4", | ||
"browserify": "^14.4.0", | ||
"coveralls": "^2.13.1", | ||
"eslint": "^4.5.0", | ||
"eslint-config-mourner": "^2.0.1", | ||
"faucet": "0.0.1", | ||
"istanbul": "^0.4.3", | ||
"tape": "^4.5.1", | ||
"uglify-js": "^2.6.2", | ||
"watchify": "^3.7.0" | ||
"nyc": "^11.1.0", | ||
"tape": "^4.8.0", | ||
"uglify-js": "^3.0.28", | ||
"watchify": "^3.9.0" | ||
}, | ||
"eslintConfig": { | ||
"extends": "mourner" | ||
"extends": "mourner", | ||
"globals": { | ||
"topojson": true | ||
} | ||
}, | ||
@@ -37,3 +40,3 @@ "license": "ISC", | ||
"test": "eslint src/*.js test/*.js debug/viz.js && tape test/test-*.js | faucet", | ||
"cov": "istanbul cover tape test/test*.js", | ||
"cov": "nyc --reporter=lcov --reporter=text-summary tape test/test*.js", | ||
"coveralls": "npm run cov && coveralls < ./coverage/lcov.info", | ||
@@ -40,0 +43,0 @@ "build-min": "browserify src/index.js -s geojsonvt | uglifyjs -c -m -o geojson-vt.js", |
@@ -57,3 +57,3 @@ ## geojson-vt — GeoJSON Vector Tiles | ||
var tileIndex = geojsonvt(data, { | ||
maxZoom: 14, // max zoom to preserve detail on | ||
maxZoom: 14, // max zoom to preserve detail on; can't be higher than 24 | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
@@ -70,2 +70,6 @@ extent: 4096, // tile extent (both width and height) | ||
By default, tiles at zoom levels above `indexMaxZoom` are generated on the fly, but you can pre-generate all possible tiles for `data` by setting `indexMaxZoom` and `maxZoom` to the same value, setting `indexMaxPoints` to `0`, and then accessing the resulting tile coordinates from the `tileCoords` property of `tileIndex`. | ||
GeoJSON-VT only operates on zoom levels up to 24. | ||
### Browser builds | ||
@@ -72,0 +76,0 @@ |
216
src/clip.js
@@ -14,3 +14,3 @@ 'use strict'; | ||
function clip(features, scale, k1, k2, axis, intersect, minAll, maxAll) { | ||
function clip(features, scale, k1, k2, axis, minAll, maxAll) { | ||
@@ -27,9 +27,8 @@ k1 /= scale; | ||
var feature = features[i], | ||
geometry = feature.geometry, | ||
type = feature.type, | ||
min, max; | ||
var feature = features[i]; | ||
var geometry = feature.geometry; | ||
var type = feature.type; | ||
min = feature.min[axis]; | ||
max = feature.max[axis]; | ||
var min = axis === 0 ? feature.minX : feature.minY; | ||
var max = axis === 0 ? feature.maxX : feature.maxY; | ||
@@ -39,114 +38,147 @@ if (min >= k1 && max <= k2) { // trivial accept | ||
continue; | ||
} else if (min > k2 || max < k1) continue; // trivial reject | ||
var slices = type === 1 ? | ||
clipPoints(geometry, k1, k2, axis) : | ||
clipGeometry(geometry, k1, k2, axis, intersect, type === 3); | ||
if (slices.length) { | ||
// if a feature got clipped, it will likely get clipped on the next zoom level as well, | ||
// so there's no need to recalculate bboxes | ||
clipped.push(createFeature(feature.tags, type, slices, feature.id)); | ||
} else if (min > k2 || max < k1) { // trivial reject | ||
continue; | ||
} | ||
} | ||
return clipped.length ? clipped : null; | ||
} | ||
var newGeometry = []; | ||
function clipPoints(geometry, k1, k2, axis) { | ||
var slice = []; | ||
if (type === 'Point' || type === 'MultiPoint') { | ||
clipPoints(geometry, newGeometry, k1, k2, axis); | ||
for (var i = 0; i < geometry.length; i++) { | ||
var a = geometry[i], | ||
ak = a[axis]; | ||
} else if (type === 'LineString') { | ||
clipLine(geometry, newGeometry, k1, k2, axis, false); | ||
if (ak >= k1 && ak <= k2) slice.push(a); | ||
} | ||
return slice; | ||
} | ||
} else if (type === 'MultiLineString') { | ||
clipLines(geometry, newGeometry, k1, k2, axis, false); | ||
function clipGeometry(geometry, k1, k2, axis, intersect, closed) { | ||
} else if (type === 'Polygon') { | ||
clipLines(geometry, newGeometry, k1, k2, axis, true); | ||
var slices = []; | ||
} else if (type === 'MultiPolygon') { | ||
for (var j = 0; j < geometry.length; j++) { | ||
var polygon = []; | ||
clipLines(geometry[j], polygon, k1, k2, axis, true); | ||
if (polygon.length) { | ||
newGeometry.push(polygon); | ||
} | ||
} | ||
} | ||
for (var i = 0; i < geometry.length; i++) { | ||
if (newGeometry.length) { | ||
if (type === 'LineString' || type === 'MultiLineString') { | ||
if (newGeometry.length === 1) { | ||
type = 'LineString'; | ||
newGeometry = newGeometry[0]; | ||
} else { | ||
type = 'MultiLineString'; | ||
} | ||
} | ||
if (type === 'Point' || type === 'MultiPoint') { | ||
type = newGeometry.length === 3 ? 'Point' : 'MultiPoint'; | ||
} | ||
var ak = 0, | ||
bk = 0, | ||
b = null, | ||
points = geometry[i], | ||
area = points.area, | ||
dist = points.dist, | ||
outer = points.outer, | ||
len = points.length, | ||
a, j, last; | ||
clipped.push(createFeature(feature.id, type, newGeometry, feature.tags)); | ||
} | ||
} | ||
var slice = []; | ||
return clipped.length ? clipped : null; | ||
} | ||
for (j = 0; j < len - 1; j++) { | ||
a = b || points[j]; | ||
b = points[j + 1]; | ||
ak = bk || a[axis]; | ||
bk = b[axis]; | ||
function clipPoints(geom, newGeom, k1, k2, axis) { | ||
for (var i = 0; i < geom.length; i += 3) { | ||
var a = geom[i + axis]; | ||
if (ak < k1) { | ||
if (a >= k1 && a <= k2) { | ||
newGeom.push(geom[i]); | ||
newGeom.push(geom[i + 1]); | ||
newGeom.push(geom[i + 2]); | ||
} | ||
} | ||
} | ||
if ((bk > k2)) { // ---|-----|--> | ||
slice.push(intersect(a, b, k1), intersect(a, b, k2)); | ||
if (!closed) slice = newSlice(slices, slice, area, dist, outer); | ||
function clipLine(geom, newGeom, k1, k2, axis, isPolygon) { | ||
} else if (bk >= k1) slice.push(intersect(a, b, k1)); // ---|--> | | ||
var slice = []; | ||
var intersect = axis === 0 ? intersectX : intersectY; | ||
} else if (ak > k2) { | ||
for (var i = 0; i < geom.length - 3; i += 3) { | ||
var ax = geom[i]; | ||
var ay = geom[i + 1]; | ||
var az = geom[i + 2]; | ||
var bx = geom[i + 3]; | ||
var by = geom[i + 4]; | ||
var a = axis === 0 ? ax : ay; | ||
var b = axis === 0 ? bx : by; | ||
var sliced = false; | ||
if ((bk < k1)) { // <--|-----|--- | ||
slice.push(intersect(a, b, k2), intersect(a, b, k1)); | ||
if (!closed) slice = newSlice(slices, slice, area, dist, outer); | ||
if (a < k1) { | ||
// ---|--> | | ||
if (b >= k1) intersect(slice, ax, ay, bx, by, k1); | ||
} else if (a > k2) { | ||
// | <--|--- | ||
if (b <= k2) intersect(slice, ax, ay, bx, by, k2); | ||
} else { | ||
addPoint(slice, ax, ay, az); | ||
} | ||
if (b < k1 && a >= k1) { | ||
// <--|--- | or <--|-----|--- | ||
intersect(slice, ax, ay, bx, by, k1); | ||
sliced = true; | ||
} | ||
if (b > k2 && a <= k2) { | ||
// | ---|--> or ---|-----|--> | ||
intersect(slice, ax, ay, bx, by, k2); | ||
sliced = true; | ||
} | ||
} else if (bk <= k2) slice.push(intersect(a, b, k2)); // | <--|--- | ||
} else { | ||
slice.push(a); | ||
if (bk < k1) { // <--|--- | | ||
slice.push(intersect(a, b, k1)); | ||
if (!closed) slice = newSlice(slices, slice, area, dist, outer); | ||
} else if (bk > k2) { // | ---|--> | ||
slice.push(intersect(a, b, k2)); | ||
if (!closed) slice = newSlice(slices, slice, area, dist, outer); | ||
} | ||
// | --> | | ||
} | ||
if (!isPolygon && sliced) { | ||
slice.size = geom.size; | ||
newGeom.push(slice); | ||
slice = []; | ||
} | ||
} | ||
// add the last point | ||
a = points[len - 1]; | ||
ak = a[axis]; | ||
if (ak >= k1 && ak <= k2) slice.push(a); | ||
// add the last point | ||
var last = geom.length - 3; | ||
ax = geom[last]; | ||
ay = geom[last + 1]; | ||
az = geom[last + 2]; | ||
a = axis === 0 ? ax : ay; | ||
if (a >= k1 && a <= k2) addPoint(slice, ax, ay, az); | ||
// close the polygon if its endpoints are not the same after clipping | ||
// close the polygon if its endpoints are not the same after clipping | ||
last = slice.length - 3; | ||
if (isPolygon && last >= 3 && (slice[last] !== slice[0] || slice[last + 1] !== slice[1])) { | ||
addPoint(slice, slice[0], slice[1], slice[2]); | ||
} | ||
last = slice[slice.length - 1]; | ||
if (closed && last && (slice[0][0] !== last[0] || slice[0][1] !== last[1])) slice.push(slice[0]); | ||
// add the final slice | ||
if (slice.length) { | ||
slice.size = geom.size; | ||
newGeom.push(slice); | ||
} | ||
} | ||
// add the final slice | ||
newSlice(slices, slice, area, dist, outer); | ||
function clipLines(geom, newGeom, k1, k2, axis, isPolygon) { | ||
for (var i = 0; i < geom.length; i++) { | ||
clipLine(geom[i], newGeom, k1, k2, axis, isPolygon); | ||
} | ||
} | ||
return slices; | ||
function addPoint(out, x, y, z) { | ||
out.push(x); | ||
out.push(y); | ||
out.push(z); | ||
} | ||
function newSlice(slices, slice, area, dist, outer) { | ||
if (slice.length) { | ||
// we don't recalculate the area/length of the unclipped geometry because the case where it goes | ||
// below the visibility threshold as a result of clipping is rare, so we avoid doing unnecessary work | ||
slice.area = area; | ||
slice.dist = dist; | ||
if (outer !== undefined) slice.outer = outer; | ||
function intersectX(out, ax, ay, bx, by, x) { | ||
out.push(x); | ||
out.push(ay + (x - ax) * (by - ay) / (bx - ax)); | ||
out.push(1); | ||
} | ||
slices.push(slice); | ||
} | ||
return []; | ||
function intersectY(out, ax, ay, bx, by, y) { | ||
out.push(ax + (y - ay) * (bx - ax) / (by - ay)); | ||
out.push(y); | ||
out.push(1); | ||
} |
@@ -17,2 +17,3 @@ 'use strict'; | ||
} | ||
} else if (data.type === 'Feature') { | ||
@@ -25,99 +26,105 @@ convertFeature(features, data, tolerance); | ||
} | ||
return features; | ||
} | ||
function convertFeature(features, feature, tolerance) { | ||
if (feature.geometry === null) { | ||
// ignore features with null geometry | ||
return; | ||
} | ||
function convertFeature(features, geojson, tolerance) { | ||
if (!geojson.geometry) return; | ||
var geom = feature.geometry, | ||
type = geom.type, | ||
coords = geom.coordinates, | ||
tags = feature.properties, | ||
id = feature.id, | ||
i, j, rings, projectedRing; | ||
var coords = geojson.geometry.coordinates; | ||
var type = geojson.geometry.type; | ||
var tol = tolerance * tolerance; | ||
var geometry = []; | ||
if (type === 'Point') { | ||
features.push(createFeature(tags, 1, [projectPoint(coords)], id)); | ||
convertPoint(coords, geometry); | ||
} else if (type === 'MultiPoint') { | ||
features.push(createFeature(tags, 1, project(coords), id)); | ||
for (var i = 0; i < coords.length; i++) { | ||
convertPoint(coords[i], geometry); | ||
} | ||
} else if (type === 'LineString') { | ||
features.push(createFeature(tags, 2, [project(coords, tolerance)], id)); | ||
convertLine(coords, geometry, tol, false); | ||
} else if (type === 'MultiLineString' || type === 'Polygon') { | ||
rings = []; | ||
for (i = 0; i < coords.length; i++) { | ||
projectedRing = project(coords[i], tolerance); | ||
if (type === 'Polygon') projectedRing.outer = (i === 0); | ||
rings.push(projectedRing); | ||
} | ||
features.push(createFeature(tags, type === 'Polygon' ? 3 : 2, rings, id)); | ||
} else if (type === 'MultiLineString') { | ||
convertLines(coords, geometry, tol, false); | ||
} else if (type === 'Polygon') { | ||
convertLines(coords, geometry, tol, true); | ||
} else if (type === 'MultiPolygon') { | ||
rings = []; | ||
for (i = 0; i < coords.length; i++) { | ||
for (j = 0; j < coords[i].length; j++) { | ||
projectedRing = project(coords[i][j], tolerance); | ||
projectedRing.outer = (j === 0); | ||
rings.push(projectedRing); | ||
} | ||
var polygon = []; | ||
convertLines(coords[i], polygon, tol, true); | ||
geometry.push(polygon); | ||
} | ||
features.push(createFeature(tags, 3, rings, id)); | ||
} else if (type === 'GeometryCollection') { | ||
for (i = 0; i < geom.geometries.length; i++) { | ||
for (i = 0; i < geojson.geometry.geometries.length; i++) { | ||
convertFeature(features, { | ||
geometry: geom.geometries[i], | ||
properties: tags | ||
geometry: geojson.geometry.geometries[i], | ||
properties: geojson.properties | ||
}, tolerance); | ||
} | ||
return; | ||
} else { | ||
throw new Error('Input data is not a valid GeoJSON object.'); | ||
} | ||
features.push(createFeature(geojson.id, type, geometry, geojson.properties)); | ||
} | ||
function project(lonlats, tolerance) { | ||
var projected = []; | ||
for (var i = 0; i < lonlats.length; i++) { | ||
projected.push(projectPoint(lonlats[i])); | ||
} | ||
if (tolerance) { | ||
simplify(projected, tolerance); | ||
calcSize(projected); | ||
} | ||
return projected; | ||
function convertPoint(coords, out) { | ||
out.push(projectX(coords[0])); | ||
out.push(projectY(coords[1])); | ||
out.push(0); | ||
} | ||
function projectPoint(p) { | ||
var sin = Math.sin(p[1] * Math.PI / 180), | ||
x = (p[0] / 360 + 0.5), | ||
y = (0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI); | ||
function convertLine(ring, out, tol, isPolygon) { | ||
var x0, y0; | ||
var size = 0; | ||
y = y < 0 ? 0 : | ||
y > 1 ? 1 : y; | ||
for (var j = 0; j < ring.length; j++) { | ||
var x = projectX(ring[j][0]); | ||
var y = projectY(ring[j][1]); | ||
return [x, y, 0]; | ||
} | ||
out.push(x); | ||
out.push(y); | ||
out.push(0); | ||
// calculate area and length of the poly | ||
function calcSize(points) { | ||
var area = 0, | ||
dist = 0; | ||
if (j > 0) { | ||
if (isPolygon) { | ||
size += (x0 * y - x * y0) / 2; // area | ||
} else { | ||
size += Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)); // length | ||
} | ||
} | ||
x0 = x; | ||
y0 = y; | ||
} | ||
for (var i = 0, a, b; i < points.length - 1; i++) { | ||
a = b || points[i]; | ||
b = points[i + 1]; | ||
var last = out.length - 3; | ||
out[2] = 1; | ||
simplify(out, 0, last, tol); | ||
out[last + 2] = 1; | ||
area += a[0] * b[1] - b[0] * a[1]; | ||
out.size = Math.abs(size); | ||
} | ||
// use Manhattan distance instead of Euclidian one to avoid expensive square root computation | ||
dist += Math.abs(b[0] - a[0]) + Math.abs(b[1] - a[1]); | ||
function convertLines(rings, out, tol, isPolygon) { | ||
for (var i = 0; i < rings.length; i++) { | ||
var geom = []; | ||
convertLine(rings[i], geom, tol, isPolygon); | ||
out.push(geom); | ||
} | ||
points.area = Math.abs(area / 2); | ||
points.dist = dist; | ||
} | ||
function projectX(x) { | ||
return x / 360 + 0.5; | ||
} | ||
function projectY(y) { | ||
var sin = Math.sin(y * Math.PI / 180); | ||
var y2 = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI; | ||
return y2 < 0 ? 0 : y2 > 1 ? 1 : y2; | ||
} |
@@ -5,3 +5,3 @@ 'use strict'; | ||
function createFeature(tags, type, geom, id) { | ||
function createFeature(id, type, geom, tags) { | ||
var feature = { | ||
@@ -11,5 +11,7 @@ id: id || null, | ||
geometry: geom, | ||
tags: tags || null, | ||
min: [Infinity, Infinity], // initial bbox values | ||
max: [-Infinity, -Infinity] | ||
tags: tags, | ||
minX: Infinity, | ||
minY: Infinity, | ||
maxX: -Infinity, | ||
maxY: -Infinity | ||
}; | ||
@@ -20,27 +22,30 @@ calcBBox(feature); | ||
// calculate the feature bounding box for faster clipping later | ||
function calcBBox(feature) { | ||
var geometry = feature.geometry, | ||
min = feature.min, | ||
max = feature.max; | ||
var geom = feature.geometry; | ||
var type = feature.type; | ||
if (feature.type === 1) { | ||
calcRingBBox(min, max, geometry); | ||
} else { | ||
for (var i = 0; i < geometry.length; i++) { | ||
calcRingBBox(min, max, geometry[i]); | ||
if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') { | ||
calcLineBBox(feature, geom); | ||
} else if (type === 'Polygon' || type === 'MultiLineString') { | ||
for (var i = 0; i < geom.length; i++) { | ||
calcLineBBox(feature, geom[i]); | ||
} | ||
} else if (type === 'MultiPolygon') { | ||
for (i = 0; i < geom.length; i++) { | ||
for (var j = 0; j < geom[i].length; j++) { | ||
calcLineBBox(feature, geom[i][j]); | ||
} | ||
} | ||
} | ||
return feature; | ||
} | ||
function calcRingBBox(min, max, points) { | ||
for (var i = 0, p; i < points.length; i++) { | ||
p = points[i]; | ||
min[0] = Math.min(p[0], min[0]); | ||
max[0] = Math.max(p[0], max[0]); | ||
min[1] = Math.min(p[1], min[1]); | ||
max[1] = Math.max(p[1], max[1]); | ||
function calcLineBBox(feature, geom) { | ||
for (var i = 0; i < geom.length; i += 3) { | ||
feature.minX = Math.min(feature.minX, geom[i]); | ||
feature.minY = Math.min(feature.minY, geom[i + 1]); | ||
feature.maxX = Math.max(feature.maxX, geom[i]); | ||
feature.maxY = Math.max(feature.maxY, geom[i + 1]); | ||
} | ||
} |
@@ -23,2 +23,4 @@ 'use strict'; | ||
if (options.maxZoom < 0 || options.maxZoom > 24) throw new Error('maxZoom should be in the 0-24 range'); | ||
var z2 = 1 << options.maxZoom, // 2^z | ||
@@ -38,3 +40,3 @@ features = convert(data, options.tolerance / (z2 * options.extent)); | ||
features = wrap(features, options.buffer / options.extent, intersectX); | ||
features = wrap(features, options.buffer / options.extent); | ||
@@ -55,3 +57,2 @@ // start slicing from the top tile down | ||
indexMaxPoints: 100000, // max number of points per tile in the tile index | ||
solidChildren: false, // whether to tile solid square tiles further | ||
tolerance: 3, // simplification tolerance (higher means simpler) | ||
@@ -67,4 +68,3 @@ extent: 4096, // tile extent | ||
options = this.options, | ||
debug = options.debug, | ||
solid = null; | ||
debug = options.debug; | ||
@@ -119,11 +119,7 @@ // avoid recursion by using a processing queue | ||
// stop tiling if the tile is solid clipped square | ||
if (!options.solidChildren && isClippedSquare(tile, options.extent, options.buffer)) { | ||
if (cz) solid = z; // and remember the zoom if we're drilling down | ||
continue; | ||
} | ||
// if we slice further down, no need to keep source geometry | ||
tile.source = null; | ||
if (features.length === 0) continue; | ||
if (debug > 1) console.time('clipping'); | ||
@@ -140,13 +136,16 @@ | ||
left = clip(features, z2, x - k1, x + k3, 0, intersectX, tile.min[0], tile.max[0]); | ||
right = clip(features, z2, x + k2, x + k4, 0, intersectX, tile.min[0], tile.max[0]); | ||
left = clip(features, z2, x - k1, x + k3, 0, tile.minX, tile.maxX); | ||
right = clip(features, z2, x + k2, x + k4, 0, tile.minX, tile.maxX); | ||
features = null; | ||
if (left) { | ||
tl = clip(left, z2, y - k1, y + k3, 1, intersectY, tile.min[1], tile.max[1]); | ||
bl = clip(left, z2, y + k2, y + k4, 1, intersectY, tile.min[1], tile.max[1]); | ||
tl = clip(left, z2, y - k1, y + k3, 1, tile.minY, tile.maxY); | ||
bl = clip(left, z2, y + k2, y + k4, 1, tile.minY, tile.maxY); | ||
left = null; | ||
} | ||
if (right) { | ||
tr = clip(right, z2, y - k1, y + k3, 1, intersectY, tile.min[1], tile.max[1]); | ||
br = clip(right, z2, y + k2, y + k4, 1, intersectY, tile.min[1], tile.max[1]); | ||
tr = clip(right, z2, y - k1, y + k3, 1, tile.minY, tile.maxY); | ||
br = clip(right, z2, y + k2, y + k4, 1, tile.minY, tile.maxY); | ||
right = null; | ||
} | ||
@@ -156,11 +155,7 @@ | ||
if (features.length) { | ||
stack.push(tl || [], z + 1, x * 2, y * 2); | ||
stack.push(bl || [], z + 1, x * 2, y * 2 + 1); | ||
stack.push(tr || [], z + 1, x * 2 + 1, y * 2); | ||
stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1); | ||
} | ||
stack.push(tl || [], z + 1, x * 2, y * 2); | ||
stack.push(bl || [], z + 1, x * 2, y * 2 + 1); | ||
stack.push(tr || [], z + 1, x * 2 + 1, y * 2); | ||
stack.push(br || [], z + 1, x * 2 + 1, y * 2 + 1); | ||
} | ||
return solid; | ||
}; | ||
@@ -173,2 +168,4 @@ | ||
if (z < 0 || z > 24) return null; | ||
var z2 = 1 << z; | ||
@@ -199,15 +196,6 @@ x = ((x % z2) + z2) % z2; // wrap tile x coordinate | ||
// it parent tile is a solid clipped square, return it instead since it's identical | ||
if (isClippedSquare(parent, extent, options.buffer)) return transform.tile(parent, extent); | ||
if (debug > 1) console.time('drilling down'); | ||
var solid = this.splitTile(parent.source, z0, x0, y0, z, x, y); | ||
this.splitTile(parent.source, z0, x0, y0, z, x, y); | ||
if (debug > 1) console.timeEnd('drilling down'); | ||
// one of the parent tiles was a solid clipped square | ||
if (solid !== null) { | ||
var m = 1 << (z - solid); | ||
id = toID(solid, Math.floor(x / m), Math.floor(y / m)); | ||
} | ||
return this.tiles[id] ? transform.tile(this.tiles[id], extent) : null; | ||
@@ -220,9 +208,2 @@ }; | ||
function intersectX(a, b, x) { | ||
return [x, (x - a[0]) * (b[1] - a[1]) / (b[0] - a[0]) + a[1], 1]; | ||
} | ||
function intersectY(a, b, y) { | ||
return [(y - a[1]) * (b[0] - a[0]) / (b[1] - a[1]) + a[0], y, 1]; | ||
} | ||
function extend(dest, src) { | ||
@@ -232,22 +213,1 @@ for (var i in src) dest[i] = src[i]; | ||
} | ||
// checks whether a tile is a whole-area fill after clipping; if it is, there's no sense slicing it further | ||
function isClippedSquare(tile, extent, buffer) { | ||
var features = tile.source; | ||
if (features.length !== 1) return false; | ||
var feature = features[0]; | ||
if (feature.type !== 3 || feature.geometry.length > 1) return false; | ||
var len = feature.geometry[0].length; | ||
if (len !== 5) return false; | ||
for (var i = 0; i < len; i++) { | ||
var p = transform.point(feature.geometry[0][i], extent, tile.z2, tile.x, tile.y); | ||
if ((p[0] !== -buffer && p[0] !== extent + buffer) || | ||
(p[1] !== -buffer && p[1] !== extent + buffer)) return false; | ||
} | ||
return true; | ||
} |
@@ -7,39 +7,23 @@ 'use strict'; | ||
function simplify(points, tolerance) { | ||
function simplify(coords, first, last, sqTolerance) { | ||
var maxSqDist = sqTolerance; | ||
var index; | ||
var sqTolerance = tolerance * tolerance, | ||
len = points.length, | ||
first = 0, | ||
last = len - 1, | ||
stack = [], | ||
i, maxSqDist, sqDist, index; | ||
var ax = coords[first]; | ||
var ay = coords[first + 1]; | ||
var bx = coords[last]; | ||
var by = coords[last + 1]; | ||
// always retain the endpoints (1 is the max value) | ||
points[first][2] = 1; | ||
points[last][2] = 1; | ||
// avoid recursion by using a stack | ||
while (last) { | ||
maxSqDist = 0; | ||
for (i = first + 1; i < last; i++) { | ||
sqDist = getSqSegDist(points[i], points[first], points[last]); | ||
if (sqDist > maxSqDist) { | ||
index = i; | ||
maxSqDist = sqDist; | ||
} | ||
for (var i = first + 3; i < last; i += 3) { | ||
var d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by); | ||
if (d > maxSqDist) { | ||
index = i; | ||
maxSqDist = d; | ||
} | ||
} | ||
if (maxSqDist > sqTolerance) { | ||
points[index][2] = maxSqDist; // save the point importance in squared pixels as a z coordinate | ||
stack.push(first); | ||
stack.push(index); | ||
first = index; | ||
} else { | ||
last = stack.pop(); | ||
first = stack.pop(); | ||
} | ||
if (maxSqDist > sqTolerance) { | ||
if (index - first > 3) simplify(coords, first, index, sqTolerance); | ||
coords[index + 2] = maxSqDist; | ||
if (last - index > 3) simplify(coords, index, last, sqTolerance); | ||
} | ||
@@ -49,9 +33,6 @@ } | ||
// square distance from a point to a segment | ||
function getSqSegDist(p, a, b) { | ||
function getSqSegDist(px, py, x, y, bx, by) { | ||
var x = a[0], y = a[1], | ||
bx = b[0], by = b[1], | ||
px = p[0], py = p[1], | ||
dx = bx - x, | ||
dy = by - y; | ||
var dx = bx - x; | ||
var dy = by - y; | ||
@@ -58,0 +39,0 @@ if (dx !== 0 || dy !== 0) { |
111
src/tile.js
@@ -16,4 +16,6 @@ 'use strict'; | ||
transformed: false, | ||
min: [2, 1], | ||
max: [-1, 0] | ||
minX: 2, | ||
minY: 1, | ||
maxX: -1, | ||
maxY: 0 | ||
}; | ||
@@ -24,9 +26,11 @@ for (var i = 0; i < features.length; i++) { | ||
var min = features[i].min, | ||
max = features[i].max; | ||
var minX = features[i].minX; | ||
var minY = features[i].minY; | ||
var maxX = features[i].maxX; | ||
var maxY = features[i].maxY; | ||
if (min[0] < tile.min[0]) tile.min[0] = min[0]; | ||
if (min[1] < tile.min[1]) tile.min[1] = min[1]; | ||
if (max[0] > tile.max[0]) tile.max[0] = max[0]; | ||
if (max[1] > tile.max[1]) tile.max[1] = max[1]; | ||
if (minX < tile.minX) tile.minX = minX; | ||
if (minY < tile.minY) tile.minY = minY; | ||
if (maxX > tile.maxX) tile.maxX = maxX; | ||
if (maxY > tile.maxY) tile.maxY = maxY; | ||
} | ||
@@ -40,9 +44,8 @@ return tile; | ||
type = feature.type, | ||
simplified = [], | ||
sqTolerance = tolerance * tolerance, | ||
i, j, ring, p; | ||
simplified = []; | ||
if (type === 1) { | ||
for (i = 0; i < geom.length; i++) { | ||
if (type === 'Point' || type === 'MultiPoint') { | ||
for (var i = 0; i < geom.length; i += 3) { | ||
simplified.push(geom[i]); | ||
simplified.push(geom[i + 1]); | ||
tile.numPoints++; | ||
@@ -52,30 +55,17 @@ tile.numSimplified++; | ||
} else { | ||
} else if (type === 'LineString') { | ||
addLine(simplified, geom, tile, tolerance, noSimplify, false, false); | ||
// simplify and transform projected coordinates for tile geometry | ||
} else if (type === 'MultiLineString' || type === 'Polygon') { | ||
for (i = 0; i < geom.length; i++) { | ||
ring = geom[i]; | ||
addLine(simplified, geom[i], tile, tolerance, noSimplify, type === 'Polygon', i === 0); | ||
} | ||
// filter out tiny polylines & polygons | ||
if (!noSimplify && ((type === 2 && ring.dist < tolerance) || | ||
(type === 3 && ring.area < sqTolerance))) { | ||
tile.numPoints += ring.length; | ||
continue; | ||
} | ||
} else if (type === 'MultiPolygon') { | ||
var simplifiedRing = []; | ||
for (j = 0; j < ring.length; j++) { | ||
p = ring[j]; | ||
// keep points with importance > tolerance | ||
if (noSimplify || p[2] > sqTolerance) { | ||
simplifiedRing.push(p); | ||
tile.numSimplified++; | ||
} | ||
tile.numPoints++; | ||
for (var k = 0; k < geom.length; k++) { | ||
var polygon = geom[k]; | ||
for (i = 0; i < polygon.length; i++) { | ||
addLine(simplified, polygon[i], tile, tolerance, noSimplify, true, i === 0); | ||
} | ||
if (type === 3) rewind(simplifiedRing, ring.outer); | ||
simplified.push(simplifiedRing); | ||
} | ||
@@ -87,3 +77,4 @@ } | ||
geometry: simplified, | ||
type: type, | ||
type: type === 'Polygon' || type === 'MultiPolygon' ? 3 : | ||
type === 'LineString' || type === 'MultiLineString' ? 2 : 1, | ||
tags: feature.tags || null | ||
@@ -98,15 +89,41 @@ }; | ||
function rewind(ring, clockwise) { | ||
var area = signedArea(ring); | ||
if (area < 0 === clockwise) ring.reverse(); | ||
function addLine(result, geom, tile, tolerance, noSimplify, isPolygon, isOuter) { | ||
var sqTolerance = tolerance * tolerance; | ||
if (!noSimplify && (geom.size < (isPolygon ? sqTolerance : tolerance))) { | ||
tile.numPoints += geom.length / 3; | ||
return; | ||
} | ||
var ring = []; | ||
for (var i = 0; i < geom.length; i += 3) { | ||
if (noSimplify || geom[i + 2] > sqTolerance) { | ||
tile.numSimplified++; | ||
ring.push(geom[i]); | ||
ring.push(geom[i + 1]); | ||
} | ||
tile.numPoints++; | ||
} | ||
if (isPolygon) rewind(ring, isOuter); | ||
result.push(ring); | ||
} | ||
function signedArea(ring) { | ||
var sum = 0; | ||
for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { | ||
p1 = ring[i]; | ||
p2 = ring[j]; | ||
sum += (p2[0] - p1[0]) * (p1[1] + p2[1]); | ||
function rewind(ring, clockwise) { | ||
var area = 0; | ||
for (var i = 0, len = ring.length, j = len - 2; i < len; j = i, i += 2) { | ||
area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]); | ||
} | ||
return sum; | ||
if (area > 0 === clockwise) { | ||
for (i = 0, len = ring.length; i < len / 2; i += 2) { | ||
var x = ring[i]; | ||
var y = ring[i + 1]; | ||
ring[i] = ring[len - 2 - i]; | ||
ring[i + 1] = ring[len - 1 - i]; | ||
ring[len - 2 - i] = x; | ||
ring[len - 1 - i] = y; | ||
} | ||
} | ||
} |
@@ -21,9 +21,15 @@ 'use strict'; | ||
feature.geometry = []; | ||
if (type === 1) { | ||
for (j = 0; j < geom.length; j++) geom[j] = transformPoint(geom[j], extent, z2, tx, ty); | ||
for (j = 0; j < geom.length; j += 2) { | ||
feature.geometry.push(transformPoint(geom[j], geom[j + 1], extent, z2, tx, ty)); | ||
} | ||
} else { | ||
for (j = 0; j < geom.length; j++) { | ||
var ring = geom[j]; | ||
for (k = 0; k < ring.length; k++) ring[k] = transformPoint(ring[k], extent, z2, tx, ty); | ||
var ring = []; | ||
for (k = 0; k < geom[j].length; k += 2) { | ||
ring.push(transformPoint(geom[j][k], geom[j][k + 1], extent, z2, tx, ty)); | ||
} | ||
feature.geometry.push(ring); | ||
} | ||
@@ -38,6 +44,6 @@ } | ||
function transformPoint(p, extent, z2, tx, ty) { | ||
var x = Math.round(extent * (p[0] * z2 - tx)), | ||
y = Math.round(extent * (p[1] * z2 - ty)); | ||
return [x, y]; | ||
function transformPoint(x, y, extent, z2, tx, ty) { | ||
return [ | ||
Math.round(extent * (x * z2 - tx)), | ||
Math.round(extent * (y * z2 - ty))]; | ||
} |
@@ -8,9 +8,9 @@ 'use strict'; | ||
function wrap(features, buffer, intersectX) { | ||
function wrap(features, buffer) { | ||
var merged = features, | ||
left = clip(features, 1, -1 - buffer, buffer, 0, intersectX, -1, 2), // left world copy | ||
right = clip(features, 1, 1 - buffer, 2 + buffer, 0, intersectX, -1, 2); // right world copy | ||
left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2), // left world copy | ||
right = clip(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2); // right world copy | ||
if (left || right) { | ||
merged = clip(features, 1, -buffer, 1 + buffer, 0, intersectX, -1, 2) || []; // center world copy | ||
merged = clip(features, 1, -buffer, 1 + buffer, 0, -1, 2) || []; // center world copy | ||
@@ -33,5 +33,6 @@ if (left) merged = shiftFeatureCoords(left, 1).concat(merged); // merge left into center | ||
if (type === 1) { | ||
if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') { | ||
newGeometry = shiftCoords(feature.geometry, offset); | ||
} else { | ||
} else if (type === 'MultiLineString' || type === 'Polygon') { | ||
newGeometry = []; | ||
@@ -41,5 +42,14 @@ for (var j = 0; j < feature.geometry.length; j++) { | ||
} | ||
} else if (type === 'MultiPolygon') { | ||
newGeometry = []; | ||
for (j = 0; j < feature.geometry.length; j++) { | ||
var newPolygon = []; | ||
for (var k = 0; k < feature.geometry[j].length; k++) { | ||
newPolygon.push(shiftCoords(feature.geometry[j][k], offset)); | ||
} | ||
newGeometry.push(newPolygon); | ||
} | ||
} | ||
newFeatures.push(createFeature(feature.tags, type, newGeometry, feature.id)); | ||
newFeatures.push(createFeature(feature.id, type, newGeometry, feature.tags)); | ||
} | ||
@@ -52,9 +62,8 @@ | ||
var newPoints = []; | ||
newPoints.area = points.area; | ||
newPoints.dist = points.dist; | ||
newPoints.size = points.size; | ||
for (var i = 0; i < points.length; i++) { | ||
newPoints.push([points[i][0] + offset, points[i][1], points[i][2]]); | ||
for (var i = 0; i < points.length; i += 3) { | ||
newPoints.push(points[i] + offset, points[i + 1], points[i + 2]); | ||
} | ||
return newPoints; | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
327665
17
1523
152
0