/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
/* vim: set et ts=4 sw=4: */
/*
* Copyright (c) 2014 Damián Nohales
*
* GNOME Maps is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* GNOME Maps is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with GNOME Maps; if not, see .
*
* Author: Damián Nohales
*/
/**
* The Levenshtein distance is a string comparison algorithm defined
* as the minimal number of characters you have to replace, insert or
* delete to transform string a into string b.
* We use it to compare the similarities of two place names.
*
* http://en.wikipedia.org/wiki/Levenshtein_distance
*/
function _getLevenshteinDistance(a, b) {
let i;
let j;
let d = [];
for (i = 0; i <= a.length; i++) {
d[i] = [];
for (j = 0; j <= b.length; j++)
d[i][j] = 0;
}
for (i = 1; i <= a.length; i++)
d[i][0] = i;
for (j = 1; j <= b.length; j++)
d[0][j] = j;
for (j = 1; j <= b.length; j++)
for (i = 1; i <= a.length; i++)
if (a[i] === b[j])
d[i][j] = d[i-1][j-1];
else
d[i][j] = Math.min(d[i-1][j] + 1,
d[i][j-1] + 1,
d[i-1][j-1] + 1);
return d[a.length][b.length];
}
function _normalize(name) {
return name.toLowerCase().trim().replace(/ +(?= )/g,'');
}
function _getPlacesLevenshteinDistance(geoPlace, socialPlace) {
let a = geoPlace.name;
let b = socialPlace.name;
return _getLevenshteinDistance(_normalize(a), _normalize(b));
}
/**
* Returns: 0 for bad match, 1 for good match and 2
* exact match.
*/
function _getKindOfMatch(geoPlace, socialPlace) {
let distance = geoPlace.location.get_distance_from(socialPlace.location);
let levenshtein = _getPlacesLevenshteinDistance(geoPlace, socialPlace);
if (distance < 0.01 && levenshtein <= 2)
return 2;
else if (distance < 0.03 && levenshtein <= 5)
return 1;
else
return 0;
}
function match(geoPlace, socialPlaces) {
let result = {
exactMatches: [],
goodMatches: [],
badMatches: []
};
socialPlaces.sort(function(a, b) {
let da = geoPlace.location.get_distance_from(a.location);
let db = geoPlace.location.get_distance_from(b.location);
if (da > db)
return 1;
else if (da < db)
return -1;
return 0;
});
socialPlaces.forEach(function(place) {
let match = _getKindOfMatch(geoPlace, place);
switch (match)
{
case 0:
result.badMatches.push(place);
break;
case 1:
result.goodMatches.push(place);
break;
case 2:
result.exactMatches.push(place);
break;
}
});
return result;
}