Geo-MedianCenter-XS
view release on metacpan or search on metacpan
I32 ix;
Newxz(data, (len + 1)*6, double);
if (data == NULL) {
return NULL;
}
for (ix = 0; ix <= len; ++ix) {
SV** value = av_fetch(list, ix, 0);
SV* rv;
AV* entry;
I32 entry_len;
I32 ex;
I32 dx = ix * 6;
if (!value || !*value || !SvROK(*value))
continue;
rv = SvRV(*value);
if (!rv || !(SvTYPE(rv) == SVt_PVAV))
continue;
entry = (AV*)rv;
entry_len = av_len(entry);
/* Must have two or three entries */
if (entry_len < 1 || entry_len > 2) {
warn("bad item in points list");
continue;
}
/* default for weight */
data[ dx + 2 ] = 1.0;
for (ex = 0; ex <= entry_len && ex < 3; ++ex) {
SV** item = av_fetch(entry, ex, 0);
if (!item || !*item) {
continue;
}
data[ dx + ex ] = SvNV(*item);
}
data[ dx + 0 ] *= (PI / 180.0); /* lat in rad */
data[ dx + 1 ] *= (PI / 180.0); /* lon in rad */
/* weight */
data[ dx + 3 ] = cos(data[ dx ]) * cos(data[ dx + 1 ]); /* x */
data[ dx + 4 ] = cos(data[ dx ]) * sin(data[ dx + 1 ]); /* y */
data[ dx + 5 ] = sin(data[ dx ]); /* z */
}
return data;
}
int
median_center(HV* opts, double* plat, double* plon) {
U32 max_iterations = 4;
double tolerance = 0.0f;
SV** value;
double* data = NULL;
double clat = 0;
double clon = 0;
I32 count = 0;
if (opts == NULL || !( SvTYPE(opts) == SVt_PVHV )) {
return -1;
}
value = hv_fetch(opts, "points", 6, 0);
if (value && *value && SvROK(*value)) {
SV* rv = SvRV(*value);
count = av_len((AV*)rv);
if (rv && (SvTYPE(rv) == SVt_PVAV) && count >= 0) {
data = data_from_av((AV*)rv);
}
}
value = hv_fetch(opts, "tolerance", 9, 0);
if (value && *value && SvOK(*value)) {
tolerance = SvNV(*value);
}
value = hv_fetch(opts, "max_iterations", 14, 0);
if (value && *value && SvOK(*value)) {
max_iterations = SvUV(*value);
}
while (data) {
double sumkx, sumky, sumkz, sumk, avgx, avgy, avgz, nlat, nlon, ndst;
size_t last = (count + 1) * 6;
size_t ix;
sumkx = sumky = sumkz = sumk = 0;
for (ix = 0; ix < last;) {
double lat = data[ix++];
double lon = data[ix++];
double wgt = data[ix++];
double dst = haversine_distance_rad(clat, clon, lat, lon);
double wgd;
if (dst == 0) {
dst = DBL_EPSILON;
}
wgd = wgt / dst;
sumkx += wgd * data[ix++];
sumky += wgd * data[ix++];
sumkz += wgd * data[ix++];
sumk += wgd;
}
avgx = sumkx / sumk;
avgy = sumky / sumk;
avgz = sumkz / sumk;
/* Should this rather use asin(avgz)? */
nlat = atan2(avgz, sqrt(avgx * avgx + avgy * avgy));
nlon = atan2(avgy, avgx);
ndst = haversine_distance_rad(clat, clon, nlat, nlon);
if (!(max_iterations-- > 0) || (ndst <= tolerance)) {
*plat = nlat * (180.0 / PI);
*plon = nlon * (180.0 / PI);
Safefree(data);
return 0;
}
clat = nlat;
clon = nlon;
}
/* If we get here, there was an error retrieving the points */
return -1;
}
MODULE = Geo::MedianCenter::XS PACKAGE = Geo::MedianCenter::XS
PROTOTYPES: DISABLE
double
haversine_distance_rad(double lat1, double lon1, double lat2, double lon2)
double
haversine_distance_dec(double lat1, double lon1, double lat2, double lon2)
void
median_center(opts)
HV* opts;
PREINIT:
double lat = 0.0f;
double lon = 0.0f;
PPCODE:
if (median_center(opts, &lat, &lon))
XSRETURN_EMPTY;
mXPUSHs(newSVnv( lat ));
mXPUSHs(newSVnv( lon ));
( run in 2.162 seconds using v1.01-cache-2.11-cpan-364913b4093 )