/** * @file mapmath.js * @version 20190415 ms * @type JS */ /** * @class KExtent * @params xmin,ymin,xmax,ymax */ function KExtent(xmin,ymin,xmax,ymax){ this.isNull=ymin===undefined; this.xmin=this.isNull?0:xmin; this.ymin=this.isNull?0:ymin; this.xmax=this.isNull?0:xmax===undefined?xmin:xmax; this.ymax=this.isNull?0:ymax===undefined?ymin:ymax; } KExtent.prototype.toShape=function(precision){ if(precision===undefined) precision=0; var line=new KLine(); line.points.push(new KPoint(this.xmin,this.ymin)); line.points.push(new KPoint(this.xmax,this.ymin)); line.points.push(new KPoint(this.xmax,this.ymax)); line.points.push(new KPoint(this.xmin,this.ymax)); line.points.push(new KPoint(this.xmin,this.ymin)); var shp=new KShape(KShape.stPolygon); shp.precision=precision; shp.lines.push(line); return shp; }; KExtent.prototype.expand=function(ext){ var expanded=false; if(!ext.isNull){ if(this.isNull){ this.xmin=ext.xmin;this.ymin=ext.ymin;this.xmax=ext.xmax;this.ymax=ext.ymax;this.isNull=false;expanded=true; }else{ if(ext.xminthis.xmax){this.xmax=ext.xmax;expanded=true;} if(ext.ymax>this.ymax){this.ymax=ext.ymax;expanded=true;} } } return expanded; }; KExtent.prototype.reset=function(){ this.isNull=true; this.xmin=0; this.ymin=0; this.xmax=0; this.ymax=0; }; KExtent.prototype.overlaps=function(ext){ return this.xmin<=ext.xmax && this.xmax>=ext.xmin && this.ymin<=ext.ymax && this.ymax>=ext.ymin; }; /** * @class KPoint * @constructor * @params x,y */ function KPoint(x,y){ this.x=x;this.y=y; } KPoint.prototype.clone=function(){ return new KPoint(this.x,this.y); }; KPoint.prototype.set=function(x,y){ this.x=x;this.y=y; }; KPoint.prototype.equals=function(other){ return this.x==other.x && this.y==other.y; }; KPoint.prototype.extent=function(){ return new KExtent(this.x,this.y,this.x,this.y); }; KPoint.prototype.snapToGrid=function(sizeX,sizeY){ if(sizeY===undefined) sizeY=sizeX; return new KPoint(Math.round(this.x/sizeX)*sizeX,Math.round(this.y/sizeY)*sizeY); }; KPoint.prototype.distanceToPoint=function(pt){ var dX=pt.x-this.x,dY=pt.y-this.y; return Math.sqrt(dX*dX+dY*dY); }; KPoint.prototype.distanceToLine=function(lineA,lineB){ var x12=lineB.x-lineA.x,y12=lineB.y-lineA.y,x13=this.x-lineA.x,y13=this.y-lineA.y; var d12=Math.sqrt(x12*x12+y12*y12);if(d12==0) return Math.sqrt(x13*x13+y13*y13); var pp=(x12*x13+y12*y13)/d12; if(pp<0) return Math.sqrt(x13*x13+y13*y13); else if(pp>d12){ var x23=this.x-lineB.x,y23=this.y-lineB.y; return Math.sqrt(x23*x23+y23*y23); }else return Math.abs((x12*y13-y12*x13)/d12); }; KPoint.prototype.isOnLine=function(lineA,lineB){ // TODO (mapmath.js) reduce precision to float return ((this.x-lineB.x)*(lineA.y-lineB.y))==((this.y-lineB.y)*(lineA.x-lineB.x)) && this.x>=Math.min(lineA.x,lineB.x) && this.x<=Math.max(lineA.x,lineB.x) && this.y>=Math.min(lineA.y,lineB.y) && this.y<=Math.max(lineA.y,lineB.y); }; /** * @class KLine * @constructor */ function KLine(){ this.points=[]; this.outerRing=-1; } KLine.prototype.clone=function(){ var n=new KLine(); for(var a=0;a=offset) return new KPoint(p1.x+dx*cc,p1.y+dy*cc); pos+=d; } } return null; }; KLine.prototype.flip=function(){ for(var a=0;a<=Math.floor((this.points.length-2)/2);a++){ var pt=this.points[a]; this.points[a]=this.points[this.points.length-1-a]; this.points[this.points.length-1-a]=pt; } }; KLine.prototype.closePath=function(){ if(this.points[0].x!=this.points[this.points.length-1].x || this.points[0].y!=this.points[this.points.length-1].y) this.points.push(this.points[0].clone()); }; KLine.prototype.getInteriorAngle=function(p,s1,s2){ var d9=p.x-s2.x,d10=p.y-s2.y,d11=s1.x-p.x,d12=s1.y-p.y; var d6=d9*d11+d10*d12; var d8=(d9*d9+d10*d10)*(d11*d11+d12*d12);if(d8==0) return 0; d6=-d6/Math.sqrt(d8); if(d6>=1) return 0; if(d6<=-1) return Math.PI; var d7=Math.acos(d6); if((d9*d12-d10*d11)>0) return d7;else return Math.PI*2-d7; }; KLine.prototype.isHole=function(){ var lastPt; if(this.points[0].equals(this.points[this.points.length-1])){ // ring is closed if(this.points.length<4) return false; lastPt=this.points.length-2; }else{ // ring is NOT closed if(this.points.length<3) return false; lastPt=this.points.length-1; } var ang=this.getInteriorAngle(this.points[lastPt],this.points[lastPt-1],this.points[0]); var interiorAng=ang; var exteriorAng=Math.PI*2-ang; var ang=this.getInteriorAngle(this.points[0],this.points[lastPt],this.points[1]); interiorAng+=ang; exteriorAng+=Math.PI*2-ang; for(var j=0;jexteriorAng; }; KLine.prototype.snapToGrid=function(sizeX,sizeY){ var n=[]; var last=null; for(var a=0;atolerance){killable=false;break}; if(killable){killed[a]=true;succ=true}; } if(succ){ var n=[]; for(var a=0;ad12){ var x23=pt.x-this.points[a+1].x,y23=pt.y-this.points[a+1].y; d=Math.sqrt(x23*x23+y23*y23); }else d=Math.abs((x12*y13-y12*x13)/d12); if(d1){ shp=new KShape(kshp.type==2?KShape.stLine:KShape.stPolygon); for(var a=0;a> 4; } var shape,fac=Math.pow(10,precision); switch(type){ case 1: shape=new KShape(KShape.stPoint); shape.precision=precision; var line=new KLine(); var x=mf.readInt32();var y=mf.readInt32(); x/=fac;y/=fac; line.points.push(new KPoint(x,y)); shape.lines.push(line); break; case 2: shape=new KShape(KShape.stLine); shape.precision=precision; shape.lines.push(KShape.createFromCgfDecodeLine(mf,fac,false,-2)); break; case 3: case 4: shape=new KShape(type==3?KShape.stLine:KShape.stPolygon); if(type==4) shape.mapped=true; shape.precision=precision; var a=0; while(!mf.eof()){ shape.lines.push(KShape.createFromCgfDecodeLine(mf,fac,shape.type==KShape.stPolygon)); if(type==4 && a!=0) shape.lines[shape.lines.length-1].outerRing=0; a++; } break; case 5: shape=new KShape(KShape.stPolygon); shape.mapped=true; shape.precision=precision; while(!mf.eof()) { var numLines=mf.readUIntVarlen(); var outerIndex=shape.lines.length; for(var a=0;a=0;a--){ this.lines[a].snapToGrid(sizeX,sizeY); if((this.type==KShape.stPolygon && this.lines[a].points.length<4) || (this.type==KShape.stLine && this.lines[a].points.length<2)){ if(this.lines.length==1){ if(this.lines[0].points.length==0) this.type=KShape.stNull; else if(this.lines[0].points.length==1) this.type=KShape.stPoint; else this.type=KShape.stLine; }else this.lines.splice(a,1); } } }; KShape.prototype.generalize=function(tolerance){ for(var a=this.lines.length-1;a>=0;a--){ this.lines[a].generalize(tolerance); if((this.type==KShape.stPolygon && this.lines[a].points.length<4) || (this.type==KShape.stLine && this.lines[a].points.length<2)){ if(this.lines.length==1){ if(this.lines[0].points.length==0) this.type=KShape.stNull; else if(this.lines[0].points.length==1) this.type=KShape.stPoint; else this.type=KShape.stLine; }else this.lines.splice(a,1); } } }; KShape.prototype.distanceToPoint=function(pt,inPart){ if(this.type==KShape.stNull) return Number.MAX_VALUE; else if(this.type==KShape.stPoint) return this.lines[0].points[0].distanceToPoint(pt); else if(this.type==KShape.stPolygon && this.intersectsPoint(pt,inPart)!=0) return 0; var closest=Number.MAX_VALUE; for(var a=0;aext.xmax || pt.yext.ymax) return 0; for(var a=0;a1) return "MULTIPOLYGON("+outParts.join(",")+")"; else return null; break; } }; KShape.prototype.toGml=function(crs,precision){ var me=this.cloneForExport(precision); switch(me.type){ case KShape.stNull: return null;break; case KShape.stPoint: return me.lines[0].points.length?''+me.lines[0].points[0].x+' '+me.lines[0].points[0].y+'':null;break; case KShape.stLine: var outLines=[]; for(var a=0;a1?"":"")+''+pts.join(" ")+""+(me.lines.length>1?"":"")); } if(outLines.length==1) return ''+outLines[0]+""; else return ''+outLines.join("")+""; break; case KShape.stPolygon: if(!me.mapped) me.mapOuterRings(); var polys=[]; for(var a=0;a":"")+''+pts.join(" ")+""+(b?"":"")); } outParts.push((polys.length>1?"":"")+outLines.join("")+(polys.length>1?"":"")); } if(polys.length==1) return ''+outParts[0]+""; else if(polys.length>1) return ''+outParts.join("")+""; else return null; break; } }; KShape.prototype.cloneForExport=function(precision){ var me=this.clone(); if(me.lines.length==0) me.type==KShape.stNull; if(me.type==KShape.stPolygon) for(var a=0;ad12){ var x23=Point[0]-LineB[0];var y23=Point[1]-LineB[1];return [Math.sqrt(x23*x23+y23*y23),NewPt]; }else return [Math.abs((x12*y13-y12*x13)/d12),NewPt]; } /** * @fn KLinesIntersect * @param line1A * @param line1B * @param line2A * @param line2B * @returns {Number} 0 = no intersection, 1 = intersection detected, -1 = lines are parallel */ function KLinesIntersect(line1A,line1B,line2A,line2B){ var rx=line1B.x-line1A.x; var ry=line1B.y-line1A.y; var sx=line2B.x-line2A.x; var sy=line2B.y-line2A.y; var det=sx*ry-sy*rx; if(det==0) // lines are parallel return -1; else{ var z=-(rx*(line1A.y-line2A.y)+ry*(line2A.x-line1A.x))/det; if(z<0 || z>1) return 0; // when z=0 or z=1 intersection at end point! z=(sx*(line2A.y-line1A.y)+sy*(line1A.x-line2A.x))/det; if(z<0 || z>1) return 0; // when z=0 or z=1 intersection at end point! return 1; } } /** * @fn KLinesIntersectionPoint * @param line1A * @param line1B * @param line2A * @param line2B * @param intersectionPoint * @param snapTolerance * @param openEnds * @returns {Number} 0 = no intersection, 1 = intersection detected, -1 = lines are parallel */ function KLinesIntersectionPoint(line1A,line1B,line2A,line2B,intersectionPoint,snapTolerance,openEnds){ var rx=line1B.x-line1A.x; var ry=line1B.y-line1A.y; var sx=line2B.x-line2A.x; var sy=line2B.y-line2A.y; var det=sx*ry-sy*rx; if(det==0) // lines are parallel return -1; else{ var z=-(rx*(line1A.y-line2A.y)+ry*(line2A.x-line1A.x))/det; if((z<0 || z>1) && !openEnds) return 0; // when z=0 or z=1 intersection at end point! z=(sx*(line2A.y-line1A.y)+sy*(line1A.x-line2A.x))/det; if((z<0 || z>1) && !openEnds) return 0; // when z=0 or z=1 intersection at end point! if(snapTolerance===undefined) snapTolerance=0; if(Math.abs(z)<=snapTolerance) // snap to start point intersectionPoint.set(line1A.x,line1A.y); else if(Math.abs(z-1)<=snapTolerance) // snap to end point intersectionPoint.set(line1B.x,line1B.y); else intersectionPoint.set(line1A.x+z*rx,line1A.y+z*ry); return 1; } } /** * @class KMultiShape * @constructor */ function KMultiShape(){ this.shapes=[]; } KMultiShape.createFromWkt=function(wkt,precision){ var ms=new KMultiShape(); if(wkt.substr(0,18)=="GEOMETRYCOLLECTION"){ var i=wkt.indexOf("("); wkt=wkt.substr(i+1,wkt.length-i-2); var stage=0,j=0; for(i=0;i"+this.shapes[a].toGml(crs,precision)+""); return ''+gmls.join("")+''; } };