summaryrefslogtreecommitdiff
path: root/navit/traffic/traff_android/traffic_traff_android.c
diff options
context:
space:
mode:
Diffstat (limited to 'navit/traffic/traff_android/traffic_traff_android.c')
-rw-r--r--navit/traffic/traff_android/traffic_traff_android.c179
1 files changed, 178 insertions, 1 deletions
diff --git a/navit/traffic/traff_android/traffic_traff_android.c b/navit/traffic/traff_android/traffic_traff_android.c
index 266f51a0c..e0862ec5c 100644
--- a/navit/traffic/traff_android/traffic_traff_android.c
+++ b/navit/traffic/traff_android/traffic_traff_android.c
@@ -22,7 +22,7 @@
*
* @brief The TraFF plugin for Android
*
- * This plugin receives TraFF feeds via Android broadcasts.
+ * This plugin receives TraFF feeds via Android broadcasts and content providers.
*/
#include <string.h>
@@ -36,13 +36,31 @@
#include "item.h"
#include "attr.h"
#include "coord.h"
+#include "map.h"
+#include "route_protected.h"
+#include "route.h"
+#include "transform.h"
#include "xmlconfig.h"
#include "android.h"
#include "traffic.h"
#include "plugin.h"
#include "callback.h"
+#include "vehicle.h"
#include "debug.h"
#include "navit.h"
+#include "util.h"
+
+/**
+ * @brief Minimum area around the current position for which to retrieve traffic updates.
+ *
+ * 100000 is equivalent to around 50 km on each side of the current position. The actual subscription area
+ * can be larger, allowing for a subscription area to be kept over multiple position updates.
+ *
+ * The actual subscription area around the current location is stored in
+ * {@link struct traffic_priv::position_rect} and updated in
+ * {@link traffic_traff_android_position_callback(struct traffic_priv *, struct navit *, struct vehicle *)}.
+ */
+#define POSITION_RECT_SIZE 100000
/**
* @brief Stores information about the plugin instance.
@@ -50,13 +68,38 @@
struct traffic_priv {
struct navit * nav; /**< The navit instance */
struct callback * cbid; /**< The callback function for TraFF feeds **/
+ int position_valid; /**< Whether Navit currently has a valid position */
+ struct coord_rect * position_rect; /**< Rectangle around last known vehicle position (in `projection_mg`) */
+ struct map_selection * route_map_sel; /**< Map selection for the current route */
jclass NavitTraffClass; /**< The `NavitTraff` class */
jobject NavitTraff; /**< An instance of `NavitTraff` */
};
+void traffic_traff_android_destroy(struct traffic_priv * this_);
struct traffic_message ** traffic_traff_android_get_messages(struct traffic_priv * this_);
/**
+ * @brief Destructor.
+ */
+void traffic_traff_android_destroy(struct traffic_priv * this_) {
+ jmethodID cid;
+
+ cid = (*jnienv)->GetMethodID(jnienv, this_->NavitTraffClass, "close", "()V");
+ if (cid == NULL) {
+ dbg(lvl_error,"no method found");
+ return; /* exception thrown */
+ }
+ (*jnienv)->CallVoidMethod(jnienv, this_->NavitTraff, cid);
+
+ if (this_->position_rect)
+ g_free(this_->position_rect);
+ this_->position_rect = NULL;
+ if (this_->route_map_sel)
+ route_free_selection(this_->route_map_sel);
+ this_->route_map_sel = NULL;
+}
+
+/**
* @brief Returns an empty traffic report.
*
* @return Always `NULL`
@@ -70,6 +113,7 @@ struct traffic_message ** traffic_traff_android_get_messages(struct traffic_priv
*/
static struct traffic_methods traffic_traff_android_meth = {
traffic_traff_android_get_messages,
+ traffic_traff_android_destroy,
};
@@ -109,12 +153,133 @@ static void traffic_traff_android_on_feed_received(struct traffic_priv * this_,
/**
+ * @brief Sets the route map selection
+ *
+ * @param this_ The instance which will handle the selection update
+ */
+static void traffic_traff_android_set_selection(struct traffic_priv * this_) {
+ struct route * route;
+ struct coord_geo lu, rl;
+ gchar *filter_list;
+ jstring j_filter_list;
+ gchar *min_road_class;
+ jmethodID cid;
+
+ if (this_->route_map_sel)
+ route_free_selection(this_->route_map_sel);
+ this_->route_map_sel = NULL;
+ if (navit_get_destination_count(this_->nav) && (route = (navit_get_route(this_->nav))))
+ this_->route_map_sel = route_get_selection(route);
+
+ /* start building the filter list */
+ filter_list = g_strconcat_printf(NULL, "<filter_list>\n");
+ if (this_->position_rect) {
+ transform_to_geo(projection_mg, &this_->position_rect->lu, &lu);
+ transform_to_geo(projection_mg, &this_->position_rect->rl, &rl);
+ filter_list = g_strconcat_printf(filter_list, " <filter bbox=\"%.5f %.5f %.5f %.5f\"/>\n",
+ rl.lat, lu.lng, lu.lat, rl.lng);
+ }
+ for (struct map_selection * sel = this_->route_map_sel; sel; sel = sel->next) {
+ transform_to_geo(projection_mg, &sel->u.c_rect.lu, &lu);
+ transform_to_geo(projection_mg, &sel->u.c_rect.rl, &rl);
+ min_road_class = order_to_min_road_class(sel->order);
+ if (!min_road_class)
+ filter_list = g_strconcat_printf(filter_list, " <filter bbox=\"%.5f %.5f %.5f %.5f\"/>\n",
+ rl.lat, lu.lng, lu.lat, rl.lng);
+ else
+ filter_list = g_strconcat_printf(filter_list, " <filter min_road_class=\"%s\" bbox=\"%.5f %.5f %.5f %.5f\"/>\n",
+ min_road_class, rl.lat, lu.lng, lu.lat, rl.lng);
+ }
+ /* the trailing \0 is required for NewStringUTF */
+ filter_list = g_strconcat_printf(filter_list, "</filter_list>\0");
+ j_filter_list = (*jnienv)->NewStringUTF(jnienv, filter_list);
+ cid = (*jnienv)->GetMethodID(jnienv, this_->NavitTraffClass, "onFilterUpdate", "(Ljava/lang/String;)V");
+ if (cid)
+ (*jnienv)->CallVoidMethod(jnienv, this_->NavitTraff, cid, j_filter_list);
+ g_free(filter_list);
+}
+
+
+/**
+ * @brief Callback for destination changes
+ *
+ * @param this_ The instance which will handle the destination update
+ */
+static void traffic_traff_android_destination_callback(struct traffic_priv * this_) {
+ traffic_traff_android_set_selection(this_);
+}
+
+
+/**
+ * @brief Callback for navigation status changes
+ *
+ * This callback is necessary to force an update of existing subscriptions when Navit acquires a new
+ * position (after not having had valid position information), as the map selection will change when
+ * the current position becomes known for the first time.
+ *
+ * @param this_ The instance which will handle the navigation status update
+ * @param status The status of the navigation engine (the value of the {@code nav_status} attribute)
+ */
+static void traffic_traff_android_status_callback(struct traffic_priv * this_, int status) {
+ int new_position_valid = (status != 1);
+ if (new_position_valid && !this_->position_valid) {
+ this_->position_valid = new_position_valid;
+ traffic_traff_android_set_selection(this_);
+ } else if (new_position_valid != this_->position_valid)
+ this_->position_valid = new_position_valid;
+}
+
+
+/**
+ * @brief Callback for position changes
+ *
+ * This updates {@link struct traffic_priv::position_rect} if the vehicle has moved far enough from its
+ * center to be within {@link POSITION_RECT_SIZE} of one of its boundaries. The new rectangle is created
+ * with twice that amount of padding, allowing the vehicle to move for at least that distance before the
+ * subscription needs to be updated again.
+ *
+ * @param this_ The instance which will handle the position update
+ * @param navit The Navit instance
+ * @param vehicle The vehicle which delivered the position update and from which the position can be queried
+ */
+static void traffic_traff_android_position_callback(struct traffic_priv * this_, struct navit *navit,
+ struct vehicle *vehicle) {
+ struct attr attr;
+ struct coord c;
+ struct coord_rect cr;
+ jmethodID cid;
+ if (!vehicle_get_attr(vehicle, attr_position_coord_geo, &attr, NULL))
+ return;
+ transform_from_geo(projection_mg, attr.u.coord_geo, &c);
+ cr.lu = c;
+ cr.rl = c;
+ cr.lu.x -= POSITION_RECT_SIZE;
+ cr.rl.x += POSITION_RECT_SIZE;
+ cr.lu.y += POSITION_RECT_SIZE;
+ cr.rl.y -= POSITION_RECT_SIZE;
+ if (!this_->position_rect)
+ this_->position_rect = g_new0(struct coord_rect, 1);
+ if (!coord_rect_contains(this_->position_rect, &cr.lu) || !coord_rect_contains(this_->position_rect, &cr.rl)) {
+ cr.lu.x -= POSITION_RECT_SIZE;
+ cr.rl.x += POSITION_RECT_SIZE;
+ cr.lu.y += POSITION_RECT_SIZE;
+ cr.rl.y -= POSITION_RECT_SIZE;
+ *(this_->position_rect) = cr;
+ traffic_traff_android_set_selection(this_);
+ }
+}
+
+
+/**
* @brief Initializes a traff_android plugin
*
* @return True on success, false on failure
*/
static int traffic_traff_android_init(struct traffic_priv * this_) {
jmethodID cid;
+ struct route * route;
+ struct attr attr;
+ struct navigation * navigation;
if (!android_find_class_global("org/navitproject/navit/NavitTraff", &this_->NavitTraffClass))
return 0;
@@ -131,6 +296,15 @@ static int traffic_traff_android_init(struct traffic_priv * this_) {
if (this_->NavitTraff)
this_->NavitTraff = (*jnienv)->NewGlobalRef(jnienv, this_->NavitTraff);
+ /* register callbacks for position and destination changes */
+ navit_add_callback(this_->nav, callback_new_attr_1(callback_cast(traffic_traff_android_position_callback),
+ attr_position_coord_geo, this_));
+ navit_add_callback(this_->nav, callback_new_attr_1(callback_cast(traffic_traff_android_destination_callback),
+ attr_destination, this_));
+ if ((navigation = navit_get_navigation(this_->nav)))
+ navigation_register_callback(navigation, attr_nav_status,
+ callback_new_attr_1(callback_cast(traffic_traff_android_status_callback), attr_nav_status, this_));
+
return 1;
}
@@ -154,6 +328,9 @@ static struct traffic_priv * traffic_traff_android_new(struct navit *nav, struct
ret = g_new0(struct traffic_priv, 1);
ret->nav = nav;
ret->cbid = callback_new_1(callback_cast(traffic_traff_android_on_feed_received), ret);
+ ret->position_valid = 0;
+ ret->position_rect = NULL;
+ ret->route_map_sel = NULL;
/* TODO populate members, if any */
*meth = traffic_traff_android_meth;