summaryrefslogtreecommitdiff
path: root/src/map.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map.c')
-rw-r--r--src/map.c1138
1 files changed, 209 insertions, 929 deletions
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