You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
spOSMroute/mapsys/map_way.c

346 lines
9.6 KiB

/***************************************************************************
* map_way.c
*
* Copyright 2009 - Steffen Pohle
* steffen@gulpe.de
****************************************************************************/
/*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "map.h"
#include "memoryleak.h"
#include "system.h"
#include "vector.h"
/*****************************************************
*
* Way handling
*
*/
/*
* add the way without checking for it's size, but do an update
*/
int map_way_add_to_hash (struct map_hash *mh, struct map_way *mw) {
int size;
struct map_way *w;
/* check if the hash pos and the waypos are valid */
if (mw->p_cnt > 0) if (map_geo2igeo (mw->p[0].lon) != mh->pos.ilon ||
map_geo2igeo (mw->p[0].lat) != mh->pos.ilat) {
d_printf ("p[0]:%d,%d mhpos:%d,%d", __FUNCTION__, map_geo2igeo (mw->p[0].lon), map_geo2igeo (mw->p[0].lat), mh->pos.ilon, mh->pos.ilat);
errorexit (-1);
}
/* check if the first element is already the one we want to update */
if (mh->ways && mh->ways->id == mw->id && mh->ways->subid == mw->subid) {
mh->ways = mh->ways->next;
mh->wayscnt--;
}
/* find possible update and go to the end */
if (mh->ways) {
/* find last way */
w = mh->ways;
while (w && w->next) {
if (w->next->id == mw->id && w->next->subid == mw->subid) {
/* found old way.. just delete .. */
w->next = w->next->next;
mh->wayscnt--;
}
if (w->next) w = w->next;
}
w->next = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
w = w->next;
}
else {
/* complete new ways */
mh->ways = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
w = (struct map_way*) map_hash_getfree_aligned (mh, POINTERALIGNMENT);
}
/* copy the way data to the free entry
* calc size and set free entry to it's new place */
size = map_way_getsize (mw);
map_way_copy (w, mw);
w->next = NULL;
mh->free = mh->free + size;
mh->wayscnt++;
SETFLAG(mh->status, MS_changed);
return 1;
};
/******************************************************************************
* Add/Update way information
* - load/ get hash table
* - calculate size in bytes for this entry
* - check if pointer in free is enought for this new entry
* if not: move old memory block out of the way and put a new data block inside.
* - add new way
*/
int map_way_add (struct map_way *way, int loadflags) {
struct map_hash *mh = NULL;
int size;
if (way->p == NULL || way->p_cnt == 0) return 0;
size = map_way_getsize (way);
mh = map_hash_get (way->p[0].lon, way->p[0].lat, loadflags);
if (mh == NULL) {
mh = map_hash_alloc (size + MAP_HASH_DEFAULTSIZE);
mh->pos.ilon = map_geo2igeo (way->p[0].lon);
mh->pos.ilat = map_geo2igeo (way->p[0].lat);
map_hash_add (mh);
}
else if (size >= map_hash_getfree_size (mh)) {
mh = map_hash_realloc (mh, mh->datasize + size + MAP_HASH_DEFAULTSIZE);
}
return map_way_add_to_hash (mh, way);
};
/*
* will copy all the data from one way to another, this function will not
* check if there are conflicts with the memory at all. Also the next field
* will be keeped untouched.
*/
void map_way_copy (struct map_way *dest, struct map_way *src) {
int i;
strncpy (dest->name, src->name, MAP_W_NAMELEN);
strncpy (dest->ref, src->ref, MAP_W_NAMELEN);
dest->id = src->id;
dest->subid = src->subid;
dest->type = src->type;
dest->flags = src->flags;
dest->p_cnt = src->p_cnt;
dest->n_cnt = src->n_cnt;
dest->p = (struct map_pos*) (dest->data);
dest->n = (struct map_waynode*) (dest->data+(sizeof (struct map_pos)*dest->p_cnt));
for (i = 0; (i < dest->p_cnt || i < dest->n_cnt); i++) {
if (i < dest->p_cnt) dest->p[i] = src->p[i];
if (i < dest->n_cnt) dest->n[i] = src->n[i];
}
};
struct map_way *map_way_find (struct map_hash *mh, unsigned long long int id, unsigned short int subid) {
struct map_way *mw;
if (mh == NULL) return NULL;
mw = mh->ways;
while (mw) {
if (mw->id == id && subid == mw->subid) return mw;
mw = mw->next;
}
d_printf ("%s: could not find way %lld:%d ipos:%d,%d", __FUNCTION__, id, subid, mh->pos.ilon, mh->pos.ilat);
return NULL;
};
int map_way_getsize (struct map_way *way) {
return (sizeof (struct map_way) + way->n_cnt * sizeof (struct map_waynode) + way->p_cnt * sizeof (struct map_pos));
};
/************************************************************
* find closest way to the givin position
* idh: distance to check (igeo distance)
* if name is not NULL and namelen above zero the name of
* the road will be copied into name
*/
int map_way_findclosest (struct map_pos *pos, struct map_pos *waypos, int idh, char *name,
int namelen, unsigned long long int *closest_id, unsigned short int *closest_sid) {
struct map_hash *mh;
struct map_hashpos ipos;
struct map_way *mw;
float dist_closest = 40000.0, f;
ipos.ilon = map_geo2igeo (pos->lon);
ipos.ilat = map_geo2igeo (pos->lat);
mh = map_hash_geti (ipos.ilon, ipos.ilat, MHLOAD_RELOAD);
if (mh == NULL) return 0;
mw = mh->ways;
while (mw) {
f = map_way_getdistance (mw, pos, waypos);
if (dist_closest > f) {
dist_closest = f;
if (name != NULL && namelen > 0) {
if (mw->name != NULL && mw->name[0] != 0)
strncpy (name, mw->name, namelen);
else if (mw->ref != NULL && mw->ref[0] != 0)
strncpy (name, mw->ref, namelen);
}
if (closest_id != NULL) *closest_id = mw->id;
if (closest_sid != NULL) *closest_sid = mw->subid;
}
mw = mw->next;
}
return 1;
};
/*
* return the closest distance to the way. If waypos is set also return the
* point of the way.
*/
float map_way_getdistance (struct map_way *mw, struct map_pos *pos, struct map_pos *waypos) {
int i, j;
float f, dist_closest;
fPoint v1, v2, v3, v4;
fPoint p1, p2, p3, p4;
if (!mw || mw->p_cnt == 0) return (-1.0);
/* check first point.. */
p3.x = map_lon2km (0.0, pos->lat);
p3.y = map_lat2km (0.0);
p2.x = map_lon2km (pos->lon - mw->p[0].lon, mw->p[0].lat);
p2.y = map_lat2km (pos->lat - mw->p[0].lat);
v2 = vec_sub (p3, p2);
dist_closest = sqrt (v2.x * v2.x + v2.y * v2.y);
if (waypos) {
waypos->lat = mw->p[0].lat;
waypos->lon = mw->p[0].lon;
}
/* check the other nodes, also the way between */
for (i = 1; i < (mw->p_cnt + mw->n_cnt); i++) {
/* mw->p... must be converted to km first. */
if (i >= mw->p_cnt) {
/* check nodes */
j = i - mw->p_cnt;
p1.x = map_lon2km (pos->lon - mw->p[mw->n[j].pnr].lon, mw->p[mw->n[j].pnr].lat);
p1.y = map_lat2km (pos->lat - mw->p[mw->n[j].pnr].lat);
p2.x = map_lon2km (pos->lon - mw->n[j].d.lon, mw->n[j].d.lat);
p2.y = map_lat2km (pos->lat - mw->n[j].d.lat);
}
else {
/* check waysegments */
p2.x = map_lon2km (pos->lon - mw->p[i].lon, mw->p[i].lat);
p2.y = map_lat2km (pos->lat - mw->p[i].lat);
p1.x = map_lon2km (pos->lon - mw->p[i-1].lon, mw->p[i-1].lat);
p1.y = map_lat2km (pos->lat - mw->p[i-1].lat);
}
v1 = vec_sub (p1, p2);
v2 = vec_sub (p3, p2);
v3 = vec_normale (v1, v2);
/* need to check the range f the new vec.. */
if (((v3.x <= 0.0 && v1.x <= 0.0) || (v3.x >= 0.0 && v1.x >= 0.0)
|| (v3.y <= 0.0 && v1.y <= 0.0) || (v3.y >= 0.0 && v1.y >= 0.0))
&& fabs(v3.x) <= fabs(v1.x) && fabs(v3.y) <= fabs(v1.y)) {
v4 = vec_sub (v3, v2);
f = sqrt (v4.x * v4.x + v4.y * v4.y);
}
/* if we are out or range just check the distance */
else {
f = sqrt (v2.x * v2.x + v2.y * v2.y);
v3 = v2;
}
if (f < dist_closest) {
dist_closest = f;
if (waypos) {
p4 = vec_add (v3, p2);
waypos->lat = pos->lat - map_km2lat (p4.y);
waypos->lon = pos->lon - map_km2lon (p4.x, waypos->lat);
}
}
}
return dist_closest;
};
/***************************************************************************
* distance calculation
*/
float map_way_getlenght (struct map_way *mw, int start, int end) {
float ret = 0.0;
int i;
if (start == -1 || end == -1) {
start = 0;
end = mw->p_cnt;
}
if (start > end) {
i = end;
end = start;
start = i;
}
for (i = start + 1; (i < mw->p_cnt && i <= end); i++) {
ret += map_getdistance (mw->p[i-1], mw->p[i]);
}
return ret;
};
/***************************************************************************
* Check References if some are identical (seperation with ';')
*/
int map_way_checkref (char *ref1, char *ref2) {
char ref1_cpy[MAP_W_REFS*MAP_W_NAMELEN];
char ref2_cpy[MAP_W_REFS*MAP_W_NAMELEN];
int res = 0, i, j;
char *next = NULL;
char *cur = NULL;
if (ref1 == NULL || ref2 == NULL) return 0;
if (ref1[0] == 0 || ref2[0] == 0) return 0;
/* make a copy of ref1 and ref2 but filter out all unneeded spaces */
for (i = 0, j = 0; i < strlen (ref1); i++)
if (ref1[i] != ' ') ref1_cpy[j++] = ref1[i];
ref1_cpy[j] = 0;
for (i = 0, j = 0; i < strlen (ref2); i++)
if (ref2[i] != ' ') ref2_cpy[j++] = ref2[i];
ref2_cpy[j] = 0;
cur = ref1_cpy;
do {
next = strchr (cur, ';');
if (next != NULL) {
next[0] = 0;
next++;
}
if (strstr (ref2_cpy, cur) != NULL) res++;
}
while ((cur = next) != NULL);
return res;
};