Text on path/js first implementation

This commit is contained in:
Komяpa 2011-05-22 15:41:11 +03:00
parent 88d1b584ef
commit abc263efb5
4 changed files with 242 additions and 19 deletions

104
src/javascript/geomops.js Normal file
View file

@ -0,0 +1,104 @@
function ST_Simplify( points, tolerance ) {
// helper classes
var Vector = function( x, y ) {
this.x = x;
this.y = y;
};
var Line = function( p1, p2 ) {
this.p1 = p1;
this.p2 = p2;
this.distanceToPoint = function( point ) {
// slope
var m = ( this.p2.y - this.p1.y ) / ( this.p2.x - this.p1.x );
// y offset
var b = this.p1.y - ( m * this.p1.x );
var d = [];
// distance to the linear equation
d.push( Math.abs( point.y - ( m * point.x ) - b ) / Math.sqrt( Math.pow( m, 2 ) + 1 ) )
// distance to p1
d.push( Math.sqrt( Math.pow( ( point.x - this.p1.x ), 2 ) + Math.pow( ( point.y - this.p1.y ), 2 ) ) )
// distance to p2
d.push( Math.sqrt( Math.pow( ( point.x - this.p2.x ), 2 ) + Math.pow( ( point.y - this.p2.y ), 2 ) ) );
// return the smallest distance
return d.sort( function( a, b ) {
return ( a - b ) //causes an array to be sorted numerically and ascending
} )[0];
}
};
var douglasPeucker = function( points, tolerance ) {
var returnPoints = [];
if ( points.length <= 2 ) {
return [points[0]];
}
// make line from start to end
var line = new Line( points[0], points[points.length - 1] );
// find the largest distance from intermediate poitns to this line
var maxDistance = 0;
var maxDistanceIndex = 0;
for( var i = 1; i <= points.length - 2; i++ ) {
var distance = line.distanceToPoint( points[ i ] );
if( distance > maxDistance ) {
maxDistance = distance;
maxDistanceIndex = i;
}
}
// check if the max distance is greater than our tollerance allows
if ( maxDistance >= tolerance ) {
var p = points[maxDistanceIndex];
line.distanceToPoint( p, true );
// include this point in the output
returnPoints = returnPoints.concat( douglasPeucker( points.slice( 0, maxDistanceIndex + 1 ), tolerance ) );
// returnPoints.push( points[maxDistanceIndex] );
returnPoints = returnPoints.concat( douglasPeucker( points.slice( maxDistanceIndex, points.length ), tolerance ) );
} else {
// ditching this point
var p = points[maxDistanceIndex];
line.distanceToPoint( p, true );
returnPoints = [points[0]];
}
return returnPoints;
};
var arr = douglasPeucker( points, tolerance );
// always have to push the very last point on so it doesn't get left off
arr.push( points[points.length - 1 ] );
return arr;
};
function ST_AngleAndCoordsAtLength(geom, len){
var length = 0;
var seglen = 0;
var x,y,p,l;
var pc = geom[0];
//alert(len);
for (c in geom){
c = geom[c];
if (c==pc) continue;
seglen = Math.sqrt(((pc[0]-c[0])*(pc[0]-c[0]))+((pc[1]-c[1])*(pc[1]-c[1])));
if ((length+seglen)>=len){
length = len - length;
x = (c[0]-pc[0])*length/seglen + pc[0]; //x on length
y = (c[1]-pc[1])*length/seglen + pc[1]; //y on length
p = Math.atan2(c[1]-pc[1],c[0]-pc[0]); // angle on length
l = Math.sqrt(((x-c[0])*(x-c[0]))+((y-c[1])*(y-c[1]))); // how many pixels left with same angle
return [p,x,y,l];
}
pc=c;
length += seglen;
}
}
function ST_Length(geom){ // length for a line formed by coordinates array
var length = 0;
var pc = geom[0];
for (c in geom){
c = geom[c];
length += Math.sqrt((pc[0]-c[0])*(pc[0]-c[0])+(pc[1]-c[1])*(pc[1]-c[1]));
pc=c;
}
return length;
}

View file

@ -34,12 +34,124 @@ function pathGeoJSON(ctx, val, ws, hs, gran, dashes, fill){
};
}
}
/*function textOnGeoJSON(ctx, val, ws, hs, gran, dashes){
function textOnGeoJSON(ctx, val, ws, hs, gran, halo, collide, text){
if (val.type == "LineString"){
$.each(val.coordinates,function(key, val) {
if (dashes=="aaa"){ctx.lineTo(ws*val[0], hs*(gran-val[1]));
}
else {ctx.dashTo(ws*val[0], hs*(gran-val[1]));}
});
var projcoords = new Array();
var textwidth = 0;
var i = 0;
while (i<text.length){
var letter = text.substr(i,1);
textwidth += ctx.measureText(letter).width;
i++;
};
var aspect = textwidth / ctx.measureText(text).width;
for (coord in val.coordinates) {
coord = val.coordinates[coord];
projcoords.push([ws*coord[0], hs*(gran-coord[1])]);
};
//projcoords = ST_Simplify(projcoords, 1);
var linelength = ST_Length(projcoords);
if (linelength>textwidth) {
//alert("text: "+text+" width:"+textwidth+" space:"+linelength);
var widthused = 0;
var i = 0;
var prevangle = "aaa";
var positions = new Array();
var solution = 0;
var flipcount = 0;
var flipped = false;
while (solution < 2) {
if (solution == 0) widthused = linelength-textwidth/2;
if (solution == 1) widthused = 0;
flipcount = 0;
i = 0;
prevangle = "aaa";
positions = new Array();
while (i<text.length){
var letter = text.substr(i,1);
var letterwidth = ctx.measureText(letter).width/aspect;
var axy = ST_AngleAndCoordsAtLength(projcoords, widthused);
if (widthused>=linelength || !axy){
//alert("cannot fit text: "+text+" widthused:"+ widthused +" width:"+textwidth+" space:"+linelength+" letter:"+letter+" aspect:"+aspect);
solution++;
positions = new Array();
if (flipped) {projcoords.reverse(); flipped=false;}
break;
} // cannot fit
if (prevangle=="aaa") prevangle = axy[0];
if (
collide.checkPointWH([axy[1], axy[2]],
2.5*letterwidth,
2.5*letterwidth)
|| Math.abs(prevangle-axy[0])>0.2){
i = 0;
positions = new Array();
letter = text.substr(i,1);
widthused += letterwidth;
continue;
}
/*while (letterwidth > axy[3] && i<text.length){
i++;
letter += text.substr(i,1);
letterwidth = ctx.measureText(letter).width;
if (
collide.checkPointWH([axy[1]+0.5*Math.cos(axy[3])*letterwidth,
axy[2]+0.5*Math.sin(axy[3])*letterwidth],
2.5*letterwidth,
2.5*letterwidth)
|| Math.abs(prevangle-axy[0])>0.2){
i = 0;
positions = new Array();
letter = text.substr(i,1);
break;
}
}*/
if (axy[0]>Math.PI/2||axy[0]<-Math.PI/2){flipcount+=letter.length};
prevangle = axy[0];
axy.push(letter);
positions.push(axy);
widthused += letterwidth;
i++;
}
if (flipped && flipcount>text.length/2) {projcoords.reverse(); flipped=false;positions = new Array(); solution++; flipcount=0;}
if (!flipped && flipcount>text.length/2) {projcoords.reverse(); flipped=true;positions = new Array();}
if (solution>=2){ return}
if (positions.length>0) {break}
}
if (solution>=2){ return}
i = 0;
while (halo && i<positions.length){
var axy = positions[i];
var letter = axy[4];
ctx.save();
ctx.translate(axy[1],axy[2]);
ctx.rotate(axy[0]);
ctx.strokeText(letter, 0, 0);
ctx.restore();
i++;
}
i=0;
while (i<positions.length){
var axy = positions[i];
var letter = axy[4];
var letterwidth = ctx.measureText(letter).width;
ctx.save();
ctx.translate(axy[1],axy[2]);
ctx.rotate(axy[0]);
collide.addPointWH([axy[1]+0.5*Math.cos(axy[3])*letterwidth,
axy[2]+0.5*Math.sin(axy[3])*letterwidth],
2.5*letterwidth,
2.5*letterwidth)
//collide.addPointWH([axy[1],axy[2]],2.5*letterwidth+20,2.5*letterwidth+20);
ctx.fillText(letter, 0, 0);
ctx.restore();
i++;
}
};
}
}*/
}

View file

@ -10,6 +10,7 @@
<script src="imagesq.js"></script>
<script src="render.js"></script>
<script src="rgbcolor.js"></script>
<script src="geomops.js"></script>
</head>

View file

@ -5,12 +5,12 @@ draw = function () {
var start = new Date().getTime();
var ctxa = document.getElementById('canvas');
ctxa.width = ctxa.width;
ctxa.height = ctxa.height;
ctxa.width = 2*ctxa.width;
ctxa.height = 2*ctxa.height;
var ctx = ctxa.getContext('2d');
var ws = ctxa.width/data.granularity;
var hs = ctxa.height/data.granularity;
var zoom = 12;
var zoom = 13;
var style = restyle({}, zoom, "canvas")["default"];
if ("fill-color" in style){ctx.fillStyle = style["fill-color"];};
if ("opacity" in style){ctx.globalAlpha = style["opacity"];};
@ -173,7 +173,7 @@ draw = function () {
$.each(dat, function(key, val) { // text pass
ctx.save()
style = val.style;
if ("text" in style && !("icon-image" in style)) {
if ("text" in style && !("icon-image" in style) && style["text"]!="") {
var offset = 0;
var opacity = 1;
var mindistance = 0;
@ -188,7 +188,7 @@ draw = function () {
var point;
if (val.type == "Point"){ point = [ws*val.coordinates[0],hs*(data.granularity-val.coordinates[1])]};
if (val.type == "Polygon"){ point = [ws*val.reprpoint[0],hs*(data.granularity-val.reprpoint[1])]};
if (val.type == "LineString"){return; point = [ws*val.coordinates[0][0],hs*(data.granularity-val.coordinates[0][1])]};
if (val.type == "LineString"){ point = [ws*val.coordinates[0][0],hs*(data.granularity-val.coordinates[0][1])]};
if (style["text"]){ctx.font = fontString(style["font-family"],style["font-size"]);};
textwidth = ctx.measureText(style["text"]).width;
if (!(style["text-allow-overlap"]=="true")&&collides.checkPointWH([point[0],point[1]+offset], textwidth, 5)) return;
@ -200,18 +200,23 @@ draw = function () {
ctx.textAlign = "center";
ctx.textBaseline = "middle";
if ("text-halo-radius" in style)
ctx.strokeText(style["text"], point[0],point[1]+offset);
ctx.fillText(style["text"], point[0],point[1]+offset);
collides.addPointWH([point[0],point[1]+offset], textwidth, 10, mindistance);
if (val.type=="Polygon" || val.type == "Point"){
if ("text-halo-radius" in style)
ctx.strokeText(style["text"], point[0],point[1]+offset);
ctx.fillText(style["text"], point[0],point[1]+offset);
collides.addPointWH([point[0],point[1]+offset], textwidth, 10, mindistance);
}
else{//Linestring
textOnGeoJSON(ctx, val, ws, hs, data.granularity, ("text-halo-radius" in style), collides, style["text"])
};
};
ctx.restore();
});
/*for (poly in collides.buffer){
collides.buffer = new Array();
for (poly in collides.buffer){
poly = collides.buffer[poly];
ctx.fillRect(poly[0],poly[1],poly[2]-poly[0],poly[3]-poly[1])
}*/
}
});
var elapsed = new Date().getTime()-start;
alert(elapsed);
@ -227,6 +232,7 @@ fontString = function(name, size){
name = name.toLowerCase();
if (name.indexOf("italic")>=0) italic = "italic";
if (name.indexOf("oblique")>=0) italic = "italic";
if (name.indexOf("bold")>=0) weight = "700";
//alert(name);
if (name.indexOf("serif")>=0) family = "sans-serif";
if (name.indexOf("dejavu sans")>=0) family = '"DejaVu Sans", Arial, sans';