Drawing irregular concentric circles using Google Maps - javascript

I have a bit of a problem. I am trying to do the following using Javascript & the Google Maps API v2:
I can draw individual circles just fine using formulas found all over the Internet. The problem I am facing is that the circles must:
A. Be concentric, and
B. Must have different radius for each "quadrant", i.e., NE, NW, SE & SW
I've searched almost everywhere I can think of on the Internet, and have come up with no way on how to do this. Clearly someone has done this before, and thus why I'm asking in a forum of programmers. :)
Thanks!
UPDATE: I have drawn out, using the following code, what I think the coordinates for each of the points would be. for the drawing below:
This was obtained using the following JS:
http://gist.github.com/181290
NOTE: This javascript is from (slightly modified) the following site, which may hold more answers in terms of what the algorithm may end up being: http://www.movable-type.co.uk/scripts/latlong.html
UPDATE 2: I was able to get this in Google Maps:
Created using the following code:
var NEQ = [0, 90];
var SEQ = [90, 180];
var SWQ = [180, 270];
var NWQ = [270, 0];
// var centrePoint = new LatLon(25.0, -83.1);
// pointsForWindQuadrant(NEQ, centrePoint, 50);
function pointsForWindQuadrant(quadrantDegrees, centrePoint, radius){
var points = [];
// Points must be pushed into the array in order
points.push(new google.maps.LatLng(centrePoint.lat, centrePoint.lon));
for(i = quadrantDegrees[0]; i <= quadrantDegrees[1]; i++){
var point = centrePoint.destPoint(i, radius * 1.85);
points.push(new google.maps.LatLng(point.lat, point.lon)); // Radius should be in nautical miles from NHC
}
points.push(new google.maps.LatLng(centrePoint.lat, centrePoint.lon));
return points;
}
UPDATE 3: I should probably also point out that this is for a geographic coordinate system (as this whole thing is for tropical cyclone wind radii), not the Cartesian coordinate system. Thanks!

Basically, calculate the circle as the x,y = (cos(a), sin(a)), and then multiple this (both terms) by a radius that's the appropriate function of the angle. I don't know Javascript well, or Google maps, so I'll do this in Python, hopefully it's clear enough from this.
from pylab import *
def Rscale(a):
if a>3*pi/2: # lower right, and then work CW around the circle
return 1.
elif a>pi: # lower left
return .9
elif a>pi/2: # upper left
return .8
else: # upper right
return 1.
def step_circle(R):
return array([(R*Rscale(a))*array([cos(a), sin(a)]) for a in arange(0, 2*pi, .001)])
for R in (.5, .7, .9): # make three concentric circles
c = step_circle(R)
plot(c[:,0], c[:,1])
show()
Which gives
I couldn't really follow your sketch, so I just guessed at the numbers. Also, I made the two rightmost quadrants to be the same since that's what your plot looked like, but that is, of course, optional.

I figured it out. Here is the final code. Maybe it can be refactored a bit?
// Returns points for a wind field for a cyclone. Requires
// a LatLon centre point, and an array of wind radii, starting
// from the northeast quadrant (NEQ), i.e., [200, 200, 150, 175]
//
// Returns points to be used in a GPolyline object.
function pointsForWindQuadrant(centrePoint, radii){
if(radii.length != 4){ return false; }
var points = [];
var angles = [0, 90, 180, 270];
// For each angle 0, 90, 180, 270...
for(a = 0; a < angles.length; a++){
// For each individual angle within the range, create a point...
for(i = angles[a]; i <= angles[a] + 90; i++){
var point = centrePoint.destPoint(i, radii[a] * 1.85); // Radius should be in nautical miles from NHC
points.push(new google.maps.LatLng(point.lat, point.lon));
}
}
// Add the first point again, to be able to close the GPolyline
var point = centrePoint.destPoint(0, radii[0] * 1.85);
points.push(new google.maps.LatLng(point.lat, point.lon));
return points;
}
This results in the following:

Related

Generate coordinates given distance, angle from a center

I have a given center in the map [x1,y1]. From that center I am drawing a circle with a 1 mile radius. I need to generate 8 more points around the circle, the distance between the individual points to center should be 1 mile, so they are on the circle bounds. I do know the formulas to get x2, y2 but the problem is it doesn't apply to earth's map since it isn't a perfect sphere.
I've tried using this, but with no luck.
Could anyone point me somewhere or maybe I got this wrong ?
Edit: solved !
So reading carefully throughout Movable Type Scripts I found this (slightly modified for my use):
let getPoint = (distance, bearing, center) => {
let δ = Number(distance) / 6371e3;
let θ = Number(bearing).toRadians();
let φ1 = center[0].toRadians();
let λ1 = center[1].toRadians();
let sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1);
let sinδ = Math.sin(δ), cosδ = Math.cos(δ);
let sinθ = Math.sin(θ), cosθ = Math.cos(θ);
let sinφ2 = sinφ1*cosδ + cosφ1*sinδ*cosθ;
let φ2 = Math.asin(sinφ2);
let y = sinθ * sinδ * cosφ1;
let x = cosδ - sinφ1 * sinφ2;
let λ2 = λ1 + Math.atan2(y, x);
return [φ2.toDegrees(), (λ2.toDegrees()+540)%360-180];
};
It did solved my problem.
You are trying to solve what is known as the first (or direct) geodetic problem. Knowing this name will make your research easier.
As pointed out by the answers to "How to draw polyline perpendicular to another polyline using Leaflet" and "Find destination coordinates given starting coodinates, bearing, and distance", your main options to approach this problem in javascript are cheap-ruler for small(ish) areas and greographiclib for large distances.
cheap-ruler tends to be very fast but inaccurate, and geographiclib tends to be slower but very accurate.
You might find other implementations, each with its own compromises. Geodesy is hard, so there is no "one true way" to calculate distances or azimuths.

Connected vertices from polygon, how to get the new polygons

I have a polygon, where some vertices are connected with lines (the startpoint and the endpoint are a vertice of the polygon). For each line (that connects vertices) are 4 rules:
The line doesn't intersect with other lines
The line doesn't intersect with the polygon
The line is completely contained by the polygon
The line isn't an edge of the polygon
Example:
In the image, the red lines are bad lines,
the black lines are polygon edges and the green line is a good line.
Line h is good
Line i is bad, because it intersects the polygon
Line j is bad, because it intersects line h and i
Line k is bad, because it is an edge of the polygon
Line g is bad, because it is not contained by the polygon
I have an array with the vertices of the polygon, and an array with the lines, like this:
polygon = [
{x: ..., y: ...},
...
]
lines = [
{
p1: {x: ..., y: ...},
p2: {x: ..., y: ...}
},
...
]
The lines array contains only valid lines.
How can I get the polygons that are sliced by the lines.
I want something like this:
function getSlicedPolygons(polygon, lines){
// No check for valid lines, they are already valid
// Do the algorithm...
}
What I've tried so far
I start on the first vertex and go next until I reach a connected vertex. From that vertex, I go to the vertex that's connected at the other ending of the line. Now I go next until another vertex that's connected and so on until I reached the vertex where I started. Now I have the first polygon. I couldn't find the others...
Code (implementation, not real code):
function getSlicedPolygons(polygon, line){
var results = []
var ofl = 0; // Overflow counter, to prevent infinite looping
while(lines.length > 0){
// Iterate through polygon
var i = 0;
var r = []; // Array of new indices
var iterations = 0; // An overflow counter again
while(i < polygon.length){
r.push[i]
// findNextConnectionIndex(...) searches for a line
// that connects with this vertex and returns the index of
// the other connected vertex
var n = findNextConnectionIndex(i, polygon, lines) || i+1
i=n;
// Don't loop infinite
iterations++;
if(iterations > 10) break;
}
var result = [];
for(var z = 0; z<r.length; z++){
result.push(polygon[r[z]])
}
results.push(result)
// Now I should do something to get another polygon next
// time ...
// Don't loop infinite
ofl++;
if(ofl >= 10) break;
}
return results;
}
It returns the same polygon 10 times in an array...
Treat the polygon and its intersecting lines as an undirected graph with loops and apply a loop-detection algorithm to it. Since we know the connecting lines, things become a bit more simple and we can actually solve the problem in O(V).
This would be a model that suffices to explain the basic principle. We can transform our polygon into a rectangle that is sliced by the list of lines. Since no lines may intersect, this will also apply to the resulting rectangle. Now one can start in an corner of the graph and travel alongside both edges until on both paths a vertex of degree 3 is reached. Thus we've found our first polygon that results from slicing the original polygon. Go on from the two points reached in the previous step until you reach vertices of degree 3 are reached again. Terminate this step when the two paths meet and you've listed all possible polygons.
A diagram of running a single step of this process:
Finding the "corner" vertex
Start from an arbitrary point in the graph/polygon and traverse the vertices along the polygon in an arbitrary direction until a vertex of degree 3 is reached. Store the corresponding slicing line and proceed along the polygon until the vertex of degree 3 is reached. If it is the same slicing line, you've found a "corner" vertex, else store the new slicing line and repeat.
EDIT
A working implementation in python:
def slice_polygon(poly, part):
# find the "corner point"
last_slice = None
last_pt = None
for pt in poly:
s = [x for x in part if pt in x]
if s:
if last_slice in s:
break
last_slice = s[0]
last_pt = pt
# find slicing starting from border-point
a = poly.index(last_pt)
b = (a + 1) % len(poly) # current positions on the polygon
sliced_poly = [] # current polygon
slicing = [] # list of all polygons that are created by the slicing
while a != b:
if not [x for x in part if poly[a] in x]:
# point doesn't lie on slicing-line => add to current polygon
sliced_poly.insert(0, poly[a]) # prepend point
a = (a - 1 + len(poly)) % len(poly) # advance a by one
elif not [x for x in part if poly[b] in x]:
# point doesn't lie on slicing-line => add to current polygon
sliced_poly.append(poly[b]) # append point
b = (b + 1 + len(poly)) % len(poly) # advance by one
else:
# append points of slicing line
sliced_poly.insert(0, poly[a])
sliced_poly.append(poly[b])
# store created polygon and start over
slicing.append(sliced_poly)
sliced_poly = []
# remove partitioning-line at which the algorithm stopped
part.remove([x for x in part if poly[a] in x and poly[b] in x][0])
# add last point to the current polygon, as it's not yet added to it
sliced_poly.append(poly[a])
# add last polygon to result-set
slicing.append(sliced_poly)
return slicing
# test
polygon = [(150, 110), (270, 40), (425, 90), (560, 150), (465, 290), (250, 290), (90, 220)]
partition = [((270, 40), (250, 290)), ((425, 90), (250, 290))]
print(slice_polygon(polygon, partition))
Output:
[[(425, 90), (560, 150), (465, 290), (250, 290)], [(270, 40), (425, 90), (250, 290)], [(90, 220), (150, 110), (270, 40), (250, 290)]]
Input:
Since there is a total of two "corner-points" (at least), we are guaranteed to find at least one, if we traverse the polygon once.

SVG find rotation angle of a path

I've a problem with my SVG map.
I use jVectorMap to create a custom map and I need to write the name of every field in the center of the field.
The example is: JSFiddle Example (zoom in the right side to see the text)
I can find the center of every field with this function:
jvm.Map.prototype.getRegionCentroid = function(region){
if(typeof region == "string")
region = this.regions[region.toUpperCase()];
var bbox = region.element.shape.getBBox(),
xcoord = (bbox.x + bbox.width/2),
ycoord = (bbox.y + bbox.height/2);
return [xcoord, ycoord];
};
but my problem is that I want to rotate the text for align it with the top line of the relative field.
I've tried with getCTM() function but it give me always the same values for every field.
How can I find the right rotation angle of every field?
Thank you to all!
Looks like squeamish ossifrage has beaten me to this one, and what they've said would be exactly my approach too...
Solution
Essentially find the longest line segment in each region's path and then orient your text to align with that line segment whilst trying to ensure that the text doesn't end up upside-down(!)
Example
Here's a sample jsfiddle
In the $(document).ready() function of the fiddle I'm adding labels to all the regions but you will note that some of the regions have centroids that aren't within the area or non-straight edges that cause problems - Modifying your map slightly might be the easiest fix.
Explanation
Here are the 3 functions I've written to demonstrate the principles:
addOrientatedLabel(regionName) - adds a label to the named region of the map.
getAngleInDegreesFromRegion(regionName) - gets the angle of the longest edge of the region
getLengthSquared(startPt,endPt) - gets length squared of line seg (more efficient than getting length).
addOrientatedLabel() places the label at the centroid using a translate transform and rotates the text to the same angle as the longest line segment in the region. In SVG transforms are resolved right to left so:
transform="translate(x,y) rotate(45)"
is interpreted as rotate first, then translate. This ordering is important!
It also uses text-anchor="middle" and dominant-baseline="middle" as explained by squeamish ossifrage. Failing to do this will cause the text to be misaligned within its region.
getAngleInDegreesFromRegion() is where all the work is done. It gets the SVG path of the region with a selector, then loops through every point in the path. Whenever a point is found that is part of a line segment (rather than a Move-To or other instruction) it calculates the squared length of the line segment. If the squared length of the line segment is the longest so far it stores its details. I use squared length because that saves performing a square root operation (its only used for comparison purposes, so squared length is fine).
Note that I initialise the longestLine data to a horizontal one so that if the region has no line segments at all you'll at least get horizontal text.
Once we have the longest line, I calculate its angle relative to the x axis with Math.atan2, and convert it from radians to degrees for SVG with (angle / Math.PI) * 180. The final trick is to identify if the angle will rotate the text upside down, and if so, to rotate another 180 degrees.
Note
I've not used SVG before so my SVG code might not be optimal, but it's tested and it works on all regions that consist mostly of straight line segments - You will need to add error checking for a production application of course!
Code
function addOrientatedLabel(regionName) {
var angleInDegrees = getAngleInDegreesFromRegion(regionName);
var map = $('#world-map').vectorMap('get', 'mapObject');
var coords = map.getRegionCentroid(regionName);
var svg = document.getElementsByTagName('g')[0]; //Get svg element
var newText = document.createElementNS("http://www.w3.org/2000/svg","text");
newText.setAttribute("font-size","4");
newText.setAttribute("text-anchor","middle");
newText.setAttribute("dominant-baseline","middle");
newText.setAttribute('font-family', 'MyriadPro-It');
newText.setAttribute('transform', 'translate(' + coords[0] + ',' + coords[1] + ') rotate(' + angleInDegrees + ')');
var textNode = document.createTextNode(regionName);
newText.appendChild(textNode);
svg.appendChild(newText);
}
Here's my method to find the longest line segment in a given map region path:
function getAngleInDegreesFromRegion(regionName) {
var svgPath = document.getElementById(regionName);
/* longest edge will default to a horizontal line */
/* (in case the shape is degenerate): */
var longestLine = { startPt: {x:0, y:0}, endPt: {x:100,y:0}, lengthSquared : 0 };
/* loop through all the points looking for the longest line segment: */
for (var i = 0 ; i < svgPath.pathSegList.numberOfItems-1; i++) {
var pt0 = svgPath.pathSegList.getItem(i);
var pt1 = svgPath.pathSegList.getItem(i+1);
if (pt1.pathSegType == SVGPathSeg.PATHSEG_LINETO_ABS) {
var lengthSquared = getLengthSquared(pt0, pt1);
if( lengthSquared > longestLine.lengthSquared ) {
longestLine = { startPt:pt0, endPt:pt1, lengthSquared:lengthSquared};
}
}/* end if dealing with line segment */
}/* end loop through all pts in svg path */
/* determine angle of longest line segement relative to x axis */
var dY = longestLine.startPt.y - longestLine.endPt.y;
var dX = longestLine.startPt.x - longestLine.endPt.x;
var angleInDegrees = ( Math.atan2(dY,dX) / Math.PI * 180.0);
/* if text would be upside down, rotate through 180 degrees: */
if( (angleInDegrees > 90 && angleInDegrees < 270) || (angleInDegrees < -90 && angleInDegrees > -270)) {
angleInDegrees += 180;
angleInDegrees %= 360;
}
return angleInDegrees;
}
Note that my getAngleInDegreesFromRegion() method will only consider the longest straight line in a path if it is created with the PATHSEG_LINETO_ABS SVG command... You'll need more functionality to handle regions which don't consist of straight lines. You could approximate by treating curves as straight lines with:
if (pt1.pathSegType != SVGPathSeg.PATHSEG_MOVETO_ABS )
But there will be some corner cases, so modifying your map data might be the easiest approach.
And finally, here's the obligatory squared distance method for completeness:
function getLengthSquared(startPt, endPt ) {
return ((startPt.x - endPt.x) * (startPt.x - endPt.x)) + ((startPt.y - endPt.y) * (startPt.y - endPt.y));
}
Hope that is clear enough to help get you started.
Querying getCTM() won't help. All that gives you is a transformation matrix for the shape's coordinate system (which, as you discovered, is the same for every shape). To get a shape's vertex coordinates, you'll have to examine the contents of region.element.shape.pathSegList.
This can get messy. Although a lot of the shapes are drawn using simple "move-to" and "line-to" commands with absolute coordinates, some use relative coordinates and other types of line. I noticed at least one cubic curve. It might be worth looking for an SVG vertex manipulation library to make life easier.
But in general terms, what you need to do is fetch the list of coordinates for each shape (converting relative coordinates to absolute where necessary), and find the segment with the longest length. Be aware that this may be the segment between the two end points of the line. You can easily find the orientation of this segment from Math.atan2(y_end-y_start,x_end-x_start).
When rotating text, make life easy for yourself by using a <g> element with a transform=translate() attribute to move the coordinate origin to where the text needs to be. Then the text won't shoot off into the distance when you add a transform=rotate() attribute to it. Also, use text-anchor="middle" and dominant-baseline="middle" to centre the text where you want it.
Your code should end up looking something like this:
var svg = document.getElementsByTagName('g')[0]; //Get svg element
var shape_angle = get_orientation_of_longest_segment(svg.pathSegList); //Write this function
var newGroup = document.createElementNS("http://www.w3.org/2000/svg","g");
var newText = document.createElementNS("http://www.w3.org/2000/svg","text");
newGroup.setAttribute("transform", "translate("+coords[0]+","+coords[1]+")");
newText.setAttribute("font-size","4");
newText.setAttribute("text-anchor","middle");
newText.setAttribute("dominant-baseline","middle");
newText.setAttribute("transform","rotate("+shape_angle+")");
newText.setAttribute('font-family', 'MyriadPro-It');
var textNode = document.createTextNode("C1902");
newText.appendChild(textNode);
newGroup.appendChild(newText);
svg.appendChild(newGroup);

Create a map with coordinates-based marker using Javascript, jQuery and/or php

I am developing a client-server application and I need to create or obtain a geographical map with some markers (just red points, for example) on it based on lat and lang.
I have been looking on the web but I couldn't find anything ok for me.
I could do it using google maps but I think I can't use my own custom map-image and my custom markers.
However, this is an example of what I'd like to do more or less having some coordinates:
http://www.morrisda.com/?post=pointmap
Here explanation of how i managed this. With example !
Here source code:
function make_pointers(latitude, longitude) {
targetmap = document.getElementById("point_map");
//it's sure it will work when your map is a perfec square, so it's got height on the x and on the y for usefull debugging.
var asseX = $('#point_map').height()
var asseY = $('#point_map').height()
//lat and lang from imput
lang = longitude;
lat = latitude;
var source = new Proj4js.Proj('EPSG:4326'); //source coordinates will be in Longitude/Latitude, WGS84
var dest = new Proj4js.Proj('EPSG:3785'); //destination coordinates in meters, global spherical mercators projection, see http://spatialreference.org/ref/epsg/3785/
var mercator_object = new Proj4js.Point(lang,lat); //any object will do as long as it has 'x' and 'y' properties
Proj4js.transform(source, dest, mercator_object);
give_top = 19971868.8804*2 //this is how a meridian is long.
mercator_longitude = mercator_object.y //this is longitude by mercator
//this switch is if latitude is negative;
if (mercator_object.y > 0) {mercator_object.y = mercator_object.y + give_top/2}
if (mercator_object.y < 0 ) {mercator_object.y = give_top/2 + mercator_object.y}
//this is my lovely proportion, value of give_top is length of a meridian
mercator_distance = (asseY*mercator_longitude)/give_top;
//distance from 0:
y = asseY/2 - mercator_distance;
//proportion to make working on x;
//(180 + lang) makes lang from 0 to 360 instead of -180, +180.
//to understand, now image equator as a line, long 360.
//now we divide our coords (from 0 to 360) for 360, and the result is how far is this point from 0 on a 360-long line.
//now a simple proportion, and we get this distance on a line long like x axis, which is the width of our map.
// % is a Modulus (division remainder)
normalized_x_coords = (180 + lang)
x = (asseX * normalized_x_coords/ 360) % asseX;
//let's put this little points on my map (subtract -3 cause our point is large 6.)
console.log(x); //this is distance from left border;
console.log(y) //this is distance from bottom border
}
Remember you need jQuery and ProJ4js to use this. More info in previous link
Have a look at Slippy Map On Canvas project that is a implemenation of a slippy tiles map using HTML5. You can configure your own map and markers, and it also supports many other features which may be useful for you.

Glitches with Triangulation + Linear Interpolation + 3D projection

Intro
Hey!
Some weeks ago, I did a small demo for a JS challenge. This demo was displaying a landscape based on a procedurally-generated heightmap. To display it as a 3D surface, I was evaluating the interpolated height of random points (Monte-Carlo rendering) then projecting them.
At that time, I was already aware of some glitches in my method, but I was waiting for the challenge to be over to seek some help. I'm counting on you. :)
Problem
So the main error I get can be seen in the following screenshot:
Screenshot - Interpolation Error? http://code.aldream.net/img/interpolation-error.jpg
As you can see in the center, some points seem like floating above the peninsula, forming like a less-dense relief. It is especially obvious with the sea behind, because of the color difference, even though the problem seems global.
Current method
Surface interpolation
To evaluate the height of each point of the surface, I'm using triangulation + linear interpolation with barycentric coordinates, ie:
I find in which square ABCD my point (x, y) is, with A = (X,Y), B = (X+1, Y), C = (X, Y+1) and D = (X+1, Y+1), X and Y being the truncated value of x, y. (each point is mapped to my heightmap)
I estimate in which triangle - ABD or ACD - my point is, using the condition: isInABD = dx > dy with dx, dy the decimal part of x, y.
I evaluate the height of my point using linear interpolation:
if in ABD, height = h(B) + [h(A) - h(B)] * (1-dx) + [h(D) - h(B)] * dy
if in ACD, height = h(C) + [h(A) - h(C)] * (1-dy) + [h(D) - h(C)] * dx, with h(X) height from the map.
Displaying
To display the point, I just convert (x, y, height) into the world coordinates, project the vertex (using simple perspective projection with yaw and pitch angles). I use a zBuffer I keep updated to check if I draw or not the obtained pixel.
Attempts
My impression is that for some points, I get a wrong interpolated height. I thus tried to search for some errors or some non-covered boundaries cases, in my implementation of the triangulation + linear interpolation. But if there are, I can't spot them.
I use the projection in other demos, so I don't think the problem comes from here. As for the zBuffering, I can't see how it could be related...
I'm running out of luck here... Any hints are most welcome!
Thank for your attention, and have a nice day!
Annexe
JsFiddle - Demo
Here is a jsFiddle http://jsfiddle.net/PWqDL/ of the whole slightly simplified demo, for those who want to tweak around...
JsFiddle - Small test for the interpolation
As I was writing down this question, I got an idea to have a better look at the results of my interpolation. I implemented a simple test in which I use a 2x2 matrix containing some hue values, and I interpolate the intermediate colors before displaying them in the canvas.
Here is the jsFiddle: http://jsfiddle.net/y2K7n/
Alas, the results seem to match the expected behavior for the kind of "triangular" interpolation I'm doing, so I'm definitly running out of ideas.
Code sample
And here is the simplified most-probably-faulty part of my JS code describing my rendering method (but the language doesn't matter much here I think), given a square heightmap "displayHeightMap" of size (dim x dim) for a landscape of size (SIZE x SIZE):
for (k = 0; k < nbMonteCarloPointsByFrame; k++) {
// Random float indices:
var i = Math.random() * (dim-1),
j = Math.random() * (dim-1),
// Integer part (troncated):
iTronc = i|0,
jTronc = j|0,
indTronc = iTronc*dim + jTronc,
// Decimal part:
iDec = i%1,
jDec = j%1,
// Now we want to intrapolate the value of the float point from the surrounding points of our map. So we want to find in which triangle is our point to evaluate the weighted average of the 3 corresponding points.
// We already know that our point is in the square defined by the map points (iTronc, jTronc), (iTronc+1, jTronc), (iTronc, jTronc+1), (iTronc+1, jTronc+1).
// If we split this square into two rectangle using the diagonale [(iTronc, jTronc), (iTronc+1, jTronc+1)], we can deduce in which triangle is our point with the following condition:
whichTriangle = iDec < jDec, // ie "are we above or under the line j = jTronc + distanceBetweenLandscapePoints - (i-iTronc)"
indThirdPointOfTriangle = indTronc +dim*whichTriangle +1-whichTriangle, // Top-right point of the square or bottm left, depending on which triangle we are in.
// Intrapolating the point's height:
deltaHeight1 = (displayHeightMap[indTronc] - displayHeightMap[indThirdPointOfTriangle]),
deltaHeight2 = (displayHeightMap[indTronc+dim+1] - displayHeightMap[indThirdPointOfTriangle]),
height = displayHeightMap[indThirdPointOfTriangle] + deltaHeight1 * (1-(whichTriangle? jDec:iDec)) + deltaHeight2 * (!whichTriangle? jDec:iDec),
posX = i*distanceBetweenLandscapePoints - SIZE/2,
posY = j*distanceBetweenLandscapePoints - SIZE/2,
posZ = height - WATER_LVL;
// 3D Projection:
var temp1 = cosYaw*(posY - camPosY) - sinYaw*(posX - camPosX),
temp2 = posZ - camPosZ,
dX = (sinYaw*(posY - camPosY) + cosYaw*(posX - camPosX)),
dY = sinPitch*temp2 + cosPitch*temp1,
dZ = cosPitch*temp2 - sinPitch*temp1,
pixelY = dY / dZ * minDim + canvasHeight,
pixelX = dX / dZ * minDim + canvasWidth,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd])) { // We check if what we want to draw will be visible or behind another element. If it will be visible (for now), we draw it and update the zBuffer:
zBuffer[canvasInd] = dZ;
// Color:
a.fillStyle = a.strokeStyle = EvaluateColor(displayHeightMap, indTronc); // Personal tweaking.
a.fillRect(pixelX, pixelY, 1, 1);
}
}
Got it. And it was as stupid a mistake as expected: I was reinitializing my zBuffer each frame...
Usually it's what you should do, but in my case, each frame (ie call of my Painting() function) adds details to the same frame (ie drawed static scene from a constant given point of view).
If I reset my zBuffer at each call of Painting(), I lose the depth information of the points drawn during the previous calls. The corresponding pixels are thus considered as blank, and will be re-painted for any projected points, without any regard for their depth.
Note: Without reinitiliazation, the zBuffer gets quite big. Another fix I should have done earlier was thus to convert the pixel's positions of the projected point (and thus the indices of the zBuffer) into integer values:
pixelY = dY / dZ * minDim + canvasHeight +.5|0,
pixelX = dX / dZ * minDim + canvasWidth +.5|0,
canvasInd = pixelY * canvasWidth*2 + pixelX;
if (dZ > 0 && (!zBuffer[canvasInd] || (dZ < zBuffer[canvasInd]))) {
// We draw the point and update the zBuffer.
}
Fun fact
If the glitches appeared more obvious for relief with the sea behind, it wasn't only for the color difference, but because the hilly parts of the landscape need much more points to be rendered than flat areas (like the sea), given their stretched surface.
My simplistic Monte-Carlo sampling of points doesn't take this characteristic into account, which means that at each call of Painting(), the sea gains statistically more density than the lands.
Because of the reinitialization of the zBuffer each frame, the sea was thus "winning the fight" in the picture's areas where mountains should have covered it (explaining the "ghostly mountains" effect there).
Corrected JsFiddle
Corrected version for those interested: http://jsfiddle.net/W997s/1/

Categories