im trying to see if this
Math.sqrt(
Math.pow((position.coords.latitude -45),2) +
Math.pow((position.coords.longitude-75),2)
)*79;
Matches this:
Distance to store (km) = Square Root (
(Current Latitude – 45)^2 +
(Current Longitude ‐75)^2
) *79
Right now im getting 11,XXX KM which is way to much, but I don't see any mistakes.
I also tried doing it like this:
var x = Math.pow((position.coords.latitude-45),2);
var y = Math.pow((position.coords.longitude-75),2);
var z = Math.sqrt(x+y);
var zz = z*79;
but it gave me the same answer.
This formula is valid for short distances to the point of latitude 45 and longitude 75.
What you want, as you're not located in Kazakstan, is the distance to (45, -75), that is
Math.sqrt(Math.pow((lat-45),2)+Math.pow((lon+75),2))*79;
^
Note that it's only as precise as your coordinates are precise and that for bigger distances you should grab a better formula.
Related
There's no simple curved-line tool in turf.js, nor is there an easy way to do it in mapbox (so far as I can see), so I've created a workaround based on this answer in this thread.
However, the curve it creates isn't very smooth or satisfying or has an inconsistent hump based on angle/length.
Ideally, I'd like an arc that is always in a nice, rounded form.
and drawing a line between them. I then offset the midpoint by distance / 5 and apply a bearing. I then connect up the three points with a turf.bezierSpline.
const start = [parseFloat(originAirport.longitude), parseFloat(originAirport.latitude)];
const end = [
parseFloat(destinationAirport.longitude),
parseFloat(destinationAirport.latitude),
];
const distance = turf.distance(start, end, { units: 'miles' });
const midpoint = turf.midpoint(start, end);
const destination = turf.destination(midpoint, distance / 5, 20, { units: 'miles' });
// curvedLine gets rendered to the page
const curvedLine = turf.bezierSpline(
turf.lineString([start, destination.geometry.coordinates, end]),
);
Desired curvature:
Well, that question was created a very long time ago, but I recently encounter this problem.
If anybody is still wondering - this code is good is general, but you've missed one detail. We can't use hardcoded bearing value 20 in turf.destination method, because it's incorrect for most cases. We need our moved midpoint to be right at the middle of our geometry, so we have to find the right angle.
const bearing = turf.bearing(start, end);
Then - if we want our arc to be on the left side of our line, we need to add 90 degrees to our calculated bearing. If on the right side - substract 90 degrees, so:
const leftSideArc = bearing + 90 > 180 ? -180 + (bearing + 90 - 180) : bearing + 90;
NOTE!!! Bearing is value between -180 and 180 degrees. Our value have to be calculated properly in case it'll exceed this range.
And then we can pass our bearing to destination method:
const destination = turf.destination(midpoint, distance / 5, leftSideArc, { units: 'miles' });
Now we have a perfect arc.
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.
I have a flight path with lat/long and elevation and I need to convert this to cartesin X,Y,Z for cesium.js.
I am running into the wall trying to convert this because I don't seem to be getting the right results from my function.
var R = 6371;
function polarToCartesian(latitude, longitude, elevation){
x = (R+elevation) * math.cos(latitude) * math.cos(longitude);
y = (R+elevation) * math.cos(latitude) * math.sin(longitude);
z = (R+elevation) * math.sin(latitude);
var ar = [x,y,z];
return ar;
}
I must not either have the correct formula for polar to cartesian or I don't have the correct radius of earth. I found somewhere that my radius should be 6371 but can't seem to find that same SO question for reference.
I am partialy checking if my code is correct by manually adding up the radius of the earth + altitude of the flight path at a given location and seeing if this equals the length of my x,y,z vector.
For example: x,y,z (3689.2472215653725,3183.2401988117012,13306.90338789763)
is outputted when I give my function this
-93.028,44.6942,7800
lat,long,elevation
Could someone point me to find the right js code to accomplish this conversion?
You should be using Cesium's built-in functions for this. See Cartesian3.fromDegrees and Cartesian3.fromDegreesArray.
For example:
var result = Cesium.Cartesian3.fromDegrees(latitude, longitude, elevation);
Note the result will be as Cesium expects: in meters, not kilometers. This also takes into account the shape of the Ellipsoid, for which the default is WGS84 (the Earth is not a perfect sphere, as your function presumes).
There is nothing wrong with the Yavascript per se. However, your equations are incorrect. You're looking to convert from Lat/Long/Alt to Spherical (aka Cartesian), which was answered here.
So you could rewrite above as:
function polarToCartesian(latitude, longitude, elevation){
const x = math.cos(latitude) * math.cos(longitude) * elevation;
const y = math.cos(latitude) * math.sin(longitude) * elevation;
const z = math.sin(latitude) * elevation;
return [x, y, z];
}
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);
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.