Javascript Selection returns wrong offset - javascript

Problem: Selection#anchorOffset, Selection#baseOffset & Selection#focusOffset from time to time returns wrong values.
Expected behavior: Make double click on the second word in code snippet (feature). You should see alert 'anchor: feature'.
Question: How to get a correct value?
JSFiddle Demo: http://jsfiddle.net/v2mmabzm/5/
Demo:
$(document).ready(function()
{
var p = $('p.text-container')
p.css({ cursor: 'pointer' });
p.dblclick(function(e) {
var html = $(e.target).html();
var range = window.getSelection() || document.getSelection() || document.selection.createRange();
var selectedWord = $.trim(range.toString());
debugger;
var anchorOffset = range.anchorOffset;
var baseOffset = range.baseOffset;
var focusOffset = range.focusOffset;
alert('anchor: ' + html.substring(anchorOffset, anchorOffset + selectedWord.length));
alert('base: ' + html.substring(baseOffset, baseOffset + selectedWord.length));
alert('focus: ' + html.substring(focusOffset, focusOffset + selectedWord.length));
});
});
span.touched {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<p class="text-container">
The future of manned space exploration and development of space depends critically on the creation of a dramatically more proficient propulsion architecture for in-space transportation. A very persuasive reason for investigating the applicability of nuclear power in rockets is the vast energy density gain of nuclear fuel when compared to chemical combustion energy. Current nuclear fusion efforts have focused on the generation of electric grid power and are wholly inappropriate for space transportation as the application of a reactor based fusion-electric system creates a colossal mass and heat rejection problem for space application. The Fusion Driven rocket (FDR) represents a revolutionary approach to fusion propulsion where the power source releases its energy directly into the propellant, not requiring conversion to electricity. It employs a solid lithium propellant that requires no significant tankage mass. The propellant is rapidly heated and accelerated to high exhaust velocity (> 30 km/s), while having no significant physical interaction with the spacecraft thereby avoiding damage to the rocket and limiting both the thermal heat load and radiator mass. In addition, it is believed that the FDR can be realized with little extrapolation from currently existing technology, at high specific power (~ 1 kW/kg), at a reasonable mass scale (<100 mt), and therefore cost. If realized, it would not only enable manned interplanetary space travel, it would allow it to become common place. The key to achieving all this stems from research at MSNW on the magnetically driven implosion of metal foils onto a magnetized plasma target to obtain fusion conditions. A logical extension of this work leads to a method that utilizes these metal shells (or liners) to not only achieve fusion conditions, but to serve as the propellant as well. Several low-mass, magnetically-driven metal liners are inductively driven to converge radially and axially and form a thick blanket surrounding the target plasmoid and compress the plasmoid to fusion conditions. Virtually all of the radiant, neutron and particle energy from the plasma is absorbed by the encapsulating, metal blanket thereby isolating the spacecraft from the fusion process and eliminating the need for large radiator mass. This energy, in addition to the intense Ohmic heating at peak magnetic field compression, is adequate to vaporize and ionize the metal blanket. The expansion of this hot, ionized metal propellant through a magnetically insulated nozzle produces high thrust at the optimal Isp. The energy from the fusion process, is thus utilized at very high efficiency. Expanding on the results from the phase I effort, phase II will focus on achieving three key criteria for the Fusion Driven Rocket to move forward for technological development:
</p>
</body>

Just change
var html = p.text();

Related

Javascript: Find double quotes within double quotes, whether at the start, middle or end

Struggling with this one...
My test data is:
const rawTextString = `"""In 1981, DuPont"" prepared charts and tables recording the C-8 concentrations over a multi-year period at selected ""receptors"" or locations."`
const rawTextString2 = `"In an internal document reporting on various studies, DuPont stated, ""We conclude that C-8 has weak tumorigenic activity in male rats based on a slight increase in benign testicular tumors. At the recommended 0.01 mg/m3, skin AEL, we conclude that there is no significant health hazard. Based on the four negative developmental studies, C-8 is not considered a developmental hazard. No information is available on the reproductive hazard."""`
const rawTextString3 = `"In November 1982, DuPont's Medical Director noted that DuPont did not have adequate ""knowledge of the chronic health effects from long-term exposure to low levels of"" PFOA, that PFOA ""is retained in the blood for a long time,"" that there ""is obviously great potential for current or future exposure of members of the local community from emissions from the Plant,"" and recommended that all ""available practical steps be taken to reduce this exposure."" In addition to being aware that PFOA is bioaccumulative, DuPont was aware by 1984 at the latest that PFOA is biopersistent."`
const doubleQuoteList = rawTextString.match(/"[^"]*"[^"]*""/gmi);
console.log(doubleQuoteList);
/*
Expected output:
""In 1981, DuPont""
""receptors""
""We conclude that C-8 has weak tumorigenic activity in male rats based on a slight increase in benign testicular tumors. At the recommended 0.01 mg/m3, skin AEL, we conclude that there is no significant health hazard. Based on the four negative developmental studies, C-8 is not considered a developmental hazard. No information is available on the reproductive hazard."""
""knowledge of the chronic health effects from long-term exposure to low levels of""
""is retained in the blood for a long time,""
""available practical steps be taken to reduce this exposure.""
*/
But the results are still wrong. Some match as expected but others don't.
Try with this regex /""([^\""]+)""/
const rawTextString = `"""In 1981, DuPont"" prepared charts and tables recording the C-8 concentrations over a multi-year period at selected ""receptors"" or locations."`
const rawTextString2 = `"In an internal document reporting on various studies, DuPont stated, ""We conclude that C-8 has weak tumorigenic activity in male rats based on a slight increase in benign testicular tumors. At the recommended 0.01 mg/m3, skin AEL, we conclude that there is no significant health hazard. Based on the four negative developmental studies, C-8 is not considered a developmental hazard. No information is available on the reproductive hazard."""`
const rawTextString3 = `"In November 1982, DuPont's Medical Director noted that DuPont did not have adequate ""knowledge of the chronic health effects from long-term exposure to low levels of"" PFOA, that PFOA ""is retained in the blood for a long time,"" that there ""is obviously great potential for current or future exposure of members of the local community from emissions from the Plant,"" and recommended that all ""available practical steps be taken to reduce this exposure."" In addition to being aware that PFOA is bioaccumulative, DuPont was aware by 1984 at the latest that PFOA is biopersistent."`
//const doubleQuoteList = rawTextString.match(/"[^"]*"[^"]*""/gmi);
const doubleQuoteList = rawTextString3.match(/""([^\""]+)""/);
console.log(doubleQuoteList);
/*
Expected output:
""In 1981, DuPont""
""receptors""
""We conclude that C-8 has weak tumorigenic activity in male rats based on a slight increase in benign testicular tumors. At the recommended 0.01 mg/m3, skin AEL, we conclude that there is no significant health hazard. Based on the four negative developmental studies, C-8 is not considered a developmental hazard. No information is available on the reproductive hazard."""
""knowledge of the chronic health effects from long-term exposure to low levels of""
""is retained in the blood for a long time,""
""available practical steps be taken to reduce this exposure.""
*/

Write the text with typing effect using javascript/Jquery

I will receive some content from server side.What I trying is to make the typing effect at the time of display this content.
$("#dislay").click(function() {
//this is the dummy content i will recieve from server
var contentFromServer = "Smile spoke total few great had never their too. Amongst moments do in arrived at my replied. Fat weddings servants but man believed prospect. Companions understood is as especially pianoforte connection introduced. Nay newspaper can sportsman are admitting gentleman belonging his. Is oppose no he summer lovers twenty in. Not his difficulty boisterous surrounded bed. Seems folly if in given scale. Sex contented dependent conveying advantage can use. Do play they miss give so up. Words to up style of since world. We leaf to snug on no need. Way own uncommonly travelling now acceptance bed compliment solicitude. Dissimilar admiration so terminated no in contrasted it. Advantages entreaties mr he apartments do. Limits far yet turned highly repair parish talked six. Draw fond rank form nor the day eat. In post mean shot ye. There out her child sir his lived. Design at uneasy me season of branch on praise esteem. Abilities discourse believing consisted remaining to no. Mistaken no me denoting dashwood as screened. Whence or esteem easily he on. Dissuade husbands at of no if disposal.";
var typerText = "";
var contentLength = contentFromServer.length;
var count = 0;
var typingSpeed = 100000 / contentLength;
var typer = setInterval(function() {
if (count > contentFromServer.length) { clearInterval(typer); }
typerText += contentFromServer.charAt(count);
document.getElementById("dislayArea").innerHTML = "" + typerText + "";
count++;
}, typingSpeed);
//reset the interval on click of button
$("#dislay").click(function() { clearInterval(typer); });
});
div {
border: 1px solid gray;
padding: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="dislay" type="button">Display Content</button>
<div id="dislayArea"></div>
The question is I do not know if I'm using the correct way or not. That is, not sure if it would be better to use the for loop, or use setInterval(what I am using). Or there is any better approach to do this.
Using setInterval() is definitely better than loop statement, as using loop will block your JS execution and you would not be able to do something during the same time. To avoid this you may use variable speed based on string length (as you have done) but IMO this will not give good visual experience.
I will also suggest to take a look at typed.js library. (There can be other libraries that achieve the same task, but I have experience with this library and it works great!)
Using the library provides more flexible control over the task with various options and again why to reinvent the wheel ?
Here is an example snippet of typed.js:
var typed = null;
$("#dislay").click(function() {
if(typed != null)
typed.destroy();
var contentFromServer = "Smile spoke total few great had never their too. Amongst moments do in arrived at my replied. Fat weddings servants but man believed prospect. Companions understood is as especially pianoforte connection introduced. Nay newspaper can sportsman are admitting gentleman belonging his. Is oppose no he summer lovers twenty in. Not his difficulty boisterous surrounded bed. Seems folly if in given scale. Sex contented dependent conveying advantage can use. Do play they miss give so up. Words to up style of since world. We leaf to snug on no need. Way own uncommonly travelling now acceptance bed compliment solicitude. Dissimilar admiration so terminated no in contrasted it. Advantages entreaties mr he apartments do. Limits far yet turned highly repair parish talked six. Draw fond rank form nor the day eat. In post mean shot ye. There out her child sir his lived. Design at uneasy me season of branch on praise esteem. Abilities discourse believing consisted remaining to no. Mistaken no me denoting dashwood as screened. Whence or esteem easily he on. Dissuade husbands at of no if disposal.";
var typedOptions = {
strings: [contentFromServer],
typeSpeed: 60,
showCursor: false
};
typed = new Typed("#displayArea", typedOptions);
});
div {
border: 1px solid gray;
padding: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/typed.js/2.0.8/typed.js"></script>
<button id="dislay" type="button">Display Content</button>
<div id="displayArea"></div>

Microsoft Translate Voice limit 200-400 words when request

I have a URL of Microsoft Translate Voice to get voice look like:
<audio controls autoplay>
<source src="https://www.bing.com/tspeak?&format=audio%2Fmp3&language=en&IG=D2CBB80AA6824D9A91B0A5D1074FC4A1&IID=translator.5034.2&text=I’m Often at the End of My Rope as a Mom of Two" type="audio/mpeg">
</audio>
The problem is: text="any text here". Any text here is limit about 200-400 word. I don't know the reason at here. In Bing Translate I can insert full 5000 words and click button audio to hear.
Have any method to pass this problem? Microsoft is limit in this URL?
Have any method to insert 5000 words like Microsoft Translate homepage?
If you open your developer console on Bing's website and start playing the sound, you'll see it sends a first mp3 request with only a couple of words, and when it's done reading it, it sends another one, and so on.
You could do the same:
// When the DOM (basically the HTML) is loaded
document.addEventListener('DOMContentLoaded', function(){
// Define your DOM elements
var getAudioBtn = document.getElementById('getAudioBtn'),
langSelect = document.getElementById("langSelect"),
langSource = document.getElementById("langSource"),
audioEl = document.getElementById('audioEl');
// Setup an event listener on the button
getAudioBtn.addEventListener('click', getContentTranslate);
// Setup an listener on the audio onended event
audioEl.addEventListener('ended', readChunkQueue);
var chunkQueue = [], // Queue of chunks of text to read
wordsPerChunk = 80, // Words per chunk
language = 'en'; // Default language
function getContentTranslate() {
// Store the language
language = langSelect.value;
// Empty the chunks array
chunkQueue = [];
// Split the text into words
var words = langSource.value.split(/ /g),
tmp = []; // Temporary array for creating a chunk
while(words.length) {
// If out temporary chunk is full, add it to the list
if (tmp.length === wordsPerChunk) {
chunkQueue.push(tmp.join(' '));
tmp = [];
}
tmp.push(words.shift());
}
if (tmp.length) {
chunkQueue.push(tmp.join(' '));
}
// Start reading these chunks
readChunkQueue();
}
function readChunkQueue() {
// If the list is empty, stop
if (!chunkQueue.length) {
return;
}
// Get the first chunk in the list
var chunk = chunkQueue.shift(),
url = 'https://www.bing.com/tspeak?&format=audio%2Fmp3'
+ '&language=' + encodeURIComponent(language)
+ '&IG=D2CBB80AA6824D9A91B0A5D1074FC4A1&IID=translator.5034.2'
+ '&text=' + encodeURIComponent(chunk);
// Set the URL as source for the audio element
audioEl.setAttribute('src', url);
}
});
<select id="langSelect">
<option value="en">English</option>
<option value="vi">Vietnamese</option>
</select>
<br>
<textarea id="langSource" placeholder="Enter text or webpage URL here">Obama Inaugural Address. 20th January 2009. My fellow citizens: I stand here today humbled by the task before us, grateful for the trust you have bestowed, mindful of the sacrifices borne by our ancestors. I thank President Bush for his service to our nation, as well as the generosity and cooperation he has shown throughout this transition. Forty-four Americans have now taken the presidential oath. The words have been spoken during rising tides of prosperity and the still waters of peace. Yet, every so often the oath is taken amidst gathering clouds and raging storms. At these moments, America has carried on not simply because of the skill or vision of those in high office, but because We the People have remained faithful to the ideals of our forbearers, and true to our founding documents. So it has been. So it must be with this generation of Americans. That we are in the midst of crisis is now well understood. Our nation is at war, against a far-reaching network of violence and hatred. Our economy is badly weakened, a consequence of greed and irresponsibility on the part of some, but also our collective failure to make hard choices and prepare the nation for a new age. Homes have been lost; jobs shed; businesses shuttered. Our health care is too costly; our schools fail too many; and each day brings further evidence that the ways we use energy strengthen our adversaries and threaten our planet. These are the indicators of crisis, subject to data and statistics. Less measurable but no less profound is a sapping of confidence across our land - a nagging fear that America's decline is inevitable, and that the next generation must lower its sights. Today I say to you that the challenges we face are real. They are serious and they are many. They will not be met easily or in a short span of time. But know this, America - they will be met. On this day, we gather because we have chosen hope over fear, unity of purpose over conflict and discord. On this day, we come to proclaim an end to the petty grievances and false promises, the recriminations and worn out dogmas, that for far too long have strangled our politics. We remain a young nation, but in the words of Scripture, the time has come to set aside childish things. The time has come to reaffirm our enduring spirit; to choose our better history; to carry forward that precious gift, that noble idea, passed on from generation to generation: the God-given promise that all are equal, all are free, and all deserve a chance to pursue their full measure of happiness. In reaffirming the greatness of our nation, we understand that greatness is never a given. It must be earned. Our journey has never been one of short-cuts or settling for less. It has not been the path for the faint-hearted - for those who prefer leisure over work, or seek only the pleasures of riches and fame. Rather, it has been the risk-takers, the doers, the makers of things - some celebrated but more often men and women obscure in their labor, who have carried us up the long, rugged path towards prosperity and freedom.</textarea>
<br>
<button id="getAudioBtn">GET AUDIO</button>
<br>
<audio id="audioEl" autoplay controls></audio>

Show more/less based on number of lines instead of text length

I have the show more/less done but it's based on the length of the text. What happens is, as there are characters that take more space than others, the "show more/less" words show in different places, not always at the end of the line. Sometimes I get something like this:
With the rest of the line free.
I want to check this based on the actual number of lines instead of the text length.
Here's a codePen of what I've got and below a code snippet:
$(document).ready(function() {
function splitText(text) {
var textBreak = textLimit;
var first = text.substring(0, textBreak);
var second = text.substring(textBreak);
var aux = second.substring(0, second.indexOf(" "));
var spaceIndex = second.indexOf(" ");
second = " " + second.substring(spaceIndex + 1);
first = first.substring(0, textBreak) + aux;
var bothTextes = [first, second];
return bothTextes;
}
var textLimit = 400;
var text = $("#companyDetails").html();
if (text.length > textLimit) {
var textArray = splitText(text);
$("#companyDetails").text(textArray[0]);
$("#companyDetails").append("<span onclick=\"expandText()\" class='show-more'>...<span class=\"red\">show more</span></span>");
$("#companyDetails").append("<span style=\"display: none\" class='rest-of-description'>" + textArray[1] + "</span>");
$("#companyDetails").append("<span onclick=\"collapseText()\" style=\"display: none\" class='red show-less'> show less </span>");
} else {
$("#companyDetails").text(text);
}
});
function expandText() {
$(".rest-of-description").show();
$(".show-less").show();
$(".show-more").hide();
}
function collapseText() {
$(".rest-of-description").hide();
$(".show-less").hide();
$(".show-more").show();
}
#companyDetails {
border: 1px solid red
}
.red {
color: red
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="companyDetails">
We at ACME offer the services to cater for all your stuff needs. As part of the ACME Inc. network we utilise 10000000 years experience of market leading in nice services. In 2015, ACME was acquired by the ACME Group, a division of Associated ACME Ltd. By
joining forces it gives us access to extensive new resources allowing us to succeed further in attracting the highest calibre of cartoon stuff. Being a specialist site with a full range of positions across the world, ACME provides our guys with a
choice that enables us to attract the best in the industry. Our dedicated team are here to make sure we continue to provide the best service to you every time. Whether you're looking to catch roadrunner or simply to have a foldable airplane that you
can fit into your pocket after flying, ACME is the company you are looking for. I case you didn't read, here it is again: We at ACME offer the services to cater for all your stuff needs. As part of the ACME Inc. network we utilise 10000000 years experience of market leading in nice services. In 2015, ACME was acquired by the ACME Group, a division of Associated ACME Ltd. By
joining forces it gives us access to extensive new resources allowing us to succeed further in attracting the highest calibre of cartoon stuff. Being a specialist site with a full range of positions across the world, ACME provides our guys with a
choice that enables us to attract the best in the industry. Our dedicated team are here to make sure we continue to provide the best service to you every time. Whether you're looking to catch roadrunner or simply to have a foldable airplane that you
can fit into your pocket after flying, ACME is the company you are looking for.
</div>
EDIT
So after #TomHood's suggestion, I can now have the line count. However, I still need to know where am I going to break the text. I can't get the final word of a specific line...
You can do this by restricting the max-height in css, based on the line-height:
#companyDetails {
border: 1px solid red;
line-height: 1.2em;
max-height: 4.8em; //4.8 = 4 lines
overflow: hidden;
}
then you just need to change the max-height property in javascript/jQuery
$('#showMore').click(function(){
$('#companyDetails').css('max-height', 'none');
});
http://codepen.io/anon/pen/KVGpwd
You could use the following markup:
<div class="companyDetailsWrap">
<p class="companyDetailsText">We at ACME offer the services to cater for all your stuff needs. As part of the ACME Inc. network we utilise 10000000 years experience of market leading in nice services. In 2015, ACME was acquired by the ACME Group, a division of Associated ACME Ltd. By
joining forces it gives us access to extensive new resources allowing us to succeed further in attracting the highest calibre of cartoon stuff. Being a specialist site with a full range of positions across the world, ACME provides our guys with a
choice that enables us to attract the best in the industry. Our dedicated team are here to make sure we continue to provide the best service to you every time. Whether you're looking to catch roadrunner or simply to have a foldable airplane that you
can fit into your pocket after flying, ACME is the company you are looking for. I case you didn't read, here it is again: We at ACME offer the services to cater for all your stuff needs. As part of the ACME Inc. network we utilise 10000000 years experience of market leading in nice services. In 2015, ACME was acquired by the ACME Group, a division of Associated ACME Ltd. By
joining forces it gives us access to extensive new resources allowing us to succeed further in attracting the highest calibre of cartoon stuff. Being a specialist site with a full range of positions across the world, ACME provides our guys with a
choice that enables us to attract the best in the industry. Our dedicated team are here to make sure we continue to provide the best service to you every time. Whether you're looking to catch roadrunner or simply to have a foldable airplane that you
can fit into your pocket after flying, ACME is the company you are looking for.</p>
</div>
Then use this script:
$(function () {
var initial = $('.companyDetailsText').text();
$('.companyDetailsText').text(initial);
while($('.companyDetailsText').outerHeight() > $('.companyDetailsWrap').height()) {
$('.companyDetailsText').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
$(window).resize(function() {
$('.companyDetailsText').text(initial);
while($('.companyDetailsText').outerHeight() > $('.companyDetailsWrap').height()) {
$('.companyDetailsText').text(function(index, text) {
return text.replace(/\W*\s(\S)*$/, '...');
});
}
});
});
This is basically listening to see if the height of your text exceeds the container, rather than using character count. You will just need to set your desired height in css on .companyDetailsWrap.

Query database values based on user's location

How can I perform a query on the database as per the user's location value? The application was developed with HTML5, CSS, Javascript, PHP has a database with columns as in the below table.
On the html webpage the users geo coordinates are collected and are to be compared with the values in the database to find the nearest place to the user with the places in the database.
Please let me know how to achieve this. Any examples / samples will be appreciated.
There is a question that compares the capabilities of various spatial databases, GIS: PostGIS/PostgreSQL vs. MySql vs. SQL Server?, where Postgis comes out a pretty clear winner over MySQL.
Whether you use MySQL or Postgis, you would be much better off, if you can, storing your latitude and longitude values as a geometry/geography (Point), as the functions that can be used to find things nearby, ST_Distance, ST_Distance_Sphere and the more obscure <-> operator, see Find n Nearest Neighbors for given Point using PostGIS? (for example usage) work directly on geometry/geography columns. Even more importantly, you can add a spatial index, which these functions need to work properly, which will outperform searches on separately indexed latitude and longitude columns by a large margin (this will depend on table size, but will grow as table size grows),
In Postgis, you can convert lat and lon to a geometry with:
alter table mytable add column geom (Geometry, 4326);
update mytable set geom = ST_SetSRID(ST_MakePoint(lon, lat), 4326)
create index ix_spatial_mytable_geom on mytable using gist(geom);
At this point, you will be able to very efficient queries to find points near other points, using any of the examples in the above links.
You can do similar things in MySQL, although, it does not support a spatial reference system, ie, the 4326 above, which means lat/lon, and it lacks a ST_MakePoint function, so you would need to use STGeomFromText and concatenate the lat/lon together to make a POINT. It also does everything in planar coordinates, as Claudio and others have stated, which is not an issue with Postgis.
I apologize for a long and somewhat tangential answer, but having done various migrations between databases on large amounts of data (MySQL, SQL Server and Postgres/GIS) and made lots of mistakes on the way, I hope I can set you off in the right direction (and add a bit of future proofing, if you want to start using some other spatial functionality, which Postigs has in spades).
For a rough measure I would try something like the following (only Euclidean geometry, it doesn't take into account the Earth curvature or problems like this).
First you could compute the difference between the user's coordinates and the coordinates of places in the database. Like this:
distLat = abs(userLat - placeLat)
distLong = abs(userLong - placeLong)
Then I would compute the distance between the two points using Pythagora's theorem. So:
distance = squareRoot(distLat * distLat + distLong * distLong)
You can compare the distances of all places in the database and take the minimum, which teoreticaly is the place nearest to the user's position.
If you use MySQL I think that a query like this should work:
SELECT * FROM places ORDER BY MIN(SQRT((p.latitude - userLatitude) * (p.latitude - userLatitude) + (p.longitude - userLongitude) * (p.longitude - userLongitude))) LIMIT 1
Beware that this query could be very slow depending on how many places you have, because it needs to read all the rows in the table and compute the distance for each one. Indexes have no effects.
Anyway, for this kind of problems you should better use GIS or databases with good geospatial extensions. MySQL geospatial extension is not very powerful, even in MySQL 5.6. It has a ST_DISTANCE function but still uses Euclidean geometry which is not very accurate for calculation on a spherical surface like the Earth. Anyway, if you use MySQL 5.6, I think that it should be better to use the ST_DISTANCE function, which is for sure much more optimized than doing calculations "manually" in the query.
See also this article for a deep explanation and more examples: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc
EDIT
As requested by the OP, I add more details on how to deal with negative coordinates.
Negative coordinates are generally not a big issue. I will try to explain this with some examples.
Take for example the coordinates of the Brooklyn bridge: 40.704391, -73.994675.
With the above formula, the distance of the Brooklyn bridge from the Statue of Liberty is:
sqrt((40.704391 - 40.689167)^2 + (-73.994675 - -74.044444)^2) = 0.052045399
Now consider the distance between the Statue of Liberty and the Brooklyn Bowl (coordinates 40.7179666, -73.9670125), that is:
sqrt((40.7179666 - 40.689167)^2 + (-73.9670125 - -74.044444)^2) = 0.082613886
As you can see the distance of the Brooklyn Bowl from the Statue of Liberty is bigger than the distance of the Brooklyn Bridge. It is correct since the Brooklyn Bowl is 4 miles away from the Statue of Liberty, while the Brooklyn Bridge is only 1 mile away from it.
In this example both the two points has a negative longitude. But the formula works even if one of the two has positive coordinates. For example, the distance between the Statue of Liberty and the Tour Eiffel (Paris, coordinates 48.858360, 2.294460), is:
sqrt((48.858360 - 40.689167)^2 + (2.294460 - -74.044444)^2) = 76.77476134
Then calculate also the distance between the Statue of Liberty and the Colosseum (Rome, coordinates 41.890238, 12.492242):
sqrt((41.890238 - 40.689167)^2 + (12.492242 - -74.044444)^2) = 86.54502063
As you can see it works, the distance of the Colosseum is bigger since it is about 8000km away from the Statue of Liberty, while the Tour Eiffel is about 800 km closer.
The only issue I see is when you have to calculate the distance between two places that are in the far east and in the far west respectively. The above formula will give you a very high distance, but actually the they could be very close. Take for example Anchorage, a city in Alaska (coordinates 61.252240, -149.896769), and Beringovskij, a city in the very east of Russia (coordinates 63.049797, 179.310011). They have a distance of only about 1500 km but with the above formula you get:
sqrt((61.252240 - 63.049797)^2 + (-149.896769 - 179.310011)^2) = 329.2116875
Definitely a too high value for only 1500 km: I would expect something less than 50.
The problem is that the formula calculates the distance taking the central meridian as a reference point, that is the meridian with a 0 degrees latitude. This is good until the distance is "no more that half the Earth".
I think that a solution could be to calculate two distances.
The first with a reference point of 0 degrees: it is what the above formula does.
The second with a reference point of 180 degrees. It's like calculating the distance on a world map shifted by 180 degrees, like this: http://www.bouwman.com/world/Formilab-180.html.
And then take the minimum of these two distances.
Thus the formula becomes a little more complex:
distance = squareRoot(min((userLat - placeLat)^2, (userLat - placeLat - 360)^2) + (userLong - placeLong)^2)
Note that we subtract 360 because it is the distance between the degree -180 and the degree 180.
With this new formula we get correct results for places that are more then 180 degrees away from each other, and we get also the same result given by the previous formula when comparing places that are less then 180 degrees away from each other. The calculate distance Anchorage - Beringovskij is now: 30.84564166.
Of course, as I have already said, this is not an exact method for calculating distances. You can take a look at this article for more "scientific" techniques: https://en.wikipedia.org/wiki/Geographical_distance :D

Categories