summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIan McIntosh <ian_mcintosh@linuxadvocate.org>2005-10-05 01:09:35 +0000
committerIan McIntosh <ian_mcintosh@linuxadvocate.org>2005-10-05 01:09:35 +0000
commit5f76cb6b989a4c93bfc2b8cbb9b41a3c66dcf13b (patch)
tree1c3761acd2dc77a8fb858e490a5747377b722e8d /src
parent10eb4a1a8c2f6251f758391be0db9018f8535511 (diff)
Add level of detail concept.
Add zoomtool. Calculate bounding box on load instead of draw. Update to tile drawing. Begin splitting this file up more. Add lots more zoom levels. Remove empty module init. Don't need to init scenemanager module.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/db.c88
-rw-r--r--src/db.h9
-rw-r--r--src/import_tiger.c168
-rw-r--r--src/main.c4
-rw-r--r--src/main.h2
-rw-r--r--src/mainwindow.c202
-rw-r--r--src/map.c1138
-rw-r--r--src/map.h77
-rw-r--r--src/map_draw_cairo.c54
-rw-r--r--src/map_draw_cairo.h2
-rw-r--r--src/map_draw_gdk.c110
-rw-r--r--src/map_draw_gdk.h3
-rw-r--r--src/map_style.c15
-rw-r--r--src/map_tile.c4
-rw-r--r--src/map_tile.h1
-rw-r--r--src/map_tilemanager.c404
-rw-r--r--src/map_tilemanager.h45
-rw-r--r--src/road.c4
-rw-r--r--src/scenemanager.c17
-rw-r--r--src/scenemanager.h1
-rw-r--r--src/search_road.c28
22 files changed, 1165 insertions, 1219 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 75e7953..aa9c0a9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,11 +25,13 @@ roadster_SOURCES = \
mainwindow.c\
gotowindow.c\
map.c\
- map_tile.c\
- map_history.c\
- map_style.c\
map_draw_cairo.c\
map_draw_gdk.c\
+ map_history.c\
+ map_hittest.c\
+ map_math.c\
+ map_style.c\
+ map_tilemanager.c\
import.c\
import_tiger.c\
importwindow.c\
diff --git a/src/db.c b/src/db.c
index 034392b..70448f6 100644
--- a/src/db.c
+++ b/src/db.c
@@ -256,11 +256,6 @@ static guint db_count_table_rows(const gchar* pszTable)
return uRows;
}
-gboolean db_is_empty()
-{
- return (db_count_table_rows(DB_ROADS_TABLENAME) == 0);
-}
-
/******************************************************
** data inserting
******************************************************/
@@ -285,9 +280,9 @@ static gboolean db_insert(const gchar* pszSQL, gint* pnReturnRowsInserted)
return FALSE;
}
-gboolean db_insert_road(gint nRoadNameID, gint nLayerType, gint nAddressLeftStart, gint nAddressLeftEnd, gint nAddressRightStart, gint nAddressRightEnd, gint nCityLeftID, gint nCityRightID, const gchar* pszZIPCodeLeft, const gchar* pszZIPCodeRight, GPtrArray* pPointsArray, gint* pReturnID)
+gboolean db_insert_road(gint nLOD, gint nRoadNameID, gint nLayerType, gint nAddressLeftStart, gint nAddressLeftEnd, gint nAddressRightStart, gint nAddressRightEnd, gint nCityLeftID, gint nCityRightID, const gchar* pszZIPCodeLeft, const gchar* pszZIPCodeRight, GPtrArray* pPointsArray, gint* pReturnID)
{
- g_assert(pReturnID != NULL);
+// g_assert(pReturnID != NULL);
if(!db_is_connected()) return FALSE;
if(pPointsArray->len == 0) return TRUE; // skip 0-length
@@ -307,23 +302,32 @@ gboolean db_insert_road(gint nRoadNameID, gint nLayerType, gint nAddressLeftStar
nCount++;
}
- gchar azQuery[MAX_SQLBUFFER_LEN];
- g_snprintf(azQuery, MAX_SQLBUFFER_LEN,
- "INSERT INTO %s SET RoadNameID=%d, TypeID=%d, Coordinates=GeometryFromText('LINESTRING(%s)')"
- ", AddressLeftStart=%d, AddressLeftEnd=%d, AddressRightStart=%d, AddressRightEnd=%d"
- ", CityLeftID=%d, CityRightID=%d"
- ", ZIPCodeLeft='%s', ZIPCodeRight='%s'",
- DB_ROADS_TABLENAME, nRoadNameID, nLayerType, azCoordinateList,
- nAddressLeftStart, nAddressLeftEnd, nAddressRightStart, nAddressRightEnd,
- nCityLeftID, nCityRightID,
- pszZIPCodeLeft, pszZIPCodeRight);
-
- if(MYSQL_RESULT_SUCCESS != mysql_query(g_pDB->pMySQLConnection, azQuery)) {
- g_warning("db_insert_road failed: %s (SQL: %s)\n", mysql_error(g_pDB->pMySQLConnection), azQuery);
- return FALSE;
+ gchar* pszQuery;
+
+ if(nLOD == 0) {
+ pszQuery = g_strdup_printf(
+ "INSERT INTO %s%d SET RoadNameID=%d, TypeID=%d, Coordinates=GeometryFromText('LINESTRING(%s)')"
+ ", AddressLeftStart=%d, AddressLeftEnd=%d, AddressRightStart=%d, AddressRightEnd=%d"
+ ", CityLeftID=%d, CityRightID=%d"
+ ", ZIPCodeLeft='%s', ZIPCodeRight='%s'",
+ DB_ROADS_TABLENAME, nLOD, nRoadNameID, nLayerType, azCoordinateList,
+ nAddressLeftStart, nAddressLeftEnd, nAddressRightStart, nAddressRightEnd,
+ nCityLeftID, nCityRightID,
+ pszZIPCodeLeft, pszZIPCodeRight);
+ }
+ else {
+ pszQuery = g_strdup_printf(
+ "INSERT INTO %s%d SET RoadNameID=%d, TypeID=%d, Coordinates=GeometryFromText('LINESTRING(%s)')",
+ DB_ROADS_TABLENAME, nLOD, nRoadNameID, nLayerType, azCoordinateList);
}
+
+ mysql_query(g_pDB->pMySQLConnection, pszQuery);
+ g_free(pszQuery);
+
// return the new ID
- *pReturnID = mysql_insert_id(g_pDB->pMySQLConnection);
+ if(pReturnID != NULL) {
+ *pReturnID = mysql_insert_id(g_pDB->pMySQLConnection);
+ }
return TRUE;
}
@@ -571,32 +575,43 @@ void db_create_tables()
// db_query("ALTER TABLE RoadName ADD INDEX (NameSoundex);", NULL);
// Road
- db_query("CREATE TABLE IF NOT EXISTS Road("
- " ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT," // XXX: can we get away with INT3 ?
+ db_query(
+ "CREATE TABLE IF NOT EXISTS Road0("
+// " ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT," // XXX: can we get away with INT3 ?
" TypeID INT1 UNSIGNED NOT NULL,"
-
" RoadNameID INT3 UNSIGNED NOT NULL," // NOTE: 3 bytes
-
" AddressLeftStart INT2 UNSIGNED NOT NULL,"
" AddressLeftEnd INT2 UNSIGNED NOT NULL,"
" AddressRightStart INT2 UNSIGNED NOT NULL,"
" AddressRightEnd INT2 UNSIGNED NOT NULL,"
-
" CityLeftID INT3 UNSIGNED NOT NULL," // NOTE: 3 bytes
" CityRightID INT3 UNSIGNED NOT NULL," // NOTE: 3 bytes
-
" ZIPCodeLeft CHAR(6) NOT NULL,"
" ZIPCodeRight CHAR(6) NOT NULL,"
-
" Coordinates point NOT NULL,"
// lots of indexes:
- " PRIMARY KEY (ID)," // XXX: we'll probably want to keep a unique ID, but we don't use this for anything yet.
+// " PRIMARY KEY (ID)," // XXX: we'll probably want to keep a unique ID, but we don't use this for anything yet.
" INDEX(RoadNameID)," // to get roads when we've matched a RoadName
" SPATIAL KEY (Coordinates));", NULL);
+ db_query(
+ "CREATE TABLE IF NOT EXISTS Road1("
+ " TypeID INT1 UNSIGNED NOT NULL,"
+ " RoadNameID INT3 UNSIGNED NOT NULL," // NOTE: 3 bytes
+ " Coordinates point NOT NULL,"
+ " SPATIAL KEY (Coordinates));", NULL);
+
+ db_query(
+ "CREATE TABLE IF NOT EXISTS Road2("
+ " TypeID INT1 UNSIGNED NOT NULL,"
+ " RoadNameID INT3 UNSIGNED NOT NULL," // NOTE: 3 bytes
+ " Coordinates point NOT NULL,"
+ " SPATIAL KEY (Coordinates));", NULL);
+
// RoadName
- db_query("CREATE TABLE IF NOT EXISTS RoadName("
+ db_query(
+ "CREATE TABLE IF NOT EXISTS RoadName("
" ID INT3 UNSIGNED NOT NULL auto_increment," // NOTE: 3 bytes
" Name VARCHAR(30) NOT NULL,"
" NameSoundex CHAR(10) NOT NULL," // see soundex() function
@@ -607,8 +622,8 @@ void db_create_tables()
,NULL);
// City
- db_query("CREATE TABLE IF NOT EXISTS City("
- // a unique ID for the value
+ db_query(
+ "CREATE TABLE IF NOT EXISTS City("
" ID INT3 UNSIGNED NOT NULL AUTO_INCREMENT," // NOTE: 3 bytes
" StateID INT2 UNSIGNED NOT NULL," // NOTE: 2 bytes
" Name CHAR(60) NOT NULL," // are city names ever 60 chars anyway?? TIGER think so
@@ -618,8 +633,8 @@ void db_create_tables()
,NULL);
// State
- db_query("CREATE TABLE IF NOT EXISTS State("
- // a unique ID for the value
+ db_query(
+ "CREATE TABLE IF NOT EXISTS State("
" ID INT2 UNSIGNED NOT NULL AUTO_INCREMENT," // NOTE: 2 bytes (enough to go global..?)
" Name CHAR(40) NOT NULL,"
" Code CHAR(3) NOT NULL," // eg. "MA"
@@ -629,7 +644,8 @@ void db_create_tables()
,NULL);
// Location
- db_query("CREATE TABLE IF NOT EXISTS Location("
+ db_query(
+ "CREATE TABLE IF NOT EXISTS Location("
" ID INT4 UNSIGNED NOT NULL AUTO_INCREMENT,"
" LocationSetID INT3 NOT NULL," // NOTE: 3 bytes
" Coordinates point NOT NULL,"
diff --git a/src/db.h b/src/db.h
index acdcae7..d34ea61 100644
--- a/src/db.h
+++ b/src/db.h
@@ -52,8 +52,6 @@ gboolean db_connect(const gchar* pzHost, const gchar* pzUserName, const gchar* p
const gchar* db_get_connection_info(void);
// utility
-gboolean db_is_empty(void);
-
gboolean db_insert_roadname(const gchar* pszName, gint nSuffixID, gint* pnReturnID);
//~ gboolean db_create_points_db(const gchar* name);
@@ -86,12 +84,7 @@ void db_enable_keys(void);
void db_disable_keys(void);
gboolean db_insert_city(const gchar* pszName, gint nStateID, gint* pnReturnCityID);
-gboolean db_insert_road(gint nRoadNameID, gint nLayerType,
- gint nAddressLeftStart, gint nAddressLeftEnd,
- gint nAddressRightStart, gint nAddressRightEnd,
- gint nCityLeftID, gint nCityRightID,
- const gchar* pszZIPCodeLeft, const gchar* pszZIPCodeRight,
- GPtrArray* pPointsArray, gint* pReturnID);
+gboolean db_insert_road(gint nLOD, gint nRoadNameID, gint nLayerType, gint nAddressLeftStart, gint nAddressLeftEnd, gint nAddressRightStart, gint nAddressRightEnd, gint nCityLeftID, gint nCityRightID, const gchar* pszZIPCodeLeft, const gchar* pszZIPCodeRight, GPtrArray* pPointsArray, gint* pReturnID);
gboolean db_city_get_id(const gchar* pszName, gint nStateID, gint* pnReturnID);
gboolean db_state_get_id(const gchar* pszName, gint* pnReturnID);
diff --git a/src/import_tiger.c b/src/import_tiger.c
index b8c823b..71887b3 100644
--- a/src/import_tiger.c
+++ b/src/import_tiger.c
@@ -155,6 +155,96 @@ typedef struct tiger_record_rtc
gint nCityID; // a database ID, stored here after it is inserted
} tiger_record_rtc_t;
+// #define MAP_OBJECT_TYPE_NONE (0)
+// #define MAP_OBJECT_TYPE_MINORROAD (1)
+// #define MAP_OBJECT_TYPE_MAJORROAD (2)
+// #define MAP_OBJECT_TYPE_MINORHIGHWAY (3)
+// #define MAP_OBJECT_TYPE_MINORHIGHWAY_RAMP (4)
+// #define MAP_OBJECT_TYPE_MAJORHIGHWAY (5) // Unused
+// #define MAP_OBJECT_TYPE_MAJORHIGHWAY_RAMP (6) // Unused
+// #define MAP_OBJECT_TYPE_RAILROAD (7)
+// #define MAP_OBJECT_TYPE_PARK (8)
+// #define MAP_OBJECT_TYPE_RIVER (9)
+// #define MAP_OBJECT_TYPE_LAKE (10)
+// #define MAP_OBJECT_TYPE_MISC_AREA (11)
+// #define MAP_OBJECT_TYPE_URBAN_AREA (12)
+
+gdouble g_afPolygonMinSizeAtLODs[MAP_NUM_LEVELS_OF_DETAIL] = {0, 0.001, 0.01}; // in world degrees
+
+gint g_aaObjectTypeDetailAtLODs[MAP_NUM_OBJECT_TYPES][MAP_NUM_LEVELS_OF_DETAIL] = {
+ {0,0,0,0},
+ {1,0,0,0}, // minor rd
+ {1,6,0,0}, // major rd
+ {1,6,12,0}, // hw
+ {1,3,0,0}, // hw ramp
+ {0,0,0,0}, // (unused)
+ {0,0,0,0}, // (unused)
+ {1,8,0,0}, // rail
+ {1,1,1,1}, // park
+ {2,16,0,0}, // river
+ {1,1,1,1}, // lake
+ {1,1,1,1}, // misc area
+ {1,1,1,1}, // urban area
+};
+
+gboolean object_type_exists_at_lod(gint nRecordType, gint nLOD)
+{
+ if(nRecordType < 0) {
+ g_warning("nRecordType = %d\n", nRecordType);
+ }
+ g_assert(nRecordType >= 0);
+ g_assert(nRecordType < MAP_NUM_OBJECT_TYPES);
+ g_assert(nLOD >= 0);
+ g_assert(nLOD <= 3);
+ return (g_aaObjectTypeDetailAtLODs[nRecordType][nLOD] > 0);
+}
+
+gint object_type_detail_at_lod(gint nRecordType, gint nLOD)
+{
+ g_assert(nRecordType >= 0);
+ g_assert(nRecordType < MAP_NUM_OBJECT_TYPES);
+ g_assert(nLOD >= 0);
+ g_assert(nLOD <= 3);
+ return (g_aaObjectTypeDetailAtLODs[nRecordType][nLOD]);
+}
+
+void reduce_object_detail_for_lod(gint nRecordType, gint nLOD, GPtrArray* pSourceArray, GPtrArray* pDestArray)
+{
+ g_assert(pSourceArray);
+ g_assert(pDestArray);
+
+ if(!object_type_exists_at_lod(nRecordType, nLOD)) return;
+
+ gint nDetail = object_type_detail_at_lod(nRecordType, nLOD);
+ g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, 0));
+
+ // our super-hacky algorithm just steps N points at a time
+ gint i;
+ for(i = nDetail ; i < (pSourceArray->len-1) ; i+=nDetail) {
+ g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, i));
+ }
+ g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, pSourceArray->len-1));
+}
+
+void util_bounding_box_of_points_array(GPtrArray* pPointsArray, maprect_t* pReturnRect)
+{
+ pReturnRect->A.fLatitude = MAX_LATITUDE; // init to worst possible values
+ pReturnRect->A.fLongitude = MAX_LONGITUDE;
+
+ pReturnRect->B.fLatitude = MIN_LATITUDE;
+ pReturnRect->B.fLongitude = MIN_LONGITUDE;
+
+ gint i;
+ for(i=0 ; i<pPointsArray->len ; i++) {
+ mappoint_t* pPoint = g_ptr_array_index(pPointsArray, i);
+
+ pReturnRect->A.fLatitude = min(pReturnRect->A.fLatitude, pPoint->fLatitude);
+ pReturnRect->A.fLongitude = min(pReturnRect->A.fLongitude, pPoint->fLongitude);
+
+ pReturnRect->B.fLatitude = max(pReturnRect->B.fLatitude, pPoint->fLatitude);
+ pReturnRect->B.fLongitude = max(pReturnRect->B.fLongitude, pPoint->fLongitude);
+ }
+}
static gboolean import_tiger_read_lat(gint8* pBuffer, gdouble* pValue)
{
@@ -254,6 +344,8 @@ static gboolean import_tiger_read_string(char* pBuffer, gint nLen, char* pValue)
return TRUE;
}
+// NOTE: This function can return MAP_OBJECT_TYPE_NONE. Lines of this type shouldn't be saved, but they
+// might be used for polygons, so we have to keep them in memory.
static gboolean import_tiger_read_layer_type(gint8* pBuffer, gint* pValue)
{
//g_print("%c%c%c\n", *(pBuffer), *(pBuffer+1), *(pBuffer+2));
@@ -282,7 +374,6 @@ static gboolean import_tiger_read_layer_type(gint8* pBuffer, gint* pValue)
}
else if(chCode == '5') { // dirt roads
//*pValue = MAP_OBJECT_TYPE_TRAIL;
- return FALSE;
}
else if(chCode == '6') {
if(chSubCode == '1') {
@@ -294,7 +385,6 @@ static gboolean import_tiger_read_layer_type(gint8* pBuffer, gint* pValue)
}
else if(chSubCode == '5') {
//*pValue = MAP_OBJECT_TYPE_FERRY_ROUTE; // where a boat carrying cars goes
- return FALSE;
}
else if(chSubCode == '7') {
g_print("found code A67: toll booth!\n");
@@ -587,8 +677,8 @@ static gboolean import_tiger_parse_table_7(gint8* pBuffer, gint nLength, GHashTa
// 22-24 is a CFCC (
gint nRecordType;
-
import_tiger_read_layer_type(&pLine[22-1], &nRecordType);
+
pRecord = g_new0(tiger_record_rt7_t, 1);
pRecord->nRecordType = nRecordType;
@@ -832,16 +922,26 @@ static void callback_save_rt1_chains(gpointer key, gpointer value, gpointer user
db_insert_roadname(pRecordRT1->achName, pRecordRT1->nRoadNameSuffixID, &nRoadNameID);
}
- gint nRoadID;
- db_insert_road(nRoadNameID,
- pRecordRT1->nRecordType,
- pRecordRT1->nAddressLeftStart,
- pRecordRT1->nAddressLeftEnd,
- pRecordRT1->nAddressRightStart,
- pRecordRT1->nAddressRightEnd,
- nCityLeftID, nCityRightID,
- azZIPCodeLeft, azZIPCodeRight,
- pTempPointsArray, &nRoadID);
+ gint nLOD = MAP_LEVEL_OF_DETAIL_BEST;
+ db_insert_road(nLOD, nRoadNameID, pRecordRT1->nRecordType,
+ pRecordRT1->nAddressLeftStart, pRecordRT1->nAddressLeftEnd,
+ pRecordRT1->nAddressRightStart, pRecordRT1->nAddressRightEnd,
+ nCityLeftID, nCityRightID,
+ azZIPCodeLeft, azZIPCodeRight,
+ pTempPointsArray, NULL);
+
+ for(nLOD = MAP_LEVEL_OF_DETAIL_BEST+1 ; nLOD <= MAP_LEVEL_OF_DETAIL_WORST ; nLOD++) {
+ GPtrArray* pReducedPointsArray = g_ptr_array_new();
+
+ reduce_object_detail_for_lod(pRecordRT1->nRecordType, nLOD, pTempPointsArray, pReducedPointsArray);
+ if(pReducedPointsArray->len > 0) {
+ g_assert(pReducedPointsArray->len >= 2);
+
+ db_insert_road(nLOD, nRoadNameID, pRecordRT1->nRecordType, 0, 0, 0, 0, 0, 0, NULL, NULL,
+ pReducedPointsArray, NULL);
+ }
+ g_ptr_array_free(pReducedPointsArray, TRUE);
+ }
}
g_ptr_array_free(pTempPointsArray, TRUE);
}
@@ -1012,12 +1112,6 @@ static void callback_save_rti_polygons(gpointer key, gpointer value, gpointer us
g_print("Found a polygon that doesn't loop %s\n", pRecordRT7->achName);
}
- // XXX: looking up a city for a polygon? unimplemented.
- gint nCityLeftID = 0;
- gchar* pszZIPCodeLeft = "";
- gint nCityRightID = 0;
- gchar* pszZIPCodeRight = "";
-
// insert record
if(pRecordRT7->nRecordType != MAP_OBJECT_TYPE_NONE) {
gint nRoadNameID = 0;
@@ -1026,14 +1120,34 @@ static void callback_save_rti_polygons(gpointer key, gpointer value, gpointer us
db_insert_roadname(pRecordRT7->achName, 0, &nRoadNameID);
}
- gint nRoadID;
- db_insert_road(
- nRoadNameID,
- pRecordRT7->nRecordType,
- 0,0,0,0,
- nCityLeftID, nCityRightID,
- pszZIPCodeLeft, pszZIPCodeRight,
- pTempPointsArray, &nRoadID);
+ // Write LOD 0
+ gint nLOD = MAP_LEVEL_OF_DETAIL_BEST;
+ db_insert_road(nLOD, nRoadNameID, pRecordRT7->nRecordType, 0, 0, 0, 0, 0, 0, "", "",
+ pTempPointsArray, NULL);
+
+ // Write higher LODs
+ maprect_t rc;
+ util_bounding_box_of_points_array(pTempPointsArray, &rc);
+ gdouble fWidth = rc.B.fLongitude - rc.A.fLongitude;
+ gdouble fHeight = rc.B.fLatitude - rc.A.fLatitude;
+
+ for(nLOD = MAP_LEVEL_OF_DETAIL_BEST+1 ; nLOD <= MAP_LEVEL_OF_DETAIL_WORST ; nLOD++) {
+ if((fWidth < g_afPolygonMinSizeAtLODs[nLOD]) || (fHeight < g_afPolygonMinSizeAtLODs[nLOD])) {
+ g_print("object exluded at LOD %d\n", nLOD);
+ break; // not visible (nor at higher LODs, so break instead of continue)
+ }
+
+ GPtrArray* pReducedPointsArray = g_ptr_array_new();
+
+ reduce_object_detail_for_lod(pRecordRT7->nRecordType, nLOD, pTempPointsArray, pReducedPointsArray);
+ if(pReducedPointsArray->len > 0) {
+ g_assert(pReducedPointsArray->len >= 2);
+
+ db_insert_road(nLOD, nRoadNameID, pRecordRT7->nRecordType, 0, 0, 0, 0, 0, 0, NULL, NULL,
+ pReducedPointsArray, NULL);
+ }
+ g_ptr_array_free(pReducedPointsArray, TRUE);
+ }
}
}
diff --git a/src/main.c b/src/main.c
index f887d01..3321847 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,7 +31,6 @@
#include "db.h"
#include "map.h"
#include "gpsclient.h"
-#include "scenemanager.h"
#include "locationset.h"
#include "location.h"
#include "search.h"
@@ -142,9 +141,6 @@ gboolean main_init(void)
g_print("initializing map\n");
map_init();
- g_print("initializing scenemanager\n");
- scenemanager_init();
-
g_print("initializing gpsclient\n");
gpsclient_init();
diff --git a/src/main.h b/src/main.h
index 0e96270..15c19f8 100644
--- a/src/main.h
+++ b/src/main.h
@@ -24,6 +24,8 @@
#ifndef _MAIN_H_
#define _MAIN_H_
+//#define G_DISABLE_ASSERT
+
#include <gtk/gtk.h>
#define USE_GNOME_VFS // comment this out to get a faster single-threaded compile (can't import, though)
diff --git a/src/mainwindow.c b/src/mainwindow.c
index 8a75000..51e1fb8 100644
--- a/src/mainwindow.c
+++ b/src/mainwindow.c
@@ -27,7 +27,7 @@
#include <gtk/gtk.h>
#include <gtk/gtksignal.h>
-//#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
//#include <gdk/gdkx.h>
//#include <cairo.h>
//#include <cairo-xlib.h>
@@ -39,6 +39,7 @@
#include "gotowindow.h"
#include "db.h"
#include "map.h"
+#include "map_hittest.h"
#include "map_style.h"
#include "importwindow.h"
#include "locationset.h"
@@ -89,6 +90,8 @@
#define LOCATIONSETLIST_COLUMN_COUNT (3)
#define LOCATIONSETLIST_COLUMN_PIXBUF (4)
+#define ZOOM_TOOL_THRESHOLD (5) // in pixels. a box less than AxA will be ignored
+
// Limits
#define MAX_SEARCH_TEXT_LENGTH (100)
#define SPEED_LABEL_FORMAT ("<span font_desc='32'>%.0f</span>")
@@ -107,20 +110,20 @@
#define MAX_DISTANCE_FOR_AUTO_SLIDE_IN_PIXELS (3500.0) // when selecting search results, we slide to them instead of jumping if they are within this distance
// Types
-typedef struct {
- GdkCursorType CursorType;
- GdkCursor* pGdkCursor;
-} cursor_t;
+// typedef struct {
+// GdkCursorType CursorType;
+// GdkCursor* pGdkCursor;
+// } cursor_t;
-typedef struct {
- char* szName;
- cursor_t Cursor;
-} toolsettings_t;
+// typedef struct {
+// char* szName;
+// cursor_t Cursor;
+// } toolsettings_t;
-typedef enum {
- kToolPointer = 0,
- kToolZoom = 1,
-} EToolType;
+// typedef enum {
+// MOUSE_TOOL_POINTER = 0,
+// MOUSE_TOOL_ZOOM = 1,
+// } EMouseToolType;
// Prototypes
static void mainwindow_setup_selected_tool(void);
@@ -169,8 +172,8 @@ struct {
// Toolbar
GtkHBox* pToolbar;
-// GtkToolButton* pPointerToolButton;
-// GtkToolButton* pZoomToolButton;
+ GtkRadioButton* pPointerToolRadioButton;
+ GtkRadioButton* pZoomToolRadioButton;
GtkHScale* pZoomScale;
GtkEntry* pSearchBox;
GtkImage* pStatusbarGPSIcon;
@@ -214,7 +217,7 @@ struct {
tooltip_t* pTooltip;
map_t* pMap;
- EToolType eSelectedTool;
+// EToolType eSelectedTool;
gboolean bScrolling;
EDirection eScrollDirection;
@@ -236,6 +239,10 @@ struct {
mappoint_t ptSlideEndLocation;
animator_t* pAnimator;
+ // Zoom Tool
+ gboolean bDrawingZoomRect;
+ screenrect_t rcZoomRect;
+
// History (forward / back)
maphistory_t* pMapHistory;
GtkButton* pForwardButton;
@@ -252,17 +259,17 @@ struct {
// XXX: Use GDK_HAND1 for the map
// Data
-toolsettings_t g_Tools[] = {
- {"Pointer Tool", {GDK_LEFT_PTR, NULL}},
- {"Zoom Tool", {GDK_CIRCLE, NULL}},
-};
-void cursor_init()
-{
- int i;
- for(i=0 ; i<G_N_ELEMENTS(g_Tools) ; i++) {
- g_Tools[i].Cursor.pGdkCursor = gdk_cursor_new(g_Tools[i].Cursor.CursorType);
- }
-}
+// toolsettings_t g_Tools[] = {
+// {"Pointer Tool", {GDK_LEFT_PTR, NULL}},
+// {"Zoom Tool", {GDK_CIRCLE, NULL}},
+// };
+// void cursor_init()
+// {
+// int i;
+// for(i=0 ; i<G_N_ELEMENTS(g_Tools) ; i++) {
+// g_Tools[i].Cursor.pGdkCursor = gdk_cursor_new(g_Tools[i].Cursor.CursorType);
+// }
+// }
static void util_set_image_to_stock(GtkImage* pImage, gchar* pszStockIconID, GtkIconSize nSize)
{
@@ -294,6 +301,11 @@ void* mainwindow_set_busy(void)
return pCursor;
}
+void mainwindow_draw_xor_rect(screenrect_t* pRect)
+{
+ map_draw_gdk_xor_rect(g_MainWindow.pMap, GTK_WIDGET(g_MainWindow.pDrawingArea)->window, pRect);
+}
+
void mainwindow_set_not_busy(void** ppCursor)
{
gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.pWindow)->window, NULL);
@@ -365,6 +377,10 @@ void mainwindow_init(GladeXML* pGladeXML)
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pSidebarNotebook, GTK_NOTEBOOK, "sidebarnotebook");
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pContentBox, GTK_VBOX, "mainwindowcontentsbox");
+// g_object_set(G_OBJECT(g_MainWindow.pSidebarNotebook), "show-border", FALSE, NULL);
+ g_object_set(G_OBJECT(g_MainWindow.pSidebarNotebook), "tab-border", 1, NULL);
+// g_object_set(G_OBJECT(g_MainWindow.pSidebarNotebook), "tab-hborder", 0, NULL);
+
// View menu
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pViewSidebarMenuItem, GTK_CHECK_MENU_ITEM, "viewsidebarmenuitem");
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pViewFullscreenMenuItem, GTK_CHECK_MENU_ITEM, "viewfullscreenmenuitem");
@@ -384,8 +400,8 @@ void mainwindow_init(GladeXML* pGladeXML)
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pMapPopupMenu, GTK_MENU, "mappopupmenu");
// Tools
- //GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pPointerToolButton, GTK_TOOL_BUTTON, "pointertoolbutton");
- //GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pZoomToolButton, GTK_TOOL_BUTTON, "zoomtoolbutton");
+ GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pPointerToolRadioButton, GTK_RADIO_BUTTON, "pointertoolradiobutton");
+ GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.pZoomToolRadioButton, GTK_RADIO_BUTTON, "zoomtoolradiobutton");
// GPS Widgets
GLADE_LINK_WIDGET(pGladeXML, g_MainWindow.GPS.pShowPositionCheckButton, GTK_CHECK_BUTTON, "gpsshowpositioncheckbutton");
@@ -446,8 +462,7 @@ void mainwindow_init(GladeXML* pGladeXML)
map_new(&g_MainWindow.pMap, GTK_WIDGET(g_MainWindow.pDrawingArea));
map_style_load(g_MainWindow.pMap, MAP_STYLE_FILENAME);
-
- cursor_init();
+// cursor_init();
mainwindow_configure_locationset_list();
mainwindow_refresh_locationset_list();
@@ -737,6 +752,13 @@ void mainwindow_update_zoom_buttons()
gtk_widget_set_sensitive(GTK_WIDGET(g_MainWindow.pZoomInMenuItem), map_can_zoom_in(g_MainWindow.pMap));
gtk_widget_set_sensitive(GTK_WIDGET(g_MainWindow.pZoomOutButton), map_can_zoom_out(g_MainWindow.pMap));
gtk_widget_set_sensitive(GTK_WIDGET(g_MainWindow.pZoomOutMenuItem), map_can_zoom_out(g_MainWindow.pMap));
+
+ // set zoomlevel scale but prevent it from calling handler (mainwindow_on_zoomscale_value_changed)
+ g_signal_handlers_block_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
+ gtk_range_set_value(GTK_RANGE(g_MainWindow.pZoomScale), map_get_zoomlevel(g_MainWindow.pMap));
+ g_signal_handlers_unblock_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
+
+ mainwindow_statusbar_update_zoomscale();
}
void mainwindow_set_zoomlevel(gint nZoomLevel)
@@ -744,11 +766,10 @@ void mainwindow_set_zoomlevel(gint nZoomLevel)
map_set_zoomlevel(g_MainWindow.pMap, nZoomLevel);
// set zoomlevel scale but prevent it from calling handler (mainwindow_on_zoomscale_value_changed)
- g_signal_handlers_block_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
- gtk_range_set_value(GTK_RANGE(g_MainWindow.pZoomScale), nZoomLevel);
- g_signal_handlers_unblock_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
+// g_signal_handlers_block_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
+// gtk_range_set_value(GTK_RANGE(g_MainWindow.pZoomScale), nZoomLevel);
+// g_signal_handlers_unblock_by_func(g_MainWindow.pZoomScale, mainwindow_on_zoomscale_value_changed, NULL);
- mainwindow_statusbar_update_zoomscale();
mainwindow_update_zoom_buttons();
}
@@ -771,11 +792,11 @@ void mainwindow_on_zoomscale_value_changed(GtkRange *range, gpointer user_data)
//
//
//
-static void gui_set_tool(EToolType eTool)
-{
- g_MainWindow.eSelectedTool = eTool;
- gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.pDrawingArea)->window, g_Tools[eTool].Cursor.pGdkCursor);
-}
+// static void gui_set_tool(EToolType eTool)
+// {
+// g_MainWindow.eSelectedTool = eTool;
+// gdk_window_set_cursor(GTK_WIDGET(g_MainWindow.pDrawingArea)->window, g_Tools[eTool].Cursor.pGdkCursor);
+// }
//
// Callbacks for About box
@@ -859,11 +880,18 @@ void mainwindow_on_sidebarmenuitem_activate(GtkMenuItem *menuitem, gpointer user
mainwindow_set_sidebox_visible(!mainwindow_get_sidebox_visible());
}
+#define ZOOM_MAJOR_TICK_SIZE (4)
+
// Zoom buttons / menu items (shared callbacks)
void mainwindow_on_zoomin_activate(GtkMenuItem *menuitem, gpointer user_data)
{
+ gint nNewZoomLevel = map_get_zoomlevel(g_MainWindow.pMap) - 1; // XXX: make zoomlevel 0-based and the -1 will go away
+ nNewZoomLevel -= (nNewZoomLevel % ZOOM_MAJOR_TICK_SIZE);
+ nNewZoomLevel += ZOOM_MAJOR_TICK_SIZE;
+ nNewZoomLevel += 1; // XXX: make zoomlevel 0-based and the +1 will go away
+
// tell the map
- map_set_zoomlevel(g_MainWindow.pMap, map_get_zoomlevel(g_MainWindow.pMap) + 1);
+ map_set_zoomlevel(g_MainWindow.pMap, nNewZoomLevel);
// update the gui
mainwindow_set_zoomlevel(map_get_zoomlevel(g_MainWindow.pMap));
@@ -874,7 +902,12 @@ void mainwindow_on_zoomin_activate(GtkMenuItem *menuitem, gpointer user_data)
void mainwindow_on_zoomout_activate(GtkMenuItem *menuitem, gpointer user_data)
{
- map_set_zoomlevel(g_MainWindow.pMap, map_get_zoomlevel(g_MainWindow.pMap) - 1);
+ gint nNewZoomLevel = map_get_zoomlevel(g_MainWindow.pMap) - 1;
+ if((nNewZoomLevel % ZOOM_MAJOR_TICK_SIZE) == 0) nNewZoomLevel -= ZOOM_MAJOR_TICK_SIZE;
+ else nNewZoomLevel -= (nNewZoomLevel % ZOOM_MAJOR_TICK_SIZE);
+ nNewZoomLevel += 1; // XXX: make zoomlevel 0-based and the +1 will go away
+
+ map_set_zoomlevel(g_MainWindow.pMap, nNewZoomLevel);
mainwindow_set_zoomlevel(map_get_zoomlevel(g_MainWindow.pMap));
mainwindow_draw_map(DRAWFLAG_GEOMETRY);
mainwindow_set_draw_pretty_timeout(DRAW_PRETTY_ZOOM_TIMEOUT_MS);
@@ -974,6 +1007,11 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
gint nWidth = GTK_WIDGET(g_MainWindow.pDrawingArea)->allocation.width;
gint nHeight = GTK_WIDGET(g_MainWindow.pDrawingArea)->allocation.height;
+
+ // nX and nY clipped to screen
+ gint nClippedX = (nX < 0) ? 0 : (nX > nWidth) ? nWidth : nX;
+ gint nClippedY = (nY < 0) ? 0 : (nY > nHeight) ? nHeight : nY;
+
EDirection eScrollDirection = DIRECTION_NONE;
// get mouse position on screen
@@ -982,7 +1020,7 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
map_windowpoint_to_mappoint(g_MainWindow.pMap, &screenpoint, &mappoint);
maphit_t* pHitStruct = NULL;
- map_hit_test(g_MainWindow.pMap, &mappoint, &pHitStruct);
+ map_hittest(g_MainWindow.pMap, &mappoint, &pHitStruct);
// hitstruct free'd far below
if(event->button == MOUSE_BUTTON_LEFT) {
@@ -1004,6 +1042,14 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
mainwindow_set_scroll_timeout();
}
+ else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_MainWindow.pZoomToolRadioButton))) {
+ //g_print("begin rect draw\n");
+ g_MainWindow.bDrawingZoomRect = TRUE;
+
+ // set both rect points to click point
+ g_MainWindow.rcZoomRect.A.nX = g_MainWindow.rcZoomRect.B.nX = nX;
+ g_MainWindow.rcZoomRect.A.nY = g_MainWindow.rcZoomRect.B.nY = nY;
+ }
else {
g_MainWindow.bMouseDragging = TRUE;
g_MainWindow.bMouseDragMovement = FALSE;
@@ -1048,6 +1094,27 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
}
}
+ if(g_MainWindow.bDrawingZoomRect == TRUE) {
+ if((map_screenrect_width(&(g_MainWindow.rcZoomRect)) > ZOOM_TOOL_THRESHOLD) && (map_screenrect_height(&(g_MainWindow.rcZoomRect)) > ZOOM_TOOL_THRESHOLD)) {
+ map_zoom_to_screenrect(g_MainWindow.pMap, &(g_MainWindow.rcZoomRect));
+
+ // update GUI
+ mainwindow_update_zoom_buttons();
+ mainwindow_statusbar_update_position();
+
+ mainwindow_draw_map(DRAWFLAG_GEOMETRY);
+ mainwindow_set_draw_pretty_timeout(DRAW_PRETTY_ZOOM_TIMEOUT_MS);
+ mainwindow_add_history();
+ }
+ else {
+ // Since we're not redrawing the map, we need to erase the selection rectangle
+ mainwindow_draw_xor_rect(&(g_MainWindow.rcZoomRect));
+ }
+
+ // all done
+ g_MainWindow.bDrawingZoomRect = FALSE;
+ }
+
// end scrolling, if active
if(g_MainWindow.bScrolling == TRUE) {
// NOTE: don't restore cursor (mouse could *still* be over screen edge)
@@ -1086,7 +1153,7 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
}
}
else if(event->type == GDK_2BUTTON_PRESS) {
- // can only double click in the middle (not on a scroll border)
+ // can only double-click in the middle (not on a scroll border)
eScrollDirection = match_border(nX, nY, nWidth, nHeight, BORDER_SCROLL_CLICK_TARGET_SIZE);
if(eScrollDirection == DIRECTION_NONE) {
animator_destroy(g_MainWindow.pAnimator);
@@ -1108,7 +1175,6 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
if(event->type == GDK_BUTTON_PRESS) {
GtkMenu* pMenu = g_MainWindow.pMapPopupMenu; // default to generic map popup
- g_print("here %s\n", pHitStruct);
if(pHitStruct != NULL) {
if(pHitStruct->eHitType == MAP_HITTYPE_LOCATION) {
// Use POI specific popup menu
@@ -1134,7 +1200,7 @@ static gboolean mainwindow_on_mouse_button_click(GtkWidget* w, GdkEventButton *e
//gtk_menu_popup(pMenu, NULL, NULL, NULL, NULL, event->button, event->time);
}
}
- map_hitstruct_free(g_MainWindow.pMap, pHitStruct);
+ map_hittest_maphit_free(g_MainWindow.pMap, pHitStruct);
return TRUE;
}
@@ -1154,7 +1220,17 @@ static gboolean mainwindow_on_mouse_motion(GtkWidget* w, GdkEventMotion *event)
gint nWidth = GTK_WIDGET(g_MainWindow.pDrawingArea)->allocation.width;
gint nHeight = GTK_WIDGET(g_MainWindow.pDrawingArea)->allocation.height;
- gint nCursor = GDK_LEFT_PTR;
+ // nX and nY clipped to screen
+ gint nClippedX = (nX < 0) ? 0 : ((nX > nWidth-1) ? nWidth-1 : nX);
+ gint nClippedY = (nY < 0) ? 0 : ((nY > nHeight-1) ? nHeight-1 : nY);
+
+ gint nCursor;
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_MainWindow.pZoomToolRadioButton))) {
+ nCursor = GDK_SIZING;
+ }
+ else {
+ nCursor = GDK_LEFT_PTR;
+ }
if(g_MainWindow.bMouseDragging) {
g_MainWindow.bMouseDragMovement = TRUE;
@@ -1183,6 +1259,15 @@ static gboolean mainwindow_on_mouse_motion(GtkWidget* w, GdkEventMotion *event)
// update direction if actively scrolling
g_MainWindow.eScrollDirection = eScrollDirection;
}
+ else if(g_MainWindow.bDrawingZoomRect) {
+ //g_print("updating rect\n");
+ mainwindow_draw_xor_rect(&g_MainWindow.rcZoomRect); // erase old rect (XOR operator rocks!)
+
+ g_MainWindow.rcZoomRect.B.nX = nClippedX;
+ g_MainWindow.rcZoomRect.B.nY = nClippedY;
+
+ mainwindow_draw_xor_rect(&g_MainWindow.rcZoomRect); // draw new rect
+ }
else {
// If not dragging or scrolling, user is just moving mouse around.
// Update tooltip and mouse cursor based on what we're pointing at.
@@ -1199,7 +1284,7 @@ static gboolean mainwindow_on_mouse_motion(GtkWidget* w, GdkEventMotion *event)
// try to "hit" something on the map. a road, a location, whatever!
maphit_t* pHitStruct = NULL;
- if(map_hit_test(g_MainWindow.pMap, &mappoint, &pHitStruct)) {
+ if(map_hittest(g_MainWindow.pMap, &mappoint, &pHitStruct)) {
// A hit! Move the tooltip here, format the text, and show it.
tooltip_set_upper_left_corner(g_MainWindow.pTooltip, (gint)(event->x_root) + TOOLTIP_OFFSET_X, (gint)(event->y_root) + TOOLTIP_OFFSET_Y);
@@ -1240,7 +1325,7 @@ static gboolean mainwindow_on_mouse_motion(GtkWidget* w, GdkEventMotion *event)
g_print("url: %s\n", pHitStruct->URLHit.pszURL);
}
- map_hitstruct_free(g_MainWindow.pMap, pHitStruct);
+ map_hittest_maphit_free(g_MainWindow.pMap, pHitStruct);
}
else {
// no hit. hide the tooltip
@@ -1293,14 +1378,27 @@ static gboolean mainwindow_on_mouse_scroll(GtkWidget* w, GdkEventScroll *event)
}
}
-static gboolean mainwindow_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+static gboolean mainwindow_on_key_press(GtkWidget *widget, GdkEventKey *pEvent, gpointer user_data)
{
- g_print("key_press\n");
+ //g_print("key_press\n");
+ if(pEvent->keyval == GDK_F1) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_MainWindow.pPointerToolRadioButton), TRUE);
+ }
+ else if(pEvent->keyval == GDK_F2) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_MainWindow.pZoomToolRadioButton), TRUE);
+ }
+ else if(pEvent->keyval == GDK_Escape) {
+ if(g_MainWindow.bDrawingZoomRect == TRUE) {
+ // cancel zoom-rect
+ mainwindow_draw_xor_rect(&(g_MainWindow.rcZoomRect));
+ g_MainWindow.bDrawingZoomRect = FALSE;
+ }
+ }
return FALSE;
}
static gboolean mainwindow_on_key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
- g_print("key_release\n");
+ //g_print("key_release\n");
return FALSE;
}
diff --git a/src/map.c b/src/map.c
index bd99259..23ffcb3 100644
--- a/src/map.c
+++ b/src/map.c
@@ -22,12 +22,12 @@
*/
#include <gdk/gdkx.h>
-//#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
#include "main.h"
#include "map_style.h"
+#include "map_tilemanager.h"
#include "gui.h"
#include "map.h"
#include "mainwindow.h"
@@ -59,32 +59,16 @@
// D) The current value is the result of some casual testing.
// XXX: The names below aren't very clear.
-#define TILE_SHIFT (1000.0) // the units we care about (1000ths of a degree)
-#define TILE_MODULUS (23) // how many of the above units each tile is on a side
-#define MAP_TILE_WIDTH (TILE_MODULUS / TILE_SHIFT) // width and height of a tile, in degrees
+// #define TILE_SHIFT (1000.0)
+// #define TILE_MODULUS (23)
+// #define TILE_WIDTH (TILE_MODULUS / TILE_SHIFT)
#define MIN_ROAD_HIT_TARGET_WIDTH (6) // make super thin roads a bit easier to hover over/click, in pixels
-#define MIN_ZOOMLEVEL_FOR_LOCATIONS (6)
/* Prototypes */
static void map_init_location_hash(map_t* pMap);
-// data loading
-static gboolean map_data_load_tiles(map_t* pMap, maprect_t* pRect);
-static gboolean map_data_load_geometry(map_t* pMap, maprect_t* pRect);
-static gboolean map_data_load_locations(map_t* pMap, maprect_t* pRect);
-
-
-// hit testing
-static gboolean map_hit_test_layer_roads(GPtrArray* pPointStringsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
-static gboolean map_hit_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pHitPoint, gdouble fMaxDistance, mappoint_t* pReturnClosestPoint, gdouble* pfReturnPercentAlongLine);
-static ESide map_side_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pClosestPointOnLine, mappoint_t* pHitPoint);
-static gboolean map_hit_test_locationselections(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationSelectionsArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
-
-static gboolean map_hit_test_locationsets(map_t* pMap, rendermetrics_t* pRenderMetrics, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
-static gboolean map_hit_test_locations(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationsArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct);
-
static void map_store_location(map_t* pMap, location_t* pLocation, gint nLocationSetID);
static void map_data_clear(map_t* pMap);
@@ -93,22 +77,65 @@ void map_get_render_metrics(map_t* pMap, rendermetrics_t* pMetrics);
gdouble map_get_straight_line_distance_in_degrees(mappoint_t* p1, mappoint_t* p2);
// Each zoomlevel has a scale and an optional name (name isn't used for anything)
-zoomlevel_t g_sZoomLevels[NUM_ZOOM_LEVELS+1] = {
- {1,0,0,0,0,"undefined"}, // no zoom level 0
-
-// { 1600000, ""},
-// { 800000, ""},
-// { 400000, ""},
-// { 200000, ""},
-// { 100000, ""},
-
- { 80000, UNIT_MILES, 2, UNIT_KILOMETERS, 2, "", }, // 1
- { 40000, UNIT_MILES, 1, UNIT_KILOMETERS, 1, "", }, // 2
- { 20000, UNIT_FEET, 2000, UNIT_METERS, 400, "", }, // 3
- { 10000, UNIT_FEET, 1000, UNIT_METERS, 200, "", }, // 4
- { 5000, UNIT_FEET, 500, UNIT_METERS, 100, "", }, // 5
+zoomlevel_t g_sZoomLevels[NUM_ZOOM_LEVELS] = {
+ // 1.166144850000 magic number for 2000-80000 in 25 steps. (each scale is previous * this #)
+ // 1.181891000000 magic number for 2000-1,600,000 in 41 steps (11 major with 3 minor in between)
+
+ {150000000, UNIT_MILES,2000,UNIT_KILOMETERS,2000, 1, 3}, // *1
+ {123000000, UNIT_MILES,1000,UNIT_KILOMETERS,48, 1, 3}, // 2
+ { 96000000, UNIT_MILES,500, UNIT_KILOMETERS,48, 1, 3}, // 3
+ { 69000000, UNIT_MILES,200, UNIT_KILOMETERS,48, 1, 3}, // 4
+
+ { 42000000, UNIT_MILES,100, UNIT_KILOMETERS,24, 1, 3}, // *5
+ { 35000000, UNIT_MILES,50, UNIT_KILOMETERS,24, 1, 3}, // 6
+ { 28000000, UNIT_MILES,20, UNIT_KILOMETERS,24, 1, 3}, // 7
+ { 21000000, UNIT_MILES,10, UNIT_KILOMETERS,24, 1, 3}, // 8
+
+ { 14000000, UNIT_MILES,10, UNIT_KILOMETERS,12, 2, 3}, // *9
+ { 11600000, UNIT_MILES,10, UNIT_KILOMETERS,12, 2, 3}, // 10
+ { 9200000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 2, 3}, // 11
+ { 6800000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 2, 3}, // 12
+
+ { 4400000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 3, 2}, // *13
+ { 3850000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 3, 2}, // 14
+ { 3300000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 3, 2}, // 15
+ { 2750000, UNIT_MILES, 5, UNIT_KILOMETERS, 7, 3, 2}, // 16
+
+ { 2200000, UNIT_MILES, 2, UNIT_KILOMETERS, 2, 4, 2}, // *17
+ { 1832250, UNIT_MILES, 2, UNIT_KILOMETERS, 2, 4, 2}, // 18
+ { 1464500, UNIT_MILES, 2, UNIT_KILOMETERS, 2, 4, 2}, // 19
+ { 1100000, UNIT_MILES, 2, UNIT_KILOMETERS, 2, 4, 2}, // 20
+
+ { 729000, UNIT_MILES, 2, UNIT_KILOMETERS, 2, 5, 1}, // *21
+ { 607500, UNIT_MILES, 1, UNIT_KILOMETERS, 1, 5, 1}, // 22
+ { 486000, UNIT_MILES, 1, UNIT_KILOMETERS, 1, 5, 1}, // 23
+ { 364500, UNIT_MILES, 1, UNIT_KILOMETERS, 1, 5, 1}, // 24
+
+ { 243000, UNIT_MILES, 1, UNIT_KILOMETERS, 1, 6, 1}, // *25
+ { 202500, UNIT_FEET, 3000,UNIT_METERS, 500, 6, 1}, // 26
+ { 162000, UNIT_FEET, 2000,UNIT_METERS, 500, 6, 1}, // 27
+ { 121500, UNIT_FEET, 2000,UNIT_METERS, 500, 6, 1}, // 28
+
+ { 81000, UNIT_FEET, 2000,UNIT_METERS, 300, 7, 0}, // *29
+ { 67500, UNIT_FEET, 2000,UNIT_METERS, 300, 7, 0}, // 30
+ { 54000, UNIT_FEET, 2000,UNIT_METERS, 300, 7, 0}, // 31
+ { 40500, UNIT_FEET, 1000,UNIT_METERS, 200, 7, 0}, // 32
+
+ { 27000, UNIT_FEET, 1000,UNIT_METERS, 200, 8, 0}, // *33
+ { 22500, UNIT_FEET, 1000,UNIT_METERS, 200, 8, 0}, // 34
+ { 18000, UNIT_FEET, 1000,UNIT_METERS, 200, 8, 0}, // 35
+ { 13500, UNIT_FEET, 500, UNIT_METERS, 100, 8, 0}, // 36
+
+ { 9000, UNIT_FEET, 500, UNIT_METERS, 100, 9, 0}, // *37
+ { 7500, UNIT_FEET, 500, UNIT_METERS, 100, 9, 0}, // 38
+ { 6000, UNIT_FEET, 300, UNIT_METERS, 50, 9, 0}, // 39
+ { 4500, UNIT_FEET, 300, UNIT_METERS, 50, 9, 0}, // 40
+
+ { 3000, UNIT_FEET, 300, UNIT_METERS, 50, 10, 0}, // *41
};
+#define MIN_ZOOMLEVEL_FOR_LOCATIONS (6)
+
gchar* g_apszMapObjectTypeNames[] = { // XXX: would be nice to remove this. although we *do* need to maintain a link between imported data and styles
"",
"minor-roads",
@@ -132,6 +159,7 @@ gchar* g_apszMapObjectTypeNames[] = { // XXX: would be nice to remove this. alth
// init the module
void map_init(void)
{
+ g_print("*********************************** %f\n", WORLD_FEET_PER_DEGREE);
}
gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget)
@@ -143,9 +171,9 @@ gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget)
map_t* pMap = g_new0(map_t, 1);
// Create array of Track handles
- pMap->pTracksArray = g_array_new(FALSE, /* not zero-terminated */
- TRUE, /* clear to 0 (?) */
- sizeof(gint));
+// pMap->pTracksArray = g_array_new(FALSE, /* not zero-terminated */
+// TRUE, /* clear to 0 (?) */
+// sizeof(gint));
map_init_location_hash(pMap);
@@ -154,15 +182,17 @@ gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget)
scenemanager_new(&(pMap->pSceneManager));
g_assert(pMap->pSceneManager);
- pMap->uZoomLevel = 1; // XXX: better way to init this? or should we force the GUI to reflect this #?
+ pMap->uZoomLevel = 1;
// init containers for geometry data
- gint i;
- for(i=0 ; i<G_N_ELEMENTS(pMap->apLayerData) ; i++) {
- maplayer_data_t* pLayer = g_new0(maplayer_data_t, 1);
- pLayer->pRoadsArray = g_ptr_array_new();
- pMap->apLayerData[i] = pLayer;
- }
+// gint i;
+// for(i=0 ; i<G_N_ELEMENTS(pMap->apLayerData) ; i++) {
+// maplayer_data_t* pLayer = g_new0(maplayer_data_t, 1);
+// pLayer->pRoadsArray = g_ptr_array_new();
+// pMap->apLayerData[i] = pLayer;
+// }
+
+ pMap->pTileManager = map_tilemanager_new();
// init POI selection
pMap->pLocationSelectionArray = g_ptr_array_new();
@@ -184,8 +214,8 @@ void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags)
// Load geometry
TIMER_BEGIN(loadtimer, "--- BEGIN ALL DB LOAD");
- map_data_clear(pMap);
- map_data_load_tiles(pMap, &(pRenderMetrics->rWorldBoundingBox));
+// map_data_clear(pMap);
+ GPtrArray* pTileArray = map_tilemanager_load_tiles_for_worldrect(pMap->pTileManager, &(pRenderMetrics->rWorldBoundingBox), pRenderMetrics->nLevelOfDetail);
TIMER_END(loadtimer, "--- END ALL DB LOAD");
scenemanager_clear(pMap->pSceneManager);
@@ -205,15 +235,15 @@ void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags)
if(nRenderMode == RENDERMODE_FAST) {
//
if(nDrawFlags & DRAWFLAG_GEOMETRY) {
- map_draw_gdk(pMap, pRenderMetrics, pTargetPixmap, DRAWFLAG_GEOMETRY);
+ map_draw_gdk(pMap, pTileArray, pRenderMetrics, pTargetPixmap, DRAWFLAG_GEOMETRY);
nDrawFlags &= ~DRAWFLAG_GEOMETRY;
}
// Call cairo for finishing the scene
- map_draw_cairo(pMap, pRenderMetrics, pTargetPixmap, nDrawFlags);
+ map_draw_cairo(pMap, pTileArray, pRenderMetrics, pTargetPixmap, nDrawFlags);
}
else { // nRenderMode == RENDERMODE_PRETTY
- map_draw_cairo(pMap, pRenderMetrics, pTargetPixmap, nDrawFlags);
+ map_draw_cairo(pMap, pTileArray, pRenderMetrics, pTargetPixmap, nDrawFlags);
}
#ifdef ENABLE_SCENEMANAGER_DEBUG_TEST
@@ -222,6 +252,8 @@ void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags)
#endif
gtk_widget_queue_draw(pMap->pTargetWidget);
+
+ g_ptr_array_free(pTileArray, TRUE);
}
// ========================================================
@@ -247,10 +279,10 @@ void map_set_zoomlevel(map_t* pMap, guint16 uZoomLevel)
if(uZoomLevel > MAX_ZOOM_LEVEL) uZoomLevel = MAX_ZOOM_LEVEL;
else if(uZoomLevel < MIN_ZOOM_LEVEL) uZoomLevel = MIN_ZOOM_LEVEL;
- if(uZoomLevel != pMap->uZoomLevel) {
+// if(uZoomLevel != pMap->uZoomLevel) {
pMap->uZoomLevel = uZoomLevel;
// map_set_redraw_needed(TRUE);
- }
+// }
}
guint16 map_get_zoomlevel(const map_t* pMap)
@@ -260,7 +292,7 @@ guint16 map_get_zoomlevel(const map_t* pMap)
guint32 map_get_zoomlevel_scale(const map_t* pMap)
{
- return g_sZoomLevels[pMap->uZoomLevel].uScale; // returns "5000" for 1:5000 scale
+ return g_sZoomLevels[pMap->uZoomLevel-1].uScale; // returns "5000" for 1:5000 scale
}
gboolean map_can_zoom_in(const map_t* pMap)
@@ -273,70 +305,6 @@ gboolean map_can_zoom_out(const map_t* pMap)
return (pMap->uZoomLevel > MIN_ZOOM_LEVEL);
}
-// ========================================================
-// Coordinate Conversion Functions
-// ========================================================
-
-gchar* g_aDistanceUnitNames[] = {
- "Feet",
- "Miles",
- "Meters",
- "Kilometers",
-};
-
-gdouble map_distance_in_units_to_degrees(map_t* pMap, gdouble fDistance, gint nDistanceUnit)
-{
- switch(nDistanceUnit) {
- case UNIT_FEET:
- return WORLD_FEET_TO_DEGREES(fDistance);
- case UNIT_MILES:
- return WORLD_MILES_TO_DEGREES(fDistance);
- case UNIT_METERS:
- return WORLD_METERS_TO_DEGREES(fDistance);
- case UNIT_KILOMETERS:
- return WORLD_KILOMETERS_TO_DEGREES(fDistance);
- default:
- g_warning("UNKNOWN DISTANCE UNIT (%d)\n", nDistanceUnit);
- return 0;
- }
-}
-
-// convert pixels to a span of degrees
-double map_pixels_to_degrees(map_t* pMap, gint16 nPixels, guint16 uZoomLevel)
-{
- double fMonitorPixelsPerInch = 85.333; // XXX: don't hardcode this
- double fPixelsPerMeter = fMonitorPixelsPerInch * INCHES_PER_METER;
- double fMetersOfPixels = ((float)nPixels) / fPixelsPerMeter;
-
- // If we had 3 meters of pixels (a very big monitor:) and the scale was 1000:1 then
- // we would want to show 3000 meters worth of world space
- double fMetersOfWorld = ((float)g_sZoomLevels[uZoomLevel].uScale) * fMetersOfPixels;
-
- return WORLD_METERS_TO_DEGREES(fMetersOfWorld);
-}
-
-double map_degrees_to_pixels(map_t* pMap, gdouble fDegrees, guint16 uZoomLevel)
-{
- double fMonitorPixelsPerInch = 85.333; // XXX: don't hardcode this
-
- double fResultInMeters = WORLD_DEGREES_TO_METERS(fDegrees);
- double fResultInPixels = (INCHES_PER_METER * fResultInMeters) * fMonitorPixelsPerInch;
- fResultInPixels /= (float)g_sZoomLevels[uZoomLevel].uScale;
- return fResultInPixels;
-}
-
-void map_windowpoint_to_mappoint(map_t* pMap, screenpoint_t* pScreenPoint, mappoint_t* pMapPoint)
-{
- // Calculate the # of pixels away from the center point the click was
- gint16 nPixelDeltaX = (gint)(pScreenPoint->nX) - (pMap->MapDimensions.uWidth / 2);
- gint16 nPixelDeltaY = (gint)(pScreenPoint->nY) - (pMap->MapDimensions.uHeight / 2);
-
- // Convert pixels to world coordinates
- pMapPoint->fLongitude = pMap->MapCenter.fLongitude + map_pixels_to_degrees(pMap, nPixelDeltaX, pMap->uZoomLevel);
- // reverse the X, clicking above
- pMapPoint->fLatitude = pMap->MapCenter.fLatitude - map_pixels_to_degrees(pMap, nPixelDeltaY, pMap->uZoomLevel);
-}
-
// Call this to pan around the map
void map_center_on_windowpoint(map_t* pMap, guint16 uX, guint16 uY)
{
@@ -345,9 +313,9 @@ void map_center_on_windowpoint(map_t* pMap, guint16 uX, guint16 uY)
gint16 nPixelDeltaY = uY - (pMap->MapDimensions.uHeight / 2);
// Convert pixels to world coordinates
- double fWorldDeltaX = map_pixels_to_degrees(pMap, nPixelDeltaX, pMap->uZoomLevel);
+ gdouble fWorldDeltaX = map_pixels_to_degrees(pMap, nPixelDeltaX, pMap->uZoomLevel);
// reverse the X, clicking above
- double fWorldDeltaY = -map_pixels_to_degrees(pMap, nPixelDeltaY, pMap->uZoomLevel);
+ gdouble fWorldDeltaY = -map_pixels_to_degrees(pMap, nPixelDeltaY, pMap->uZoomLevel);
// g_message("panning %d,%d pixels (%.10f,%.10f world coords)\n", nPixelDeltaX, nPixelDeltaY, fWorldDeltaX, fWorldDeltaY);
@@ -357,6 +325,41 @@ void map_center_on_windowpoint(map_t* pMap, guint16 uX, guint16 uY)
map_set_centerpoint(pMap, &pt);
}
+void map_zoom_to_screenrect(map_t* pMap, const screenrect_t* pRect)
+{
+ // recenter on rect
+ screenpoint_t ptCenter;
+ map_get_screenrect_centerpoint(pRect, &ptCenter);
+ map_center_on_windowpoint(pMap, ptCenter.nX, ptCenter.nY);
+ //g_print("box centerpoint = %d,%d\n", ptCenter.nX, ptCenter.nY);
+
+ // calculate size of rectangle in degrees
+ gdouble fBoxLongitude = map_pixels_to_degrees(pMap, map_screenrect_width(pRect), pMap->uZoomLevel);
+ gdouble fBoxLatitude = map_pixels_to_degrees(pMap, map_screenrect_height(pRect), pMap->uZoomLevel);
+ //g_print("box size pixels = %d,%d\n", map_screenrect_width(pRect), map_screenrect_height(pRect));
+ //g_print("box size = %f,%f\n", fBoxLongitude, fBoxLatitude);
+
+ // go from zoomed all the way to all the way out, looking for a rectangle
+ // that is wide & tall enough to show all of pRect
+ gboolean bFound = FALSE;
+ gint nZoomLevel;
+ for(nZoomLevel = MAX_ZOOM_LEVEL ; nZoomLevel >= MIN_ZOOM_LEVEL ; nZoomLevel--) {
+ pMap->uZoomLevel = nZoomLevel;
+
+ rendermetrics_t metrics;
+ map_get_render_metrics(pMap, &metrics);
+ //g_print("Screen at zoomlevel %d = %f,%f\n", nZoomLevel, metrics.fScreenLatitude, metrics.fScreenLongitude);
+
+ if(fBoxLatitude < metrics.fScreenLatitude && fBoxLongitude < metrics.fScreenLongitude) {
+ //g_print("Success\n");
+ break;
+ }
+ }
+ // either we hit the 'break' and left pMap->uZoomLevel with the appropriate zoomlevel, or we
+ // finished the loop and left pMap->uZoomLevel with MIN_ZOOM_LEVEL (farthest from the ground)
+ // which is the best we can do. either way we recentered.
+}
+
void map_set_centerpoint(map_t* pMap, const mappoint_t* pPoint)
{
g_assert(pPoint != NULL);
@@ -386,10 +389,7 @@ void map_set_dimensions(map_t* pMap, const dimensions_t* pDimensions)
// XXX: free old pixmap?
//g_assert(pMap->pPixmap == NULL);
- pMap->pPixmap = gdk_pixmap_new(
- pMap->pTargetWidget->window,
- pMap->MapDimensions.uWidth, pMap->MapDimensions.uHeight,
- -1);
+ pMap->pPixmap = gdk_pixmap_new(pMap->pTargetWidget->window, pMap->MapDimensions.uWidth, pMap->MapDimensions.uHeight, -1);
}
// ========================================================
@@ -401,13 +401,16 @@ void map_get_render_metrics(map_t* pMap, rendermetrics_t* pMetrics)
g_assert(pMetrics != NULL);
//
- // Set up renderMetrics array
+ // Copy a few values in
//
pMetrics->nZoomLevel = map_get_zoomlevel(pMap);
pMetrics->nWindowWidth = pMap->MapDimensions.uWidth;
pMetrics->nWindowHeight = pMap->MapDimensions.uHeight;
+ pMetrics->nLevelOfDetail = g_sZoomLevels[pMetrics->nZoomLevel-1].nLevelOfDetail;
+ //
// Calculate how many world degrees we'll be drawing
+ //
pMetrics->fScreenLatitude = map_pixels_to_degrees(pMap, pMap->MapDimensions.uHeight, pMetrics->nZoomLevel);
pMetrics->fScreenLongitude = map_pixels_to_degrees(pMap, pMap->MapDimensions.uWidth, pMetrics->nZoomLevel);
@@ -418,798 +421,50 @@ void map_get_render_metrics(map_t* pMap, rendermetrics_t* pMetrics)
pMetrics->rWorldBoundingBox.B.fLatitude = pMap->MapCenter.fLatitude + pMetrics->fScreenLatitude/2;
}
-static gboolean map_data_load_tiles(map_t* pMap, maprect_t* pRect)
-{
-// g_print("*****\n"
-// "rect is (%f,%f)(%f,%f)\n", pRect->A.fLatitude,pRect->A.fLongitude, pRect->B.fLatitude,pRect->B.fLongitude);
- gint32 nLatStart = (gint32)(pRect->A.fLatitude * TILE_SHIFT);
- // round it DOWN (south)
- if(pRect->A.fLatitude > 0) {
- nLatStart -= (nLatStart % TILE_MODULUS);
- }
- else {
- nLatStart -= (nLatStart % TILE_MODULUS);
- nLatStart -= TILE_MODULUS;
- }
-
- gint32 nLonStart = (gint32)(pRect->A.fLongitude * TILE_SHIFT);
- // round it DOWN (west)
- if(pRect->A.fLongitude > 0) {
- nLonStart -= (nLonStart % TILE_MODULUS);
- }
- else {
- nLonStart -= (nLonStart % TILE_MODULUS);
- nLonStart -= TILE_MODULUS;
- }
+// void map_add_track(map_t* pMap, gint hTrack)
+// {
+// g_array_append_val(pMap->pTracksArray, hTrack);
+// }
- gint32 nLatEnd = (gint32)(pRect->B.fLatitude * TILE_SHIFT);
- // round it UP (north)
- if(pRect->B.fLatitude > 0) {
- nLatEnd -= (nLatEnd % TILE_MODULUS);
- nLatEnd += TILE_MODULUS;
- }
- else {
- nLatEnd -= (nLatEnd % TILE_MODULUS);
- }
-
- gint32 nLonEnd = (gint32)(pRect->B.fLongitude * TILE_SHIFT);
- // round it UP (east)
- if(pRect->B.fLongitude > 0) {
- nLonEnd -= (nLonEnd % TILE_MODULUS);
- nLonEnd += TILE_MODULUS;
- }
- else {
- nLonEnd -= (nLonEnd % TILE_MODULUS);
- }
-
- // how many tiles are we loading in each direction? (nice and safe as integer math...)
- gint nLatNumTiles = (nLatEnd - nLatStart) / TILE_MODULUS;
- gint nLonNumTiles = (nLonEnd - nLonStart) / TILE_MODULUS;
-
- gdouble fLatStart = (gdouble)nLatStart / TILE_SHIFT;
- gdouble fLonStart = (gdouble)nLonStart / TILE_SHIFT;
-
- if(fLatStart > pRect->A.fLatitude) {
- g_print("fLatStart %f > pRect->A.fLatitude %f\n", fLatStart, pRect->A.fLatitude);
- g_assert(fLatStart <= pRect->A.fLatitude);
- }
- if(fLonStart > pRect->A.fLongitude) {
- g_print("fLonStart %f > pRect->A.fLongitude %f!!\n", fLonStart, pRect->A.fLongitude);
- g_assert_not_reached();
- }
-
- gint nLat,nLon;
- for(nLat = 0 ; nLat < nLatNumTiles ; nLat++) {
- for(nLon = 0 ; nLon < nLonNumTiles ; nLon++) {
-
- maprect_t rect;
- rect.A.fLatitude = fLatStart + ((gdouble)(nLat) * MAP_TILE_WIDTH);
- rect.A.fLongitude = fLonStart + ((gdouble)(nLon) * MAP_TILE_WIDTH);
- rect.B.fLatitude = fLatStart + ((gdouble)(nLat+1) * MAP_TILE_WIDTH);
- rect.B.fLongitude = fLonStart + ((gdouble)(nLon+1) * MAP_TILE_WIDTH);
-
- map_data_load_geometry(pMap, &rect);
- map_data_load_locations(pMap, &rect);
- }
- }
-}
-
-static gboolean map_enhance_linestring(GPtrArray* pSourceArray, GPtrArray* pDestArray, gboolean (*callback_alloc_point)(mappoint_t**), gdouble fMaxDistanceBetweenPoints, gdouble fMaxRandomDistance)
-{
- g_assert(pSourceArray->len >= 2);
-//g_print("pSourceArray->len = %d\n", pSourceArray->len);
-
- // add first point
- g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, 0));
-
- gint i = 0;
- for(i=0 ; i<(pSourceArray->len-1) ; i++) {
- mappoint_t* pPoint1 = g_ptr_array_index(pSourceArray, i);
- mappoint_t* pPoint2 = g_ptr_array_index(pSourceArray, i+1);
-
- gdouble fRise = (pPoint2->fLatitude - pPoint1->fLatitude);
- gdouble fRun = (pPoint2->fLongitude - pPoint1->fLongitude);
-
- gdouble fLineLength = sqrt((fRun*fRun) + (fRise*fRise));
-// g_print("fLineLength = %f\n", fLineLength);
- gint nNumMiddlePoints = (gint)(fLineLength / fMaxDistanceBetweenPoints);
-// g_print("(fLineLength / fMaxDistanceBetweenPoints) = nNumNewPoints; %f / %f = %d\n", fLineLength, fMaxDistanceBetweenPoints, nNumNewPoints);
- if(nNumMiddlePoints == 0) continue; // nothing to add
-
-// g_print("fDistanceBetweenPoints = %f\n", fDistanceBetweenPoints);
-
- gdouble fNormalizedX = fRun / fLineLength;
- gdouble fNormalizedY = fRise / fLineLength;
-// g_print("fNormalizedX = %f\n", fNormalizedX);
-// g_print("fNormalizedY = %f\n", fNormalizedY);
-
- gdouble fPerpendicularNormalizedX = fRise / fLineLength;
- gdouble fPerpendicularNormalizedY = -(fRun / fLineLength);
-
- // add points along the line
- gdouble fDistanceBetweenPoints = fLineLength / (gdouble)(nNumMiddlePoints + 1);
-
- gint j;
- for(j=0 ; j<nNumMiddlePoints ; j++) {
- mappoint_t* pNewPoint = NULL;
- callback_alloc_point(&pNewPoint);
- gdouble fDistanceFromPoint1 = (j+1) * fDistanceBetweenPoints;
-
- pNewPoint->fLongitude = pPoint1->fLongitude + (fDistanceFromPoint1 * fNormalizedX);
- pNewPoint->fLatitude = pPoint1->fLatitude + (fDistanceFromPoint1 * fNormalizedY);
-
- gdouble fRandomMovementLength = fMaxRandomDistance * g_random_double_range(-1.0, 1.0);
- pNewPoint->fLongitude += (fPerpendicularNormalizedX * fRandomMovementLength); // move each component
- pNewPoint->fLatitude += (fPerpendicularNormalizedY * fRandomMovementLength);
-
- g_ptr_array_add(pDestArray, pNewPoint);
- }
- }
- // add last point
- g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, pSourceArray->len-1));
-//g_print("pDestArray->len = %d\n", pDestArray->len);
-}
-
-static gboolean map_data_load_geometry(map_t* pMap, maprect_t* pRect)
-{
- g_return_val_if_fail(pMap != NULL, FALSE);
- g_return_val_if_fail(pRect != NULL, FALSE);
-
- db_resultset_t* pResultSet = NULL;
- db_row_t aRow;
-
- gint nZoomLevel = map_get_zoomlevel(pMap);
-
- TIMER_BEGIN(mytimer, "BEGIN Geometry LOAD");
-
- // generate SQL
- gchar azCoord1[20], azCoord2[20], azCoord3[20], azCoord4[20], azCoord5[20], azCoord6[20], azCoord7[20], azCoord8[20];
- gchar* pszSQL;
- pszSQL = g_strdup_printf(
- "SELECT Road.ID, Road.TypeID, AsBinary(Road.Coordinates), RoadName.Name, RoadName.SuffixID, AddressLeftStart, AddressLeftEnd, AddressRightStart, AddressRightEnd"
- " FROM Road "
- " LEFT JOIN RoadName ON (Road.RoadNameID=RoadName.ID)"
- " WHERE"
- " MBRIntersects(GeomFromText('Polygon((%s %s,%s %s,%s %s,%s %s,%s %s))'), Coordinates)"
- ,
- // upper left
- g_ascii_dtostr(azCoord1, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord2, 20, pRect->A.fLongitude),
- // upper right
- g_ascii_dtostr(azCoord3, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord4, 20, pRect->B.fLongitude),
- // bottom right
- g_ascii_dtostr(azCoord5, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord6, 20, pRect->B.fLongitude),
- // bottom left
- g_ascii_dtostr(azCoord7, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord8, 20, pRect->A.fLongitude),
- // upper left again
- azCoord1, azCoord2);
-
- //g_print("sql: %s\n", pszSQL);
-
- db_query(pszSQL, &pResultSet);
- g_free(pszSQL);
-
- TIMER_SHOW(mytimer, "after query");
-
- guint32 uRowCount = 0;
- if(pResultSet) {
- while((aRow = db_fetch_row(pResultSet))) {
- uRowCount++;
-
- // aRow[0] is ID
- // aRow[1] is TypeID
- // aRow[2] is Coordinates in mysql's text format
- // aRow[3] is road name
- // aRow[4] is road name suffix id
- // aRow[5] is road address left start
- // aRow[6] is road address left end
- // aRow[7] is road address right start
- // aRow[8] is road address right end
-
- // Get layer type that this belongs on
- gint nTypeID = atoi(aRow[1]);
- if(nTypeID < MAP_OBJECT_TYPE_FIRST || nTypeID > MAP_OBJECT_TYPE_LAST) {
- g_warning("geometry record '%s' has bad type '%s'\n", aRow[0], aRow[1]);
- continue;
- }
-
- //road_t* pNewRoad = NULL;
- //road_alloc(&pNewRoad);
- road_t* pNewRoad = g_new0(road_t, 1);
-
- // Build name by adding suffix, if one is present
- if(aRow[3] != NULL && aRow[4] != NULL) {
- const gchar* pszSuffix = road_suffix_itoa(atoi(aRow[4]), ROAD_SUFFIX_LENGTH_SHORT);
- pNewRoad->pszName = g_strdup_printf("%s%s%s", aRow[3], (pszSuffix[0] != '\0') ? " " : "", pszSuffix);
- }
- else {
- pNewRoad->pszName = g_strdup(""); // XXX: could we maybe not do this?
- }
- pNewRoad->nAddressLeftStart = atoi(aRow[5]);
- pNewRoad->nAddressLeftEnd = atoi(aRow[6]);
- pNewRoad->nAddressRightStart = atoi(aRow[7]);
- pNewRoad->nAddressRightEnd = atoi(aRow[8]);
-
- // perhaps let the wkb parser create the array (at the perfect size)
- pNewRoad->pMapPointsArray = g_array_new(FALSE, FALSE, sizeof(road_t));
- db_parse_wkb_linestring(aRow[2], pNewRoad->pMapPointsArray, &(pNewRoad->rWorldBoundingBox));
-
-#ifdef ENABLE_RIVER_TO_LAKE_LOADTIME_HACK // XXX: combine this and above hack and you get lakes with squiggly edges. whoops. :)
- if(nTypeID == MAP_OBJECT_TYPE_RIVER) {
- mappoint_t* pPointA = &g_array_index(pNewRoad->pMapPointsArray, mappoint_t, 0);
- mappoint_t* pPointB = &g_array_index(pNewRoad->pMapPointsArray, mappoint_t, pNewRoad->pMapPointsArray->len-1);
-
- if(pPointA->fLatitude == pPointB->fLatitude && pPointA->fLongitude == pPointB->fLongitude) {
- nTypeID = MAP_OBJECT_TYPE_LAKE;
- }
- }
-#endif
-
- // Add this item to layer's list of pointstrings
- g_ptr_array_add(pMap->apLayerData[nTypeID]->pRoadsArray, pNewRoad);
- } // end while loop on rows
- //g_print("[%d rows]\n", uRowCount);
- TIMER_SHOW(mytimer, "after rows retrieved");
-
- db_free_result(pResultSet);
- TIMER_SHOW(mytimer, "after free results");
- TIMER_END(mytimer, "END Geometry LOAD");
-
- return TRUE;
- }
- else {
- //g_print(" no rows\n");
- return FALSE;
- }
-}
-
-static gboolean map_data_load_locations(map_t* pMap, maprect_t* pRect)
-{
- g_return_val_if_fail(pMap != NULL, FALSE);
- g_return_val_if_fail(pRect != NULL, FALSE);
-
-// if(map_get_zoomlevel(pMap) < MIN_ZOOM_LEVEL_FOR_LOCATIONS) {
-// return TRUE;
-// }
-
- TIMER_BEGIN(mytimer, "BEGIN Locations LOAD");
-
- // generate SQL
- gchar* pszSQL;
- gchar azCoord1[20], azCoord2[20], azCoord3[20], azCoord4[20], azCoord5[20], azCoord6[20], azCoord7[20], azCoord8[20];
- pszSQL = g_strdup_printf(
- "SELECT Location.ID, Location.LocationSetID, AsBinary(Location.Coordinates), LocationAttributeValue_Name.Value" // LocationAttributeValue_Name.Value is the "Name" field of this Location
- " FROM Location"
- " LEFT JOIN LocationAttributeValue AS LocationAttributeValue_Name ON (LocationAttributeValue_Name.LocationID=Location.ID AND LocationAttributeValue_Name.AttributeNameID=%d)"
- " WHERE"
- " MBRIntersects(GeomFromText('Polygon((%s %s,%s %s,%s %s,%s %s,%s %s))'), Coordinates)",
- LOCATION_ATTRIBUTE_ID_NAME, // attribute ID for 'name'
- // upper left
- g_ascii_dtostr(azCoord1, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord2, 20, pRect->A.fLongitude),
- // upper right
- g_ascii_dtostr(azCoord3, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord4, 20, pRect->B.fLongitude),
- // bottom right
- g_ascii_dtostr(azCoord5, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord6, 20, pRect->B.fLongitude),
- // bottom left
- g_ascii_dtostr(azCoord7, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord8, 20, pRect->A.fLongitude),
- // upper left again
- azCoord1, azCoord2);
- //g_print("sql: %s\n", pszSQL);
-
- db_resultset_t* pResultSet = NULL;
- db_query(pszSQL, &pResultSet);
- g_free(pszSQL);
-
- TIMER_SHOW(mytimer, "after query");
-
- guint32 uRowCount = 0;
- if(pResultSet) {
- db_row_t aRow;
- while((aRow = db_fetch_row(pResultSet))) {
- uRowCount++;
-
- // aRow[0] is ID
- // aRow[1] is LocationSetID
- // aRow[2] is Coordinates in mysql's binary format
- // aRow[3] is Name
-
- // Get layer type that this belongs on
- gint nLocationSetID = atoi(aRow[1]);
-
- // Extract
- location_t* pNewLocation = NULL;
- location_alloc(&pNewLocation);
-
- pNewLocation->nID = atoi(aRow[0]);
-
- // Parse coordinates
- db_parse_wkb_point(aRow[2], &(pNewLocation->Coordinates));
-
- // make a copy of the name field, or "" (never leave it == NULL)
- pNewLocation->pszName = g_strdup(aRow[3] != NULL ? aRow[3] : "");
- map_store_location(pMap, pNewLocation, nLocationSetID);
- } // end while loop on rows
- //g_print("[%d rows]\n", uRowCount);
- TIMER_SHOW(mytimer, "after rows retrieved");
-
- db_free_result(pResultSet);
- TIMER_END(mytimer, "END Locations LOAD");
- return TRUE;
- }
- else {
- TIMER_END(mytimer, "END Locations LOAD (0 results)");
- return FALSE;
- }
-}
-
-static void map_data_clear(map_t* pMap)
-{
- // Clear layers
- gint i,j;
- for(i=0 ; i<G_N_ELEMENTS(pMap->apLayerData) ; i++) {
- maplayer_data_t* pLayerData = pMap->apLayerData[i];
-
- // Free each
- for(j = (pLayerData->pRoadsArray->len - 1) ; j>=0 ; j--) {
- road_t* pRoad = g_ptr_array_remove_index_fast(pLayerData->pRoadsArray, j);
- g_array_free(pRoad->pMapPointsArray, TRUE);
- g_free(pRoad);
- }
- g_assert(pLayerData->pRoadsArray->len == 0);
- }
-
- // Clear locations
- map_init_location_hash(pMap);
-}
-
-double map_get_distance_in_meters(mappoint_t* pA, mappoint_t* pB)
-{
- // XXX: this function is broken.
-
- // This functions calculates the length of the arc of the "greatcircle" that goes through
- // the two points A and B and whos center is the center of the sphere, O.
-
- // When we multiply this angle (in radians) by the radius, we get the length of the arc.
-
- // NOTE: This algorithm wrongly assumes that Earth is a perfect sphere.
- // It is actually slightly egg shaped. But it's good enough.
-
- // All trig functions expect arguments in radians.
- double fLonA_Rad = DEG2RAD(pA->fLongitude);
- double fLonB_Rad = DEG2RAD(pB->fLongitude);
- double fLatA_Rad = DEG2RAD(pA->fLatitude);
- double fLatB_Rad = DEG2RAD(pB->fLatitude);
-
- // Step 1. Calculate AOB (in radians).
- // An explanation of this equation is at http://mathforum.org/library/drmath/view/51722.html
- double fAOB_Rad = acos((cos(fLatA_Rad) * cos(fLatB_Rad) * cos(fLonB_Rad - fLonA_Rad)) + (sin(fLatA_Rad) * sin(fLatB_Rad)));
-
- // Step 2. Multiply the angle by the radius of the sphere to get arc length.
- return fAOB_Rad * RADIUS_OF_WORLD_IN_METERS;
-}
-
-gdouble map_get_straight_line_distance_in_degrees(mappoint_t* p1, mappoint_t* p2)
-{
- gdouble fDeltaX = ((p2->fLongitude) - (p1->fLongitude));
- gdouble fDeltaY = ((p2->fLatitude) - (p1->fLatitude));
-
- return sqrt((fDeltaX*fDeltaX) + (fDeltaY*fDeltaY));
-}
-
-gdouble map_get_distance_in_pixels(map_t* pMap, mappoint_t* p1, mappoint_t* p2)
-{
- rendermetrics_t metrics;
- map_get_render_metrics(pMap, &metrics);
-
- gdouble fX1 = SCALE_X(&metrics, p1->fLongitude);
- gdouble fY1 = SCALE_Y(&metrics, p1->fLatitude);
-
- gdouble fX2 = SCALE_X(&metrics, p2->fLongitude);
- gdouble fY2 = SCALE_Y(&metrics, p2->fLatitude);
-
- gdouble fDeltaX = fX2 - fX1;
- gdouble fDeltaY = fY2 - fY1;
-
- return sqrt((fDeltaX*fDeltaX) + (fDeltaY*fDeltaY));
-}
-
-gboolean map_points_equal(mappoint_t* p1, mappoint_t* p2)
-{
- return( p1->fLatitude == p2->fLatitude && p1->fLongitude == p2->fLongitude);
-}
-
-void map_add_track(map_t* pMap, gint hTrack)
-{
- g_array_append_val(pMap->pTracksArray, hTrack);
-}
-
-// ========================================================
-// Hit Testing
-// ========================================================
-
-void map_hitstruct_free(map_t* pMap, maphit_t* pHitStruct)
-{
- if(pHitStruct == NULL) return;
-
- // free type-specific stuff
- if(pHitStruct->eHitType == MAP_HITTYPE_URL) {
- g_free(pHitStruct->URLHit.pszURL);
- }
-
- // free common stuff
- g_free(pHitStruct->pszText);
- g_free(pHitStruct);
-}
-
-// XXX: perhaps make map_hit_test return a more complex structure indicating what type of hit it is?
-gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, maphit_t** ppReturnStruct)
+gboolean map_object_type_atoi(const gchar* pszName, gint* pnReturnObjectTypeID)
{
-#if 0 // GGGGGGGGGGGGGGGG
- rendermetrics_t rendermetrics;
- map_get_render_metrics(pMap, &rendermetrics);
-
- if(map_hit_test_locationselections(pMap, &rendermetrics, pMap->pLocationSelectionArray, pMapPoint, ppReturnStruct)) {
- return TRUE;
- }
-
- if(map_hit_test_locationsets(pMap, &rendermetrics, pMapPoint, ppReturnStruct)) {
- return TRUE;
- }
-
- // Test things in the REVERSE order they are drawn (otherwise we'll match things that have been painted-over)
gint i;
- for(i=G_N_ELEMENTS(layerdraworder)-1 ; i>=0 ; i--) {
- if(layerdraworder[i].eSubLayerRenderType != SUBLAYER_RENDERTYPE_LINES) continue;
-
- gint nLayer = layerdraworder[i].nLayer;
-
- // use width from whichever layer it's wider in
- gdouble fLineWidth = max(g_aLayers[nLayer]->Style.aSubLayers[0].afLineWidths[pMap->uZoomLevel-1],
- g_aLayers[nLayer]->Style.aSubLayers[1].afLineWidths[pMap->uZoomLevel-1]);
-
-#define EXTRA_CLICKABLE_ROAD_IN_PIXELS (3)
-
- // make thin roads a little easier to hit
- // fLineWidth = max(fLineWidth, MIN_ROAD_HIT_TARGET_WIDTH);
-
- // XXX: hack, map_pixels should really take a floating point instead.
- gdouble fMaxDistance = map_pixels_to_degrees(pMap, 1, pMap->uZoomLevel) * ((fLineWidth/2) + EXTRA_CLICKABLE_ROAD_IN_PIXELS); // half width on each side
-
- if(map_hit_test_layer_roads(pMap->apLayerData[nLayer]->pRoadsArray, fMaxDistance, pMapPoint, ppReturnStruct)) {
- return TRUE;
- }
- // otherwise try next layer...
- }
-#endif
- return FALSE;
-}
-
-static gboolean map_hit_test_layer_roads(GPtrArray* pRoadsArray, gdouble fMaxDistance, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
-{
- g_assert(ppReturnStruct != NULL);
- g_assert(*ppReturnStruct == NULL); // pointer to null pointer
-
- /* this is helpful for testing with the g_print()s in map_hit_test_line() */
-/* mappoint_t p1 = {2,2}; */
-/* mappoint_t p2 = {-10,10}; */
-/* mappoint_t p3 = {0,10}; */
-/* map_hit_test_line(&p1, &p2, &p3, 20); */
-/* return FALSE; */
-
- // Loop through line strings, order doesn't matter here since they're all on the same level.
- gint iString;
- for(iString=0 ; iString<pRoadsArray->len ; iString++) {
- road_t* pRoad = g_ptr_array_index(pRoadsArray, iString);
- if(pRoad->pMapPointsArray->len < 2) continue;
-
- // start on 1 so we can do -1 trick below
- gint iPoint;
- for(iPoint=1 ; iPoint<pRoad->pMapPointsArray->len ; iPoint++) {
- mappoint_t* pPoint1 = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint-1);
- mappoint_t* pPoint2 = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint);
-
- mappoint_t pointClosest;
- gdouble fPercentAlongLine;
-
- // hit test this line
- if(map_hit_test_line(pPoint1, pPoint2, pHitPoint, fMaxDistance, &pointClosest, &fPercentAlongLine)) {
- //g_print("fPercentAlongLine = %f\n",fPercentAlongLine);
-
- // fill out a new maphit_t struct with details
- maphit_t* pHitStruct = g_new0(maphit_t, 1);
- pHitStruct->eHitType = MAP_HITTYPE_ROAD;
-
- if(pRoad->pszName[0] == '\0') {
- pHitStruct->pszText = g_strdup("<i>unnamed</i>");
- }
- else {
- ESide eSide = map_side_test_line(pPoint1, pPoint2, &pointClosest, pHitPoint);
-
- gint nAddressStart;
- gint nAddressEnd;
- if(eSide == SIDE_LEFT) {
- nAddressStart = pRoad->nAddressLeftStart;
- nAddressEnd = pRoad->nAddressLeftEnd;
- }
- else {
- nAddressStart = pRoad->nAddressRightStart;
- nAddressEnd = pRoad->nAddressRightEnd;
- }
-
- if(nAddressStart == 0 || nAddressEnd == 0) {
- pHitStruct->pszText = g_strdup_printf("%s", pRoad->pszName);
- }
- else {
- gint nMinAddres = min(nAddressStart, nAddressEnd);
- gint nMaxAddres = max(nAddressStart, nAddressEnd);
-
- pHitStruct->pszText = g_strdup_printf("%s <b>#%d-%d</b>", pRoad->pszName, nMinAddres, nMaxAddres);
- }
- }
- *ppReturnStruct = pHitStruct;
- return TRUE;
- }
- }
- }
- return FALSE;
-}
-
-// Does the given point come close enough to the line segment to be considered a hit?
-static gboolean map_hit_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pHitPoint, gdouble fMaxDistance, mappoint_t* pReturnClosestPoint, gdouble* pfReturnPercentAlongLine)
-{
- if(pHitPoint->fLatitude < (pPoint1->fLatitude - fMaxDistance) && pHitPoint->fLatitude < (pPoint2->fLatitude - fMaxDistance)) return FALSE;
- if(pHitPoint->fLongitude < (pPoint1->fLongitude - fMaxDistance) && pHitPoint->fLongitude < (pPoint2->fLongitude - fMaxDistance)) return FALSE;
-
- // Some bad ASCII art demonstrating the situation:
- //
- // / (u)
- // / |
- // / |
- // (0,0) =====(a)========== (v)
-
- // v is the translated-to-origin vector of line (road)
- // u is the translated-to-origin vector of the hitpoint
- // a is the closest point on v to the end of u (the hit point)
-
- //
- // 1. Convert p1->p2 vector into a vector (v) that is assumed to come out of the origin (0,0)
- //
- mappoint_t v;
- v.fLatitude = pPoint2->fLatitude - pPoint1->fLatitude; // 10->90 becomes 0->80 (just store 80)
- v.fLongitude = pPoint2->fLongitude - pPoint1->fLongitude;
-
- gdouble fLengthV = sqrt((v.fLatitude*v.fLatitude) + (v.fLongitude*v.fLongitude));
- if(fLengthV == 0.0) return FALSE; // bad data: a line segment with no length?
-
- //
- // 2. Make a unit vector out of v (meaning same direction but length=1) by dividing v by v's length
- //
- mappoint_t unitv;
- unitv.fLatitude = v.fLatitude / fLengthV;
- unitv.fLongitude = v.fLongitude / fLengthV; // unitv is now a unit (=1.0) length v
-
- //
- // 3. Translate the hitpoint in the same way we translated v
- //
- mappoint_t u;
- u.fLatitude = pHitPoint->fLatitude - pPoint1->fLatitude;
- u.fLongitude = pHitPoint->fLongitude - pPoint1->fLongitude;
-
- //
- // 4. Use the dot product of (unitv) and (u) to find (a), the point along (v) that is closest to (u). see diagram above.
- //
- gdouble fLengthAlongV = (unitv.fLatitude * u.fLatitude) + (unitv.fLongitude * u.fLongitude);
-
- // Does it fall along the length of the line *segment* v? (we know it falls along the infinite line v, but that does us no good.)
- // (This produces false negatives on round/butt end caps, but that's better that a false positive when another line is actually there!)
- if(fLengthAlongV > 0 && fLengthAlongV < fLengthV) {
- mappoint_t a;
- a.fLatitude = v.fLatitude * (fLengthAlongV / fLengthV); // multiply each component by the percentage
- a.fLongitude = v.fLongitude * (fLengthAlongV / fLengthV);
- // NOTE: (a) is *not* where it actually hit on the *map*. don't draw this point! we'd have to translate it back away from the origin.
-
- //
- // 5. Calculate the distance from the end of (u) to (a). If it's less than the fMaxDistance, it's a hit.
- //
- gdouble fRise = u.fLatitude - a.fLatitude;
- gdouble fRun = u.fLongitude - a.fLongitude;
- gdouble fDistanceSquared = fRise*fRise + fRun*fRun; // compare squared distances. same results but without the sqrt.
-
- if(fDistanceSquared <= (fMaxDistance*fMaxDistance)) {
- /* debug aids */
- /* g_print("pPoint1 (%f,%f)\n", pPoint1->fLatitude, pPoint1->fLongitude); */
- /* g_print("pPoint2 (%f,%f)\n", pPoint2->fLatitude, pPoint2->fLongitude); */
- /* g_print("pHitPoint (%f,%f)\n", pHitPoint->fLatitude, pHitPoint->fLongitude); */
- /* g_print("v (%f,%f)\n", v.fLatitude, v.fLongitude); */
- /* g_print("u (%f,%f)\n", u.fLatitude, u.fLongitude); */
- /* g_print("unitv (%f,%f)\n", unitv.fLatitude, unitv.fLongitude); */
- /* g_print("fDotProduct = %f\n", fDotProduct); */
- /* g_print("a (%f,%f)\n", a.fLatitude, a.fLongitude); */
- /* g_print("fDistance = %f\n", sqrt(fDistanceSquared)); */
- if(pReturnClosestPoint) {
- pReturnClosestPoint->fLatitude = a.fLatitude + pPoint1->fLatitude;
- pReturnClosestPoint->fLongitude = a.fLongitude + pPoint1->fLongitude;
- }
-
- if(pfReturnPercentAlongLine) {
- *pfReturnPercentAlongLine = (fLengthAlongV / fLengthV);
- }
+ for(i=0 ; i<MAP_NUM_OBJECT_TYPES ; i++) {
+ if(strcmp(pszName, g_apszMapObjectTypeNames[i]) == 0) {
+ *pnReturnObjectTypeID = i;
return TRUE;
}
}
return FALSE;
}
-static ESide map_side_test_line(mappoint_t* pPoint1, mappoint_t* pPoint2, mappoint_t* pClosestPointOnLine, mappoint_t* pHitPoint)
-{
- // make a translated-to-origin *perpendicular* vector of the line (points to the "left" of the line when walking from point 1 to 2)
- mappoint_t v;
- v.fLatitude = (pPoint2->fLongitude - pPoint1->fLongitude); // NOTE: swapping lat and lon to make perpendicular
- v.fLongitude = -(pPoint2->fLatitude - pPoint1->fLatitude);
-
- // make a translated-to-origin vector of the line from closest point to hitpoint
- mappoint_t u;
- u.fLatitude = pHitPoint->fLatitude - pClosestPointOnLine->fLatitude;
- u.fLongitude = pHitPoint->fLongitude - pClosestPointOnLine->fLongitude;
-
- // figure out the signs of the elements of the vectors
- gboolean bULatPositive = (u.fLatitude >= 0);
- gboolean bULonPositive = (u.fLongitude >= 0);
- gboolean bVLatPositive = (v.fLatitude >= 0);
- gboolean bVLonPositive = (v.fLongitude >= 0);
-
- //g_print("%s,%s %s,%s\n", bVLonPositive?"Y":"N", bVLatPositive?"Y":"N", bULonPositive?"Y":"N", bULatPositive?"Y":"N");
-
- // if the signs agree, it's to the left, otherwise to the right
- if(bULatPositive == bVLatPositive && bULonPositive == bVLonPositive) {
- return SIDE_LEFT;
- }
- else {
- // let's check our algorithm: if the signs aren't both the same, they should be both opposite
- // ...unless the vector from hitpoint to line center is 0, which doesn't have sign
-
- //g_print("%f,%f %f,%f\n", u.fLatitude, u.fLongitude, v.fLatitude, v.fLongitude);
- g_assert(bULatPositive != bVLatPositive || u.fLatitude == 0.0);
- g_assert(bULonPositive != bVLonPositive || u.fLongitude == 0.0);
- return SIDE_RIGHT;
- }
-}
-
-// hit test all locations
-static gboolean map_hit_test_locationsets(map_t* pMap, rendermetrics_t* pRenderMetrics, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
+gboolean map_layer_render_type_atoi(const gchar* pszName, gint* pnReturnRenderTypeID)
{
- gdouble fMaxDistance = map_pixels_to_degrees(pMap, 1, pMap->uZoomLevel) * 3; // XXX: don't hardcode distance :)
+ gchar* g_apszMapRenderTypeNames[] = {"lines", "polygons", "line-labels", "polygon-labels", "fill", "locations", "location-labels"};
- const GPtrArray* pLocationSetsArray = locationset_get_array();
gint i;
- for(i=0 ; i<pLocationSetsArray->len ; i++) {
- locationset_t* pLocationSet = g_ptr_array_index(pLocationSetsArray, i);
-
- // the user is NOT trying to click on invisible things :)
- if(!locationset_is_visible(pLocationSet)) continue;
-
- // 2. Get array of Locations from the hash table using LocationSetID
- GPtrArray* pLocationsArray;
- pLocationsArray = g_hash_table_lookup(pMap->pLocationArrayHashTable, &(pLocationSet->nID));
- if(pLocationsArray != NULL) {
- if(map_hit_test_locations(pMap, pRenderMetrics, pLocationsArray, pHitPoint, ppReturnStruct)) {
- return TRUE;
- }
- }
- else {
- // none loaded
- }
- }
- return FALSE;
-}
-
-// hit-test an array of locations
-static gboolean map_hit_test_locations(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationsArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
-{
- gint i;
- for(i=(pLocationsArray->len-1) ; i>=0 ; i--) { // NOTE: test in *reverse* order so we hit the ones drawn on top first
- location_t* pLocation = g_ptr_array_index(pLocationsArray, i);
-
- // bounding box test
- if(pLocation->Coordinates.fLatitude < pRenderMetrics->rWorldBoundingBox.A.fLatitude
- || pLocation->Coordinates.fLongitude < pRenderMetrics->rWorldBoundingBox.A.fLongitude
- || pLocation->Coordinates.fLatitude > pRenderMetrics->rWorldBoundingBox.B.fLatitude
- || pLocation->Coordinates.fLongitude > pRenderMetrics->rWorldBoundingBox.B.fLongitude)
- {
- continue; // not visible
- }
-
- gdouble fX1 = SCALE_X(pRenderMetrics, pLocation->Coordinates.fLongitude);
- gdouble fY1 = SCALE_Y(pRenderMetrics, pLocation->Coordinates.fLatitude);
- gdouble fX2 = SCALE_X(pRenderMetrics, pHitPoint->fLongitude);
- gdouble fY2 = SCALE_Y(pRenderMetrics, pHitPoint->fLatitude);
-
- gdouble fDeltaX = fX2 - fX1;
- gdouble fDeltaY = fY2 - fY1;
-
- fDeltaX = abs(fDeltaX);
- fDeltaY = abs(fDeltaY);
-
- if(fDeltaX <= 3 && fDeltaY <= 3) {
- // fill out a new maphit_t struct with details
- maphit_t* pHitStruct = g_new0(maphit_t, 1);
- pHitStruct->eHitType = MAP_HITTYPE_LOCATION;
- pHitStruct->LocationHit.nLocationID = pLocation->nID;
-
- pHitStruct->LocationHit.Coordinates.fLatitude = pLocation->Coordinates.fLatitude;
- pHitStruct->LocationHit.Coordinates.fLongitude = pLocation->Coordinates.fLongitude;
-
- if(pLocation->pszName[0] == '\0') {
- pHitStruct->pszText = g_strdup_printf("<i>unnamed POI %d</i>", pLocation->nID);
- }
- else {
- pHitStruct->pszText = g_strdup(pLocation->pszName);
- }
- *ppReturnStruct = pHitStruct;
+ for(i=0 ; i<G_N_ELEMENTS(g_apszMapRenderTypeNames) ; i++) {
+ if(strcmp(pszName, g_apszMapRenderTypeNames[i]) == 0) {
+ *pnReturnRenderTypeID = i;
return TRUE;
}
}
return FALSE;
}
-gboolean hit_test_screenpoint_in_rect(screenpoint_t* pPt, screenrect_t* pRect)
-{
- return(pPt->nX >= pRect->A.nX && pPt->nX <= pRect->B.nX && pPt->nY >= pRect->A.nY && pPt->nY <= pRect->B.nY);
-}
-
-static gboolean map_hit_test_locationselections(map_t* pMap, rendermetrics_t* pRenderMetrics, GPtrArray* pLocationSelectionArray, mappoint_t* pHitPoint, maphit_t** ppReturnStruct)
-{
- screenpoint_t screenpoint;
- screenpoint.nX = (gint)SCALE_X(pRenderMetrics, pHitPoint->fLongitude);
- screenpoint.nY = (gint)SCALE_Y(pRenderMetrics, pHitPoint->fLatitude);
-
- gint i;
- for(i=(pLocationSelectionArray->len-1) ; i>=0 ; i--) {
- locationselection_t* pLocationSelection = g_ptr_array_index(pLocationSelectionArray, i);
-
- if(pLocationSelection->bVisible == FALSE) continue;
-
- if(hit_test_screenpoint_in_rect(&screenpoint, &(pLocationSelection->InfoBoxRect))) {
- // fill out a new maphit_t struct with details
- maphit_t* pHitStruct = g_new0(maphit_t, 1);
-
- if(hit_test_screenpoint_in_rect(&screenpoint, &(pLocationSelection->InfoBoxCloseRect))) {
- pHitStruct->eHitType = MAP_HITTYPE_LOCATIONSELECTION_CLOSE;
- pHitStruct->pszText = g_strdup("close");
- pHitStruct->LocationSelectionHit.nLocationID = pLocationSelection->nLocationID;
- }
- else if(hit_test_screenpoint_in_rect(&screenpoint, &(pLocationSelection->EditRect))) {
- pHitStruct->eHitType = MAP_HITTYPE_LOCATIONSELECTION_EDIT;
- pHitStruct->pszText = g_strdup("edit");
- pHitStruct->LocationSelectionHit.nLocationID = pLocationSelection->nLocationID;
- }
- else {
- gboolean bURLMatch = FALSE;
-
- gint iURL;
- for(iURL=0 ; iURL<pLocationSelection->nNumURLs ; iURL++) {
- if(hit_test_screenpoint_in_rect(&screenpoint, &(pLocationSelection->aURLs[iURL].Rect))) {
- pHitStruct->eHitType = MAP_HITTYPE_URL;
- pHitStruct->pszText = g_strdup("click to open location");
- pHitStruct->URLHit.pszURL = g_strdup(pLocationSelection->aURLs[iURL].pszURL);
-
- bURLMatch = TRUE;
- break;
- }
- }
-
- // no url match, just return a generic "hit the locationselection box"
- if(!bURLMatch) {
- pHitStruct->eHitType = MAP_HITTYPE_LOCATIONSELECTION;
- pHitStruct->pszText = g_strdup("");
- pHitStruct->LocationSelectionHit.nLocationID = pLocationSelection->nLocationID;
- }
- }
- *ppReturnStruct = pHitStruct;
- return TRUE;
- }
- }
- return FALSE;
-}
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
+//
// Get an attribute from a selected location
@@ -1226,32 +481,6 @@ const gchar* map_location_selection_get_attribute(const map_t* pMap, const locat
return NULL;
}
-gboolean map_object_type_atoi(const gchar* pszName, gint* pnReturnObjectTypeID)
-{
- gint i;
- for(i=0 ; i<MAP_NUM_OBJECT_TYPES ; i++) {
- if(strcmp(pszName, g_apszMapObjectTypeNames[i]) == 0) {
- *pnReturnObjectTypeID = i;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-gboolean map_layer_render_type_atoi(const gchar* pszName, gint* pnReturnRenderTypeID)
-{
- gchar* g_apszMapRenderTypeNames[] = {"lines", "polygons", "line-labels", "polygon-labels", "fill", "locations", "location-labels"};
-
- gint i;
- for(i=0 ; i<G_N_ELEMENTS(g_apszMapRenderTypeNames) ; i++) {
- if(strcmp(pszName, g_apszMapRenderTypeNames[i]) == 0) {
- *pnReturnRenderTypeID = i;
- return TRUE;
- }
- }
- return FALSE;
-}
-
void map_callback_free_locations_array(gpointer p)
{
GPtrArray* pLocationsArray = (GPtrArray*)p;
@@ -1364,12 +593,63 @@ gboolean map_location_selection_remove(map_t* pMap, gint nLocationID)
return FALSE; // removed
}
-gboolean map_rects_overlap(const maprect_t* p1, const maprect_t* p2)
+
+#ifdef ROADSTER_DEAD_CODE
+/*
+static gboolean map_enhance_linestring(GPtrArray* pSourceArray, GPtrArray* pDestArray, gboolean (*callback_alloc_point)(mappoint_t**), gdouble fMaxDistanceBetweenPoints, gdouble fMaxRandomDistance)
{
- if(p1->B.fLatitude < p2->A.fLatitude) return FALSE;
- if(p1->B.fLongitude < p2->A.fLongitude) return FALSE;
- if(p1->A.fLatitude > p2->B.fLatitude) return FALSE;
- if(p1->A.fLongitude > p2->B.fLongitude) return FALSE;
+ g_assert(pSourceArray->len >= 2);
+//g_print("pSourceArray->len = %d\n", pSourceArray->len);
- return TRUE;
+ // add first point
+ g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, 0));
+
+ gint i = 0;
+ for(i=0 ; i<(pSourceArray->len-1) ; i++) {
+ mappoint_t* pPoint1 = g_ptr_array_index(pSourceArray, i);
+ mappoint_t* pPoint2 = g_ptr_array_index(pSourceArray, i+1);
+
+ gdouble fRise = (pPoint2->fLatitude - pPoint1->fLatitude);
+ gdouble fRun = (pPoint2->fLongitude - pPoint1->fLongitude);
+
+ gdouble fLineLength = sqrt((fRun*fRun) + (fRise*fRise));
+// g_print("fLineLength = %f\n", fLineLength);
+ gint nNumMiddlePoints = (gint)(fLineLength / fMaxDistanceBetweenPoints);
+// g_print("(fLineLength / fMaxDistanceBetweenPoints) = nNumNewPoints; %f / %f = %d\n", fLineLength, fMaxDistanceBetweenPoints, nNumNewPoints);
+ if(nNumMiddlePoints == 0) continue; // nothing to add
+
+// g_print("fDistanceBetweenPoints = %f\n", fDistanceBetweenPoints);
+
+ gdouble fNormalizedX = fRun / fLineLength;
+ gdouble fNormalizedY = fRise / fLineLength;
+// g_print("fNormalizedX = %f\n", fNormalizedX);
+// g_print("fNormalizedY = %f\n", fNormalizedY);
+
+ gdouble fPerpendicularNormalizedX = fRise / fLineLength;
+ gdouble fPerpendicularNormalizedY = -(fRun / fLineLength);
+
+ // add points along the line
+ gdouble fDistanceBetweenPoints = fLineLength / (gdouble)(nNumMiddlePoints + 1);
+
+ gint j;
+ for(j=0 ; j<nNumMiddlePoints ; j++) {
+ mappoint_t* pNewPoint = NULL;
+ callback_alloc_point(&pNewPoint);
+ gdouble fDistanceFromPoint1 = (j+1) * fDistanceBetweenPoints;
+
+ pNewPoint->fLongitude = pPoint1->fLongitude + (fDistanceFromPoint1 * fNormalizedX);
+ pNewPoint->fLatitude = pPoint1->fLatitude + (fDistanceFromPoint1 * fNormalizedY);
+
+ gdouble fRandomMovementLength = fMaxRandomDistance * g_random_double_range(-1.0, 1.0);
+ pNewPoint->fLongitude += (fPerpendicularNormalizedX * fRandomMovementLength); // move each component
+ pNewPoint->fLatitude += (fPerpendicularNormalizedY * fRandomMovementLength);
+
+ g_ptr_array_add(pDestArray, pNewPoint);
+ }
+ }
+ // add last point
+ g_ptr_array_add(pDestArray, g_ptr_array_index(pSourceArray, pSourceArray->len-1));
+//g_print("pDestArray->len = %d\n", pDestArray->len);
}
+*/
+#endif
diff --git a/src/map.h b/src/map.h
index b526e31..6c288d2 100644
--- a/src/map.h
+++ b/src/map.h
@@ -24,6 +24,7 @@
#ifndef _MAP_H_
#define _MAP_H_
+#include <math.h>
#include "gfreelist.h"
//
@@ -48,6 +49,9 @@
#define MAP_OBJECT_TYPE_FIRST (1)
#define MAP_OBJECT_TYPE_LAST (12)
+#define MAP_LEVEL_OF_DETAIL_BEST (0)
+#define MAP_LEVEL_OF_DETAIL_WORST (3)
+#define MAP_NUM_LEVELS_OF_DETAIL (4)
//
// Line CAP styles
@@ -101,9 +105,9 @@ typedef enum {
struct GtkWidget;
#define MIN_ZOOM_LEVEL (1)
-#define MAX_ZOOM_LEVEL (5)
-#define NUM_ZOOM_LEVELS (5)
-#define MIN_ZOOM_LEVEL_FOR_LOCATIONS (6) // don't show POI above this level
+#define MAX_ZOOM_LEVEL (41)
+#define NUM_ZOOM_LEVELS (41)
+#define MIN_ZOOM_LEVEL_FOR_LOCATIONS (25) // don't show POI above this level
#include "scenemanager.h"
@@ -153,7 +157,10 @@ typedef struct {
EDistanceUnits eScaleMetricUnit;
gint nScaleMetricNumber;
- gchar* szName;
+ //gchar* szName;
+
+ gint nStyleZoomLevel; // pretend we're zoomlevel X because there are more real zoomlevels than in the map layer style definitions
+ gint nLevelOfDetail; // pretend we're zoomlevel X because there are more real zoomlevels than in the map layer style definitions
} zoomlevel_t;
extern zoomlevel_t g_sZoomLevels[];
@@ -173,19 +180,22 @@ typedef struct {
maprect_t rWorldBoundingBox;
gint nWindowWidth;
gint nWindowHeight;
+ gint nLevelOfDetail;
} rendermetrics_t;
#define SCALE_X(p, x) ((((x) - (p)->rWorldBoundingBox.A.fLongitude) / (p)->fScreenLongitude) * (p)->nWindowWidth)
#define SCALE_Y(p, y) ((p)->nWindowHeight - ((((y) - (p)->rWorldBoundingBox.A.fLatitude) / (p)->fScreenLatitude) * (p)->nWindowHeight))
-typedef struct {
- GPtrArray* pRoadsArray;
-} maplayer_data_t;
+// typedef struct {
+// GPtrArray* pRoadsArray;
+// } maplayer_data_t;
typedef struct {
GPtrArray* pLocationsArray;
} maplayer_locations_t;
+#include "map_tilemanager.h"
+
typedef struct {
mappoint_t MapCenter;
dimensions_t MapDimensions;
@@ -195,7 +205,9 @@ typedef struct {
// data
GArray *pTracksArray;
- maplayer_data_t *apLayerData[ MAP_NUM_OBJECT_TYPES + 1 ];
+// maplayer_data_t *apLayerData[ MAP_NUM_OBJECT_TYPES + 1 ];
+
+ maptilemanager_t* pTileManager;
// Locationsets
GHashTable *pLocationArrayHashTable;
@@ -209,42 +221,6 @@ typedef struct {
} map_t;
typedef enum {
- MAP_HITTYPE_LOCATION,
- MAP_HITTYPE_ROAD,
-
- // the following all use LocationSelectionHit in the union below
- MAP_HITTYPE_LOCATIONSELECTION, // hit somewhere on a locationselection graphic (info balloon)
- MAP_HITTYPE_LOCATIONSELECTION_CLOSE, // hit locationselection graphic close graphic (info balloon [X])
- MAP_HITTYPE_LOCATIONSELECTION_EDIT, // hit locationselection graphic edit graphic (info balloon "edit")
-
- MAP_HITTYPE_URL,
-} EMapHitType;
-
-typedef struct {
- EMapHitType eHitType;
- gchar* pszText;
- union {
- struct {
- gint nLocationID;
- mappoint_t Coordinates;
- } LocationHit;
-
- struct {
- gint nRoadID;
- mappoint_t ClosestPoint;
- } RoadHit;
-
- struct {
- gint nLocationID;
- } LocationSelectionHit;
-
- struct {
- gchar* pszURL;
- } URLHit;
- };
-} maphit_t;
-
-typedef enum {
MAP_LAYER_RENDERTYPE_LINES,
MAP_LAYER_RENDERTYPE_POLYGONS,
MAP_LAYER_RENDERTYPE_LINE_LABELS,
@@ -287,8 +263,9 @@ typedef struct {
#define DRAWFLAG_ALL (1|2)
-#define NUM_SUBLAYER_TO_DRAW (21) //(24)
-extern draworder_t layerdraworder[NUM_SUBLAYER_TO_DRAW]; //
+// #define NUM_SUBLAYER_TO_DRAW (21) //(24)
+// extern draworder_t layerdraworder[NUM_SUBLAYER_TO_DRAW]; //
+
void map_init(void);
gboolean map_new(map_t** ppMap, GtkWidget* pTargetWidget);
@@ -331,9 +308,6 @@ void map_release_pixmap(map_t* pMap);
void map_draw(map_t* pMap, GdkPixmap* pTargetPixmap, gint nDrawFlags);
void map_add_track(map_t* pMap, gint hTrack);
-gboolean map_hit_test(map_t* pMap, mappoint_t* pMapPoint, maphit_t** ppReturnStruct);
-void map_hitstruct_free(map_t* pMap, maphit_t* pHitStruct);
-
gboolean map_location_selection_add(map_t* pMap, gint nLocationID);
gboolean map_location_selection_remove(map_t* pMap, gint nLocationID);
@@ -344,4 +318,9 @@ gboolean map_layer_render_type_atoi(const gchar* pszName, gint* pnReturnRenderTy
gboolean map_rects_overlap(const maprect_t* p1, const maprect_t* p2);
+void map_zoom_to_screenrect(map_t* pMap, const screenrect_t* pRect);
+gint map_screenrect_width(const screenrect_t* pRect);
+gint map_screenrect_height(const screenrect_t* pRect);
+void map_get_screenrect_centerpoint(const screenrect_t* pRect, screenpoint_t* pPoint);
+
#endif
diff --git a/src/map_draw_cairo.c b/src/map_draw_cairo.c
index e3543e1..6d65ce9 100644
--- a/src/map_draw_cairo.c
+++ b/src/map_draw_cairo.c
@@ -99,7 +99,7 @@ void map_draw_cairo_set_rgba(cairo_t* pCairo, color_t* pColor)
cairo_set_source_rgba(pCairo, pColor->fRed, pColor->fGreen, pColor->fBlue, pColor->fAlpha);
}
-void map_draw_cairo(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags)
+void map_draw_cairo(map_t* pMap, GPtrArray* pTiles, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags)
{
// 1. Set draw target to X Drawable
Display* dpy;
@@ -143,32 +143,38 @@ void map_draw_cairo(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPi
for(i=pMap->pLayersArray->len-1 ; i>=0 ; i--) {
maplayer_t* pLayer = g_ptr_array_index(pMap->pLayersArray, i);
+ gint nStyleZoomLevel = g_sZoomLevels[pRenderMetrics->nZoomLevel-1].nStyleZoomLevel;
+
if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_LINES) {
if(nDrawFlags & DRAWFLAG_GEOMETRY) {
- map_draw_cairo_layer_roads(pMap, pCairo, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]);
+// map_draw_cairo_layer_roads(pMap, pCairo, pRenderMetrics,
+// pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
+// pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]);
}
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_POLYGONS) {
if(nDrawFlags & DRAWFLAG_GEOMETRY) {
- map_draw_cairo_layer_polygons(pMap, pCairo, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]);
+// map_draw_cairo_layer_polygons(pMap, pCairo, pRenderMetrics,
+// pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
+// pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]);
}
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_LINE_LABELS) {
if(nDrawFlags & DRAWFLAG_LABELS) {
- map_draw_cairo_layer_road_labels(pMap, pCairo, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]);
+ gint iTile;
+ for(iTile=0 ; iTile < pTiles->len ; iTile++) {
+ maptile_t* pTile = g_ptr_array_index(pTiles, iTile);
+ map_draw_cairo_layer_road_labels(pMap, pCairo, pRenderMetrics,
+ pTile->apMapObjectArrays[pLayer->nDataSource], // data
+ pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]);
+ }
}
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_POLYGON_LABELS) {
if(nDrawFlags & DRAWFLAG_LABELS) {
- map_draw_cairo_layer_polygon_labels(pMap, pCairo, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]);
+// map_draw_cairo_layer_polygon_labels(pMap, pCairo, pRenderMetrics,
+// pMap->apLayerData[pLayer->nDataSource]->pRoadsArray,
+// pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]);
}
}
}
@@ -246,6 +252,11 @@ void map_draw_cairo_layer_road_labels(map_t* pMap, cairo_t* pCairo, rendermetric
for(i=0 ; i<pRoadsArray->len ; i++) {
road_t* pRoad = g_ptr_array_index(pRoadsArray, i);
+
+ if(!map_rects_overlap(&(pRoad->rWorldBoundingBox), &(pRenderMetrics->rWorldBoundingBox))) {
+ continue;
+ }
+
if(pRoad->pszName[0] != '\0') {
map_draw_cairo_road_label(pMap, pCairo, pLayerStyle, pRenderMetrics, pRoad->pMapPointsArray, pRoad->pszName);
}
@@ -480,26 +491,11 @@ static void map_draw_cairo_road_label_one_segment(map_t* pMap, cairo_t *pCairo,
mappoint_t* pTmp = pMapPoint1; pMapPoint1 = pMapPoint2; pMapPoint2 = pTmp;
}
- // find extents
- gdouble fMaxLat = max(pMapPoint1->fLatitude, pMapPoint2->fLatitude);
- gdouble fMinLat = min(pMapPoint1->fLatitude, pMapPoint2->fLatitude);
- gdouble fMaxLon = max(pMapPoint1->fLongitude, pMapPoint2->fLongitude);
- gdouble fMinLon = min(pMapPoint1->fLongitude, pMapPoint2->fLongitude);
-
gdouble fX1 = SCALE_X(pRenderMetrics, pMapPoint1->fLongitude);
gdouble fY1 = SCALE_Y(pRenderMetrics, pMapPoint1->fLatitude);
gdouble fX2 = SCALE_X(pRenderMetrics, pMapPoint2->fLongitude);
gdouble fY2 = SCALE_Y(pRenderMetrics, pMapPoint2->fLatitude);
- // rectangle overlap test
- if(fMaxLat < pRenderMetrics->rWorldBoundingBox.A.fLatitude
- || fMaxLon < pRenderMetrics->rWorldBoundingBox.A.fLongitude
- || fMinLat > pRenderMetrics->rWorldBoundingBox.B.fLatitude
- || fMinLon > pRenderMetrics->rWorldBoundingBox.B.fLongitude)
- {
- return;
- }
-
gdouble fRise = fY2 - fY1;
gdouble fRun = fX2 - fX1;
gdouble fLineLengthSquared = (fRun*fRun) + (fRise*fRise);
@@ -1276,7 +1272,7 @@ void map_draw_cairo_polygon_label(map_t* pMap, cairo_t *pCairo, maplayerstyle_t*
static void map_draw_cairo_map_scale(map_t* pMap, cairo_t *pCairo, rendermetrics_t* pRenderMetrics)
{
- zoomlevel_t* pZoomLevel = &g_sZoomLevels[pRenderMetrics->nZoomLevel];
+ zoomlevel_t* pZoomLevel = &g_sZoomLevels[pRenderMetrics->nZoomLevel-1];
// Imperial
gdouble fImperialLength = 0;
diff --git a/src/map_draw_cairo.h b/src/map_draw_cairo.h
index 6e1f3d9..441d0fc 100644
--- a/src/map_draw_cairo.h
+++ b/src/map_draw_cairo.h
@@ -26,6 +26,6 @@
#include <cairo.h>
-void map_draw_cairo(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags);
+void map_draw_cairo(map_t* pMap, GPtrArray* pTiles, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags);
#endif
diff --git a/src/map_draw_gdk.c b/src/map_draw_gdk.c
index 4f53b6d..dd455bb 100644
--- a/src/map_draw_gdk.c
+++ b/src/map_draw_gdk.c
@@ -1,5 +1,5 @@
/***************************************************************************
- * map_draw_cairo.c
+ * map_draw_gdk.c
*
* Copyright 2005 Ian McIntosh
* ian_mcintosh@linuxadvocate.org
@@ -69,7 +69,24 @@ void map_draw_gdk_set_color(GdkGC* pGC, color_t* pColor)
gdk_gc_set_rgb_fg_color(pGC, &clr);
}
-void map_draw_gdk(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags)
+void map_draw_gdk_xor_rect(map_t* pMap, GdkDrawable* pTargetDrawable, screenrect_t* pRect)
+{
+ GdkGC* pGC = pMap->pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->pTargetWidget)];
+
+ GdkGCValues gcValues;
+ gdk_gc_get_values(pGC, &gcValues);
+
+ GdkColor clrWhite = {0, 32000, 32000, 32000};
+ gdk_gc_set_function(pGC, GDK_XOR);
+ gdk_gc_set_rgb_fg_color(pGC, &clrWhite);
+ gdk_draw_rectangle(pTargetDrawable, pGC, FALSE,
+ min(pRect->A.nX, pRect->B.nX), min(pRect->A.nY, pRect->B.nY), // x,y
+ map_screenrect_width(pRect), map_screenrect_height(pRect)); // w,h
+
+ gdk_gc_set_values(pGC, &gcValues, GDK_GC_FUNCTION | GDK_GC_FOREGROUND);
+}
+
+void map_draw_gdk(map_t* pMap, GPtrArray* pTiles, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags)
{
TIMER_BEGIN(maptimer, "BEGIN RENDER MAP (gdk)");
@@ -86,29 +103,40 @@ void map_draw_gdk(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixm
// 2.1. Draw Background
// map_draw_gdk_background(pMap, pPixmap);
+ gint nStyleZoomLevel = g_sZoomLevels[pRenderMetrics->nZoomLevel-1].nStyleZoomLevel;
+
// 2.2. Draw layer list in reverse order (painter's algorithm: http://en.wikipedia.org/wiki/Painter's_algorithm )
for(i=pMap->pLayersArray->len-1 ; i>=0 ; i--) {
maplayer_t* pLayer = g_ptr_array_index(pMap->pLayersArray, i);
if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_FILL) {
map_draw_gdk_layer_fill(pMap, pPixmap, pRenderMetrics,
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]); // style
+ pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]); // style
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_LINES) {
- map_draw_gdk_layer_lines(pMap, pPixmap, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray, // data
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]); // style
+ gint iTile;
+ for(iTile=0 ; iTile < pTiles->len ; iTile++) {
+ maptile_t* pTile = g_ptr_array_index(pTiles, iTile);
+ map_draw_gdk_layer_lines(pMap, pPixmap, pRenderMetrics,
+ pTile->apMapObjectArrays[pLayer->nDataSource], // data
+ pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]); // style
+ }
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_POLYGONS) {
- map_draw_gdk_layer_polygons(pMap, pPixmap, pRenderMetrics,
- pMap->apLayerData[pLayer->nDataSource]->pRoadsArray, // data
- pLayer->paStylesAtZoomLevels[pRenderMetrics->nZoomLevel-1]); // style
+ gint iTile;
+ for(iTile=0 ; iTile < pTiles->len ; iTile++) {
+ maptile_t* pTile = g_ptr_array_index(pTiles, iTile);
+ map_draw_gdk_layer_polygons(pMap, pPixmap, pRenderMetrics,
+ pTile->apMapObjectArrays[pLayer->nDataSource], // data
+ pLayer->paStylesAtZoomLevels[nStyleZoomLevel-1]); // style
+ }
}
else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_LOCATIONS) {
- map_draw_gdk_locations(pMap, pPixmap, pRenderMetrics);
+// map_draw_gdk_locations(pMap, pPixmap, pRenderMetrics);
}
- else if(pLayer->nDrawType == MAP_LAYER_RENDERTYPE_LOCATION_LABELS) {
- //map_draw_gdk_locations(pMap, pPixmap, pRenderMetrics);
+ else {
+// g_print("pLayer->nDrawType = %d\n", pLayer->nDrawType);
+// g_assert_not_reached();
}
}
}
@@ -148,7 +176,6 @@ static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, renderm
map_draw_gdk_set_color(pGC, &(pLayerStyle->clrPrimary));
}
-
for(iString=0 ; iString<pRoadsArray->len ; iString++) {
pRoad = g_ptr_array_index(pRoadsArray, iString);
@@ -156,25 +183,27 @@ static void map_draw_gdk_layer_polygons(map_t* pMap, GdkPixmap* pPixmap, renderm
continue;
}
- if(pRoad->pMapPointsArray->len >= 2) {
- GdkPoint aPoints[MAX_GDK_LINE_SEGMENTS];
+ if(pRoad->pMapPointsArray->len < 3) {
+ //g_warning("not drawing polygon with < 3 points\n");
+ continue;
+ }
- if(pRoad->pMapPointsArray->len > MAX_GDK_LINE_SEGMENTS) {
- g_warning("not drawing line with > %d segments\n", MAX_GDK_LINE_SEGMENTS);
- continue;
- }
+ if(pRoad->pMapPointsArray->len > MAX_GDK_LINE_SEGMENTS) {
+ //g_warning("not drawing polygon with > %d points\n", MAX_GDK_LINE_SEGMENTS);
+ continue;
+ }
- // XXX: the bounding box should be pre-calculated!!!!
- for(iPoint=0 ; iPoint<pRoad->pMapPointsArray->len ; iPoint++) {
- pPoint = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint);
+ GdkPoint aPoints[MAX_GDK_LINE_SEGMENTS];
- aPoints[iPoint].x = pLayerStyle->nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->fLongitude);
- aPoints[iPoint].y = pLayerStyle->nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->fLatitude);
- }
+ for(iPoint=0 ; iPoint<pRoad->pMapPointsArray->len ; iPoint++) {
+ pPoint = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint);
+
+ aPoints[iPoint].x = pLayerStyle->nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->fLongitude);
+ aPoints[iPoint].y = pLayerStyle->nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->fLatitude);
+ }
- gdk_draw_polygon(pPixmap, pMap->pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->pTargetWidget)],
- TRUE, aPoints, pRoad->pMapPointsArray->len);
- }
+ gdk_draw_polygon(pPixmap, pMap->pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->pTargetWidget)],
+ TRUE, aPoints, pRoad->pMapPointsArray->len);
}
if(pLayerStyle->pGlyphFill != NULL) {
// Restore fill style
@@ -259,23 +288,26 @@ static void map_draw_gdk_layer_lines(map_t* pMap, GdkPixmap* pPixmap, rendermetr
}
if(pRoad->pMapPointsArray->len > MAX_GDK_LINE_SEGMENTS) {
- //g_warning("not drawing line with > %d segments\n", MAX_GDK_LINE_SEGMENTS);
+ //g_warning("not drawing line with > %d points\n", MAX_GDK_LINE_SEGMENTS);
+ continue;
+ }
+
+ if(pRoad->pMapPointsArray->len < 2) {
+ //g_warning("not drawing line with < 2 points\n");
continue;
}
- if(pRoad->pMapPointsArray->len >= 2) {
- // Copy all points into this array. Yuuup this is slow. :)
- GdkPoint aPoints[MAX_GDK_LINE_SEGMENTS];
+ // Copy all points into this array. Yuuup this is slow. :)
+ GdkPoint aPoints[MAX_GDK_LINE_SEGMENTS];
- for(iPoint=0 ; iPoint<pRoad->pMapPointsArray->len ; iPoint++) {
- pPoint = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint);
+ for(iPoint=0 ; iPoint<pRoad->pMapPointsArray->len ; iPoint++) {
+ pPoint = &g_array_index(pRoad->pMapPointsArray, mappoint_t, iPoint);
- aPoints[iPoint].x = pLayerStyle->nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->fLongitude);
- aPoints[iPoint].y = pLayerStyle->nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->fLatitude);
- }
+ aPoints[iPoint].x = pLayerStyle->nPixelOffsetX + (gint)SCALE_X(pRenderMetrics, pPoint->fLongitude);
+ aPoints[iPoint].y = pLayerStyle->nPixelOffsetY + (gint)SCALE_Y(pRenderMetrics, pPoint->fLatitude);
+ }
- gdk_draw_lines(pPixmap, pMap->pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->pTargetWidget)], aPoints, pRoad->pMapPointsArray->len);
- }
+ gdk_draw_lines(pPixmap, pMap->pTargetWidget->style->fg_gc[GTK_WIDGET_STATE(pMap->pTargetWidget)], aPoints, pRoad->pMapPointsArray->len);
}
}
diff --git a/src/map_draw_gdk.h b/src/map_draw_gdk.h
index ccf0bdc..85240b4 100644
--- a/src/map_draw_gdk.h
+++ b/src/map_draw_gdk.h
@@ -26,6 +26,7 @@
#include <gdk/gdk.h>
-void map_draw_gdk(map_t* pMap, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags);
+void map_draw_gdk(map_t* pMap, GPtrArray* pTiles, rendermetrics_t* pRenderMetrics, GdkPixmap* pPixmap, gint nDrawFlags);
+void map_draw_gdk_xor_rect(map_t* pMap, GdkDrawable* pTargetDrawable, screenrect_t* pRect);
#endif
diff --git a/src/map_style.c b/src/map_style.c
index 7c508eb..bcf4870 100644
--- a/src/map_style.c
+++ b/src/map_style.c
@@ -30,6 +30,9 @@
#include "glyph.h"
#include "map_style.h"
+#define MIN_STYLE_LEVEL (1)
+#define MAX_STYLE_LEVEL (10)
+
// utility functions for iterating through lists of XML things
#define EACH_ATTRIBUTE_OF_NODE(a,n) (a) = (n)->properties ; (a) != NULL ; (a) = (a)->next
#define EACH_CHILD_OF_NODE(c,n) (c) = (n)->children ; (c) != NULL ; (c) = (c)->next
@@ -127,12 +130,12 @@ gboolean map_style_parse_zoomlevel(const gchar* pszZoomLevel, gint* pnReturnMinZ
nMin = nMax = atoi(pszStr);
}
- if(nMin < MIN_ZOOM_LEVEL || nMin > MAX_ZOOM_LEVEL) {
- g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
+ if(nMin < MIN_STYLE_LEVEL || nMin > MAX_STYLE_LEVEL) {
+ g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_STYLE_LEVEL, MAX_STYLE_LEVEL);
bReturn = FALSE;
}
- else if(nMax < MIN_ZOOM_LEVEL || nMax > MAX_ZOOM_LEVEL) {
- g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
+ else if(nMax < MIN_STYLE_LEVEL || nMax > MAX_STYLE_LEVEL) {
+ g_warning("zoom-level '%s' out of valid range (%d to %d)\n", pszZoomLevel, MIN_STYLE_LEVEL, MAX_STYLE_LEVEL);
bReturn = FALSE;
}
else {
@@ -375,8 +378,8 @@ map_style_parse_layer_property(map_t* pMap, xmlDocPtr pDoc, maplayer_t *pLayer,
}
}
- gint nMinZoomLevel = MIN_ZOOM_LEVEL;
- gint nMaxZoomLevel = MAX_ZOOM_LEVEL;
+ gint nMinZoomLevel = MIN_STYLE_LEVEL;
+ gint nMaxZoomLevel = MAX_STYLE_LEVEL;
if(pszZoomLevel != NULL) {
map_style_parse_zoomlevel(pszZoomLevel, &nMinZoomLevel, &nMaxZoomLevel);
}
diff --git a/src/map_tile.c b/src/map_tile.c
deleted file mode 100644
index 80da339..0000000
--- a/src/map_tile.c
+++ /dev/null
@@ -1,4 +0,0 @@
-void map_tile_load()
-{
-
-}
diff --git a/src/map_tile.h b/src/map_tile.h
deleted file mode 100644
index 8b13789..0000000
--- a/src/map_tile.h
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/map_tilemanager.c b/src/map_tilemanager.c
new file mode 100644
index 0000000..d5d75ec
--- /dev/null
+++ b/src/map_tilemanager.c
@@ -0,0 +1,404 @@
+/***************************************************************************
+ * map_tilemanager.c
+ *
+ * Copyright 2005 Ian McIntosh
+ * ian_mcintosh@linuxadvocate.org
+ ****************************************************************************/
+
+/*
+ * 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 Library 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 <gtk/gtk.h>
+#include "util.h"
+#include "map_tilemanager.h"
+#include "db.h"
+#include "road.h"
+
+// Prototypes
+static void _map_tilemanager_tile_load_map_objects(maptile_t* pTile, maprect_t* pRect, gint nLOD);
+static maptile_t* map_tilemanager_tile_cache_lookup(maptilemanager_t* pTileManager, maprect_t* pRect, gint nLOD);
+static maptile_t* map_tilemanager_tile_new(maptilemanager_t* pTileManager, maprect_t* pRect, gint nLOD);
+
+struct {
+ gdouble fShift; // the units we care about (eg. 1000 = 1000ths of a degree)
+ gint nModulus; // how many of the above units each tile is on a side
+ gdouble fWidth; // width and height of a tile, in degrees
+} g_aTileSizeAtLevelOfDetail[MAP_NUM_LEVELS_OF_DETAIL] = {
+ {1000.0, 70, 70.0 / 1000.0},
+ {100.0, 35, 35.0 / 100.0},
+ {10.0, 35, 35.0 / 10.0},
+ {1.0, 100, 100.0 / 1.0},
+};
+
+// Public API
+maptilemanager_t* map_tilemanager_new()
+{
+ maptilemanager_t* pNew = g_new0(maptilemanager_t, 1);
+
+ gint i;
+ for(i=0 ; i<MAP_NUM_LEVELS_OF_DETAIL ; i++) {
+ pNew->apTileCachedArrays[i] = g_ptr_array_new();
+ }
+ return pNew;
+}
+
+GPtrArray* map_tilemanager_load_tiles_for_worldrect(maptilemanager_t* pTileManager, maprect_t* pRect, gint nLOD)
+{
+ //
+ // Break the worldrect up into the aligned squares that we load
+ //
+ gdouble fTileShift = g_aTileSizeAtLevelOfDetail[nLOD].fShift;
+ gint nTileModulus = g_aTileSizeAtLevelOfDetail[nLOD].nModulus;
+ gdouble fTileWidth = g_aTileSizeAtLevelOfDetail[nLOD].fWidth;
+
+ gint32 nLatStart = (gint32)(pRect->A.fLatitude * fTileShift);
+ // round it DOWN (south)
+ if(pRect->A.fLatitude > 0) {
+ nLatStart -= (nLatStart % nTileModulus);
+ }
+ else {
+ nLatStart -= (nLatStart % nTileModulus);
+ nLatStart -= nTileModulus;
+ }
+
+ gint32 nLonStart = (gint32)(pRect->A.fLongitude * fTileShift);
+ // round it DOWN (west)
+ if(pRect->A.fLongitude > 0) {
+ nLonStart -= (nLonStart % nTileModulus);
+ }
+ else {
+ nLonStart -= (nLonStart % nTileModulus);
+ nLonStart -= nTileModulus;
+ }
+
+ gint32 nLatEnd = (gint32)(pRect->B.fLatitude * fTileShift);
+ // round it UP (north)
+ if(pRect->B.fLatitude > 0) {
+ nLatEnd -= (nLatEnd % nTileModulus);
+ nLatEnd += nTileModulus;
+ }
+ else {
+ nLatEnd -= (nLatEnd % nTileModulus);
+ }
+
+ gint32 nLonEnd = (gint32)(pRect->B.fLongitude * fTileShift);
+ // round it UP (east)
+ if(pRect->B.fLongitude > 0) {
+ nLonEnd -= (nLonEnd % nTileModulus);
+ nLonEnd += nTileModulus;
+ }
+ else {
+ nLonEnd -= (nLonEnd % nTileModulus);
+ }
+
+ // how many tiles are we loading in each direction?
+ gint nLatNumTiles = (nLatEnd - nLatStart) / nTileModulus;
+ gint nLonNumTiles = (nLonEnd - nLonStart) / nTileModulus;
+
+ gdouble fLatStart = (gdouble)nLatStart / fTileShift;
+ gdouble fLonStart = (gdouble)nLonStart / fTileShift;
+
+ if(fLatStart > pRect->A.fLatitude) {
+ g_print("fLatStart %f > pRect->A.fLatitude %f\n", fLatStart, pRect->A.fLatitude);
+ g_assert(fLatStart <= pRect->A.fLatitude);
+ }
+ if(fLonStart > pRect->A.fLongitude) {
+ g_print("fLonStart %f > pRect->A.fLongitude %f!!\n", fLonStart, pRect->A.fLongitude);
+ g_assert_not_reached();
+ }
+
+ GPtrArray* pTileArray = g_ptr_array_new();
+ g_assert(pTileArray);
+
+ gint nLat,nLon;
+ for(nLat = 0 ; nLat < nLatNumTiles ; nLat++) {
+ for(nLon = 0 ; nLon < nLonNumTiles ; nLon++) {
+
+ maprect_t rect;
+ rect.A.fLatitude = fLatStart + ((gdouble)(nLat) * fTileWidth);
+ rect.A.fLongitude = fLonStart + ((gdouble)(nLon) * fTileWidth);
+ rect.B.fLatitude = fLatStart + ((gdouble)(nLat+1) * fTileWidth);
+ rect.B.fLongitude = fLonStart + ((gdouble)(nLon+1) * fTileWidth);
+
+ maptile_t* pTile = map_tilemanager_tile_cache_lookup(pTileManager, &rect, nLOD);
+ if(pTile) {
+ // cache hit
+ g_ptr_array_add(pTileArray, pTile);
+ }
+ else {
+ // cache miss
+ pTile = map_tilemanager_tile_new(pTileManager, &rect, nLOD);
+ g_ptr_array_add(pTileArray, pTile);
+ }
+ }
+ }
+ return pTileArray;
+}
+
+static maptile_t* map_tilemanager_tile_new(maptilemanager_t* pTileManager, maprect_t* pRect, gint nLOD)
+{
+ //g_print("New tile for (%f,%f),(%f,%f)\n", pRect->A.fLongitude, pRect->A.fLatitude, pRect->B.fLongitude, pRect->B.fLatitude);
+
+ maptile_t* pNewTile = g_new0(maptile_t, 1);
+ memcpy(&(pNewTile->rcWorldBoundingBox), pRect, sizeof(maprect_t));
+
+ gint i;
+ for(i=0 ; i<MAP_NUM_OBJECT_TYPES ; i++) {
+ pNewTile->apMapObjectArrays[i] = g_ptr_array_new();
+ }
+ g_print("(");
+ _map_tilemanager_tile_load_map_objects(pNewTile, pRect, nLOD);
+// _map_tilemanager_tile_load_locations(pNewTile, pRect);
+ g_print(")");
+
+ // Add to cache
+ g_ptr_array_add(pTileManager->apTileCachedArrays[nLOD], pNewTile);
+ return pNewTile;
+}
+
+//
+// Private functions
+//
+static maptile_t* map_tilemanager_tile_cache_lookup(maptilemanager_t* pTileManager, maprect_t* pRect, gint nLOD)
+{
+ // XXX: this should not match on rect, it should match on LOD and Tile ID only
+ GPtrArray* pArray = pTileManager->apTileCachedArrays[nLOD];
+
+ gint i;
+ for(i=0 ; i<pArray->len ; i++) {
+ maptile_t* pTile = g_ptr_array_index(pArray, i);
+
+ if(map_math_maprects_equal(&(pTile->rcWorldBoundingBox), pRect)) {
+ //g_print("Cache hit\n");
+ return pTile;
+ }
+ }
+ //g_print("cache miss for (%f,%f),(%f,%f)\n", pRect->A.fLongitude, pRect->A.fLatitude, pRect->B.fLongitude, pRect->B.fLatitude);
+ return NULL;
+}
+
+static void _map_tilemanager_tile_load_map_objects(maptile_t* pTile, maprect_t* pRect, gint nLOD)
+{
+ db_resultset_t* pResultSet = NULL;
+ db_row_t aRow;
+
+ TIMER_BEGIN(mytimer, "BEGIN Geometry LOAD");
+
+ gchar* pszRoadTableName = g_strdup_printf("Road%d", nLOD);
+
+ // generate SQL
+ gchar azCoord1[20], azCoord2[20], azCoord3[20], azCoord4[20], azCoord5[20], azCoord6[20], azCoord7[20], azCoord8[20];
+ gchar* pszSQL;
+ pszSQL = g_strdup_printf(
+ "SELECT 0 AS ID, %s.TypeID, AsBinary(%s.Coordinates), RoadName.Name, RoadName.SuffixID %s"
+ " FROM %s "
+ " LEFT JOIN RoadName ON (%s.RoadNameID=RoadName.ID)"
+ " WHERE"
+ " MBRIntersects(GeomFromText('Polygon((%s %s,%s %s,%s %s,%s %s,%s %s))'), Coordinates)",
+
+ //pszRoadTableName, no ID column
+ pszRoadTableName, pszRoadTableName,
+
+ // Load all details for LOD 0
+ (nLOD == 0) ? ", AddressLeftStart, AddressLeftEnd, AddressRightStart, AddressRightEnd" : "",
+ pszRoadTableName, pszRoadTableName,
+
+ // upper left
+ g_ascii_dtostr(azCoord1, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord2, 20, pRect->A.fLongitude),
+ // upper right
+ g_ascii_dtostr(azCoord3, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord4, 20, pRect->B.fLongitude),
+ // bottom right
+ g_ascii_dtostr(azCoord5, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord6, 20, pRect->B.fLongitude),
+ // bottom left
+ g_ascii_dtostr(azCoord7, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord8, 20, pRect->A.fLongitude),
+ // upper left again
+ azCoord1, azCoord2);
+
+ //g_print("sql: %s\n", pszSQL);
+
+ db_query(pszSQL, &pResultSet);
+ g_free(pszSQL);
+ g_free(pszRoadTableName);
+
+ TIMER_SHOW(mytimer, "after query");
+
+ guint32 uRowCount = 0;
+ if(pResultSet) {
+ while((aRow = db_fetch_row(pResultSet))) {
+ uRowCount++;
+
+ // aRow[0] is ID
+ // aRow[1] is TypeID
+ // aRow[2] is Coordinates in mysql's text format
+ // aRow[3] is road name
+ // aRow[4] is road name suffix id
+ // aRow[5] is road address left start
+ // aRow[6] is road address left end
+ // aRow[7] is road address right start
+ // aRow[8] is road address right end
+
+ // Get layer type that this belongs on
+ gint nTypeID = atoi(aRow[1]);
+ if(nTypeID < MAP_OBJECT_TYPE_FIRST || nTypeID > MAP_OBJECT_TYPE_LAST) {
+ g_warning("geometry record '%s' has bad type '%s'\n", aRow[0], aRow[1]);
+ continue;
+ }
+
+ //road_t* pNewRoad = NULL;
+ //road_alloc(&pNewRoad);
+ road_t* pNewRoad = g_new0(road_t, 1);
+
+ // Build name by adding suffix, if one is present
+ if(aRow[3] != NULL && aRow[4] != NULL) {
+ const gchar* pszSuffix = road_suffix_itoa(atoi(aRow[4]), ROAD_SUFFIX_LENGTH_SHORT);
+ pNewRoad->pszName = g_strdup_printf("%s%s%s", aRow[3], (pszSuffix[0] != '\0') ? " " : "", pszSuffix);
+ }
+ else {
+ pNewRoad->pszName = g_strdup(""); // XXX: could we maybe not do this?
+ }
+ // We only load this st
+ if(nLOD == MAP_LEVEL_OF_DETAIL_BEST) {
+ pNewRoad->nAddressLeftStart = atoi(aRow[5]);
+ pNewRoad->nAddressLeftEnd = atoi(aRow[6]);
+ pNewRoad->nAddressRightStart = atoi(aRow[7]);
+ pNewRoad->nAddressRightEnd = atoi(aRow[8]);
+ }
+
+ // perhaps let the wkb parser create the array (at the perfect size)
+ pNewRoad->pMapPointsArray = g_array_new(FALSE, FALSE, sizeof(road_t));
+ db_parse_wkb_linestring(aRow[2], pNewRoad->pMapPointsArray, &(pNewRoad->rWorldBoundingBox));
+
+#ifdef ENABLE_RIVER_TO_LAKE_LOADTIME_HACK // XXX: combine this and above hack and you get lakes with squiggly edges. whoops. :)
+ if(nTypeID == MAP_OBJECT_TYPE_RIVER) {
+ mappoint_t* pPointA = &g_array_index(pNewRoad->pMapPointsArray, mappoint_t, 0);
+ mappoint_t* pPointB = &g_array_index(pNewRoad->pMapPointsArray, mappoint_t, pNewRoad->pMapPointsArray->len-1);
+
+ if(pPointA->fLatitude == pPointB->fLatitude && pPointA->fLongitude == pPointB->fLongitude) {
+ nTypeID = MAP_OBJECT_TYPE_LAKE;
+ }
+ }
+#endif
+
+ // Add this item to layer's list of pointstrings
+ g_ptr_array_add(pTile->apMapObjectArrays[nTypeID], pNewRoad);
+ } // end while loop on rows
+ //g_print("[%d rows]\n", uRowCount);
+ TIMER_SHOW(mytimer, "after rows retrieved");
+
+ db_free_result(pResultSet);
+ TIMER_SHOW(mytimer, "after free results");
+ TIMER_END(mytimer, "END Geometry LOAD");
+ }
+}
+
+// static gboolean map_data_load_locations(map_t* pMap, maprect_t* pRect)
+// {
+// g_return_val_if_fail(pMap != NULL, FALSE);
+// g_return_val_if_fail(pRect != NULL, FALSE);
+//
+// // if(map_get_zoomlevel(pMap) < MIN_ZOOM_LEVEL_FOR_LOCATIONS) {
+// // return TRUE;
+// // }
+//
+// TIMER_BEGIN(mytimer, "BEGIN Locations LOAD");
+//
+// // generate SQL
+// gchar* pszSQL;
+// gchar azCoord1[20], azCoord2[20], azCoord3[20], azCoord4[20], azCoord5[20], azCoord6[20], azCoord7[20], azCoord8[20];
+// pszSQL = g_strdup_printf(
+// "SELECT Location.ID, Location.LocationSetID, AsBinary(Location.Coordinates), LocationAttributeValue_Name.Value" // LocationAttributeValue_Name.Value is the "Name" field of this Location
+// " FROM Location"
+// " LEFT JOIN LocationAttributeValue AS LocationAttributeValue_Name ON (LocationAttributeValue_Name.LocationID=Location.ID AND LocationAttributeValue_Name.AttributeNameID=%d)"
+// " WHERE"
+// " MBRIntersects(GeomFromText('Polygon((%s %s,%s %s,%s %s,%s %s,%s %s))'), Coordinates)",
+// LOCATION_ATTRIBUTE_ID_NAME, // attribute ID for 'name'
+// // upper left
+// g_ascii_dtostr(azCoord1, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord2, 20, pRect->A.fLongitude),
+// // upper right
+// g_ascii_dtostr(azCoord3, 20, pRect->A.fLatitude), g_ascii_dtostr(azCoord4, 20, pRect->B.fLongitude),
+// // bottom right
+// g_ascii_dtostr(azCoord5, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord6, 20, pRect->B.fLongitude),
+// // bottom left
+// g_ascii_dtostr(azCoord7, 20, pRect->B.fLatitude), g_ascii_dtostr(azCoord8, 20, pRect->A.fLongitude),
+// // upper left again
+// azCoord1, azCoord2);
+// //g_print("sql: %s\n", pszSQL);
+//
+// db_resultset_t* pResultSet = NULL;
+// db_query(pszSQL, &pResultSet);
+// g_free(pszSQL);
+//
+// TIMER_SHOW(mytimer, "after query");
+//
+// guint32 uRowCount = 0;
+// if(pResultSet) {
+// db_row_t aRow;
+// while((aRow = db_fetch_row(pResultSet))) {
+// uRowCount++;
+//
+// // aRow[0] is ID
+// // aRow[1] is LocationSetID
+// // aRow[2] is Coordinates in mysql's binary format
+// // aRow[3] is Name
+//
+// // Get layer type that this belongs on
+// gint nLocationSetID = atoi(aRow[1]);
+//
+// // Extract
+// location_t* pNewLocation = NULL;
+// location_alloc(&pNewLocation);
+//
+// pNewLocation->nID = atoi(aRow[0]);
+//
+// // Parse coordinates
+// db_parse_wkb_point(aRow[2], &(pNewLocation->Coordinates));
+//
+// // make a copy of the name field, or "" (never leave it == NULL)
+// pNewLocation->pszName = g_strdup(aRow[3] != NULL ? aRow[3] : "");
+// map_store_location(pMap, pNewLocation, nLocationSetID);
+// } // end while loop on rows
+// //g_print("[%d rows]\n", uRowCount);
+// TIMER_SHOW(mytimer, "after rows retrieved");
+//
+// db_free_result(pResultSet);
+// TIMER_END(mytimer, "END Locations LOAD");
+// return TRUE;
+// }
+// else {
+// TIMER_END(mytimer, "END Locations LOAD (0 results)");
+// return FALSE;
+// }
+// }
+//
+// static void map_data_clear(map_t* pMap)
+// {
+// // Clear layers
+// gint i,j;
+// for(i=0 ; i<G_N_ELEMENTS(pMap->apLayerData) ; i++) {
+// maplayer_data_t* pLayerData = pMap->apLayerData[i];
+//
+// // Free each
+// for(j = (pLayerData->pRoadsArray->len - 1) ; j>=0 ; j--) {
+// road_t* pRoad = g_ptr_array_remove_index_fast(pLayerData->pRoadsArray, j);
+// g_array_free(pRoad->pMapPointsArray, TRUE);
+// g_free(pRoad);
+// }
+// g_assert(pLayerData->pRoadsArray->len == 0);
+// }
+//
+// // Clear locations
+// map_init_location_hash(pMap);
+// }
diff --git a/src/map_tilemanager.h b/src/map_tilemanager.h
new file mode 100644
index 0000000..f00fd22
--- /dev/null
+++ b/src/map_tilemanager.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * map_tilemanager.h
+ *
+ * Copyright 2005 Ian McIntosh
+ * ian_mcintosh@linuxadvocate.org
+ ****************************************************************************/
+
+/*
+ * 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 Library 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.
+ */
+
+#ifndef _MAP_TILEMANAGER_H_
+#define _MAP_TILEMANAGER_H_
+
+#include <gtk/gtk.h>
+
+typedef struct {
+ GPtrArray* apTileCachedArrays[4]; // MAP_NUM_LEVELS_OF_DETAIL
+} maptilemanager_t;
+
+#include "map.h"
+
+typedef struct {
+ maprect_t rcWorldBoundingBox;
+ GPtrArray* apMapObjectArrays[ MAP_NUM_OBJECT_TYPES + 1 ];
+} maptile_t;
+
+maptilemanager_t* map_tilemanager_new();
+
+// returns GArray containing maptile_t types
+GPtrArray* map_tilemanager_load_tiles_for_worldrect(maptilemanager_t* pTileManager, maprect_t* pWorldRect, gint nLOD);
+
+#endif
diff --git a/src/road.c b/src/road.c
index 8833101..5d5c5a0 100644
--- a/src/road.c
+++ b/src/road.c
@@ -22,11 +22,7 @@
*/
#include <gtk/gtk.h>
-#include "main.h"
#include "road.h"
-#include "util.h"
-#include "map.h"
-#include "gfreelist.h"
struct {
gchar* pszLong;
diff --git a/src/scenemanager.c b/src/scenemanager.c
index 2cd5646..9c91650 100644
--- a/src/scenemanager.c
+++ b/src/scenemanager.c
@@ -21,23 +21,18 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <gtk/gtk.h>
-
-#include "main.h"
-#include "scenemanager.h"
-
-#define ENABLE_NO_DUPLICATE_LABELS
-
/*
Goals:
- Keep text labels and other screen objects from overlapping
- Prevent the same text from showing up too often (currently not more than once)
*/
-void scenemanager_init(void)
-{
-
-}
+#include <gtk/gtk.h>
+
+#include "main.h"
+#include "scenemanager.h"
+
+#define ENABLE_NO_DUPLICATE_LABELS
void scenemanager_new(scenemanager_t** ppReturn)
{
diff --git a/src/scenemanager.h b/src/scenemanager.h
index 9cae407..831058a 100644
--- a/src/scenemanager.h
+++ b/src/scenemanager.h
@@ -38,7 +38,6 @@ typedef struct scenemanager {
GHashTable* pLabelHash;
} scenemanager_t;
-void scenemanager_init(void);
void scenemanager_new(scenemanager_t** ppReturn);
void scenemanager_set_screen_dimensions(scenemanager_t* pSceneManager, gint nWindowWidth, gint nWindowHeight);
diff --git a/src/search_road.c b/src/search_road.c
index 0724a5e..a46f16d 100644
--- a/src/search_road.c
+++ b/src/search_road.c
@@ -34,7 +34,7 @@
#include "glyph.h"
#include "searchwindow.h" // for defines about glyph size
-#define ROAD_RESULT_SUGGESTED_ZOOMLEVEL (4)
+#define ROAD_RESULT_SUGGESTED_ZOOMLEVEL (37)
#define FORMAT_ROAD_RESULT_WITHOUT_NUMBER ("%s %s\n%s")
#define FORMAT_ROAD_RESULT_WITH_NUMBER ("%d %s %s\n%s")
@@ -214,10 +214,10 @@ void search_road_on_roadsearch_struct(const roadsearch_t* pRoadSearch)
if(pRoadSearch->nNumber != ROADSEARCH_NUMBER_NONE) {
pszAddressClause = g_strdup_printf(
" AND ("
- "(%d BETWEEN Road.AddressLeftStart AND Road.AddressLeftEnd)"
- " OR (%d BETWEEN Road.AddressLeftEnd AND Road.AddressLeftStart)"
- " OR (%d BETWEEN Road.AddressRightStart AND Road.AddressRightEnd)"
- " OR (%d BETWEEN Road.AddressRightEnd AND Road.AddressRightStart)"
+ "(%d BETWEEN Road0.AddressLeftStart AND Road0.AddressLeftEnd)"
+ " OR (%d BETWEEN Road0.AddressLeftEnd AND Road0.AddressLeftStart)"
+ " OR (%d BETWEEN Road0.AddressRightStart AND Road0.AddressRightEnd)"
+ " OR (%d BETWEEN Road0.AddressRightEnd AND Road0.AddressRightStart)"
")", pRoadSearch->nNumber, pRoadSearch->nNumber,
pRoadSearch->nNumber, pRoadSearch->nNumber);
}
@@ -238,7 +238,7 @@ void search_road_on_roadsearch_struct(const roadsearch_t* pRoadSearch)
gchar* pszZIPClause;
if(pRoadSearch->pszZIPCode != NULL) {
gchar* pszSafeZIP = db_make_escaped_string(pRoadSearch->pszZIPCode);
- pszZIPClause = g_strdup_printf(" AND (Road.ZIPCodeLeft='%s' OR Road.ZIPCodeRight='%s')", pszSafeZIP, pszSafeZIP);
+ pszZIPClause = g_strdup_printf(" AND (Road0.ZIPCodeLeft='%s' OR Road0.ZIPCodeRight='%s')", pszSafeZIP, pszSafeZIP);
db_free_escaped_string(pszSafeZIP);
}
else {
@@ -247,7 +247,7 @@ void search_road_on_roadsearch_struct(const roadsearch_t* pRoadSearch)
gchar* pszCityClause;
if(pRoadSearch->nCityID != 0) {
- pszCityClause = g_strdup_printf(" AND (Road.CityLeftID=%d OR Road.CityRightID=%d)", pRoadSearch->nCityID, pRoadSearch->nCityID);
+ pszCityClause = g_strdup_printf(" AND (Road0.CityLeftID=%d OR Road0.CityRightID=%d)", pRoadSearch->nCityID, pRoadSearch->nCityID);
}
else {
pszCityClause = g_strdup("");
@@ -280,19 +280,19 @@ void search_road_on_roadsearch_struct(const roadsearch_t* pRoadSearch)
//pszRoadNameCondition = g_strdup_printf("RoadName.NameSoundex = SUBSTRING(SOUNDEX('%s') FROM 1 FOR 10)", pszSafeRoadName);
gchar* pszQuery = g_strdup_printf(
- "SELECT Road.ID, RoadName.Name, RoadName.SuffixID, AsBinary(Road.Coordinates), Road.AddressLeftStart, Road.AddressLeftEnd, Road.AddressRightStart, Road.AddressRightEnd, CityLeft.Name, CityRight.Name"
- ", StateLeft.Code, StateRight.Code, Road.ZIPCodeLeft, Road.ZIPCodeRight"
+ "SELECT 0 AS ID, RoadName.Name, RoadName.SuffixID, AsBinary(Road0.Coordinates), Road0.AddressLeftStart, Road0.AddressLeftEnd, Road0.AddressRightStart, Road0.AddressRightEnd, CityLeft.Name, CityRight.Name"
+ ", StateLeft.Code, StateRight.Code, Road0.ZIPCodeLeft, Road0.ZIPCodeRight"
" FROM RoadName"
- " LEFT JOIN Road ON (RoadName.ID=Road.RoadNameID%s)" // address # clause
+ " LEFT JOIN Road0 ON (RoadName.ID=Road0.RoadNameID%s)" // address # clause
// left side
- " LEFT JOIN City AS CityLeft ON (Road.CityLeftID=CityLeft.ID)"
+ " LEFT JOIN City AS CityLeft ON (Road0.CityLeftID=CityLeft.ID)"
" LEFT JOIN State AS StateLeft ON (CityLeft.StateID=StateLeft.ID)"
// right side
- " LEFT JOIN City AS CityRight ON (Road.CityRightID=CityRight.ID)"
+ " LEFT JOIN City AS CityRight ON (Road0.CityRightID=CityRight.ID)"
" LEFT JOIN State AS StateRight ON (CityRight.StateID=StateRight.ID)"
" WHERE %s"
// " WHERE RoadName.Name='%s'"
- " AND Road.ID IS NOT NULL" // don't include rows where the Road didn't match
+ " AND Road0.TypeID IS NOT NULL" // (any field will do) don't include rows where the Road didn't match
// begin clauses
"%s"
"%s"
@@ -353,7 +353,7 @@ void search_road_on_roadsearch_struct(const roadsearch_t* pRoadSearch)
db_parse_wkb_linestring(aRow[3], pMapPointsArray, &r);
search_road_filter_result(aRow[1], pRoadSearch->nNumber, atoi(aRow[2]), atoi(aRow[4]), atoi(aRow[5]), atoi(aRow[6]), atoi(aRow[7]), aRow[8], aRow[9], aRow[10], aRow[11], aRow[12], aRow[13], pMapPointsArray);
//g_print("%03d: Road.ID='%s' RoadName.Name='%s', Suffix=%s, L:%s-%s, R:%s-%s\n", nCount, aRow[0], aRow[1], aRow[3], aRow[4], aRow[5], aRow[6], aRow[7]);
-
+
g_array_free(pMapPointsArray, TRUE);
}
}