Permalink
Browse files

Merge branch 'staging'

  • Loading branch information...
gravitystorm committed Apr 26, 2012
2 parents 5a0ff96 + 1068ecd commit ec9bc6f3ebd4f1f61ff3cafcb547e2f160924956
@@ -11,11 +11,13 @@
//= require knockout
//= require superbly-tagfield.min
//= require maps
+//= require map_collisions
//= require map_display
//= require map_edit
//= require map_popup
//= require map_style
//= require openlayers_pz
+//= require openlayers_flatjson
//= require ui
//= require tags
//= require library_message
@@ -0,0 +1,68 @@
+/**
+ *
+ * Displaying collision information, with popups
+ *
+ */
+
+MapCollisions = {
+ collisions_url: "<%= Geo::COLLISIONS_URL %>",
+ collisions_key: "<%= Geo::COLLISIONS_API_KEY %>",
+ map: null,
+ layer: null,
+
+ init: function(m) {
+ this.map = m;
+ l = new OpenLayers.Layer.Vector("Collisions", {
+ projection: new OpenLayers.Projection("EPSG:4326"),
+ strategies: [new OpenLayers.Strategy.BBOX({resFactor: 3, ratio: 1.5})],
+ styleMap: MapStyle.collisionStyle(),
+ protocol: new OpenLayers.Protocol.Script({
+ url: this.collisions_url,
+ format: new OpenLayers.Format.FlatJSON({
+ getResultArray : function(obj){ return obj.collisions.collision},
+ getLat : function(obj) { return obj.latitude },
+ getLon : function(obj) { return obj.longitude }
+ }),
+ params: {
+ key: this.collisions_key
+ },
+ filterToParams: function(filter, params) {
+ // serialise BBOX into n/s/e/w url parameters
+ if (filter.type === OpenLayers.Filter.Spatial.BBOX) {
+ params.w = filter.value.toArray()[0];
+ params.s = filter.value.toArray()[1];
+ params.e = filter.value.toArray()[2];
+ params.n = filter.value.toArray()[3];
+ }
+ return params;
+ }
+ })
+ });
+ this.layer = l;
+ l.setVisibility(false);
+ m.addLayer(l);
+ m.addControl(new OpenLayers.Control.SelectFeature(l, {id: 'collSelector', onSelect: MapCollisions.createPopup, onUnselect: MapCollisions.destroyPopup }));
+ m.getControl('collSelector').activate();
+ m.getControl('collSelector').handlers.feature.stopDown = false; // Allow click-drag on polygons to move the map
+ },
+
+ createPopup: function(feature) {
+ feature.popup = new OpenLayers.Popup.FramedCloud("pop",
+ feature.geometry.getBounds().getCenterLonLat(),
+ null,
+ '<h3><a href="' + feature.attributes.url + '">Collision ' + feature.attributes.accref + ' in ' + feature.attributes.accyr + '</a></h3>' +
+ '<p>Date and time: ' + feature.attributes.datetime + '</p>' +
+ '<p>Severity: ' + feature.attributes.severity + '</p>' +
+ '<p><a href="' + feature.attributes.url + '">View full, detailed report at CycleStreets</a></p>',
+ null,
+ true,
+ function() { MapCollisions.map.getControl('collSelector').unselectAll(); this.destroy(); }
+ );
+ map.addPopup(feature.popup);
+ },
+
+ destroyPopup: function(feature) {
+ feature.popup.destroy();
+ feature.popup = null;
+ }
+}
@@ -50,5 +50,17 @@ MapStyle = {
styleMap.styles["default"].addRules(this.displayRules);
styleMap.styles["select"].addRules(this.displayRules);
return styleMap;
+ },
+
+ collisionStyle: function() {
+ var lookup = {
+ fatal: { strokeColor: '#aa0000', fillColor: '#ff0000', pointRadius: 10 },
+ serious: { strokeColor: '#e44500', fillColor: '#ff8814', pointRadius: 8 },
+ slight: { strokeColor: '#a7932f', fillColor: '#fcff00', pointRadius: 6 }
+ }
+ var styleMap = new OpenLayers.StyleMap();
+ styleMap.addUniqueValueRules("default", "severity", lookup);
+ styleMap.addUniqueValueRules("select", "severity", lookup);
+ return styleMap;
}
}
@@ -0,0 +1,145 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the Clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format/JSON.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Geometry/Point.js
+ * @requires OpenLayers/Geometry/MultiPoint.js
+ * @requires OpenLayers/Geometry/LineString.js
+ * @requires OpenLayers/Geometry/MultiLineString.js
+ * @requires OpenLayers/Geometry/Polygon.js
+ * @requires OpenLayers/Geometry/MultiPolygon.js
+ * @requires OpenLayers/Console.js
+ */
+
+/**
+ * Class: OpenLayers.Format.FlatJSON
+ * Read JSON POI documents with no GeoJSON structure.
+ * Create a new parser with the
+ * <OpenLayers.Format.FlatJSON> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Format>
+ */
+OpenLayers.Format.FlatJSON = OpenLayers.Class(OpenLayers.Format, {
+
+ /**
+ * Method: getResultArray
+ * Return the Array that cointains the items from
+ * the obj. Method to be overriden
+ *
+ * Parameters:
+ * obj - {Object} An object created from a JSON document
+ *
+ * Returns:
+ * <Object> An array of items.
+ */
+ getResultArray: function(obj){ return obj.results},
+
+ /**
+ * Method: getLat
+ * Return the latitude value contained in the obj.
+ * Method to be overriden
+ *
+ * Parameters:
+ * obj - {Object} An object item from a JSON object
+ *
+ * Returns:
+ * A number
+ */
+ getLat: function(obj){ return obj.lat },
+ /**
+ * Method: getLon
+ * Return the longitude value contained in the obj.
+ * Method to be overriden
+ *
+ * Parameters:
+ * obj - {Object} An object item from a JSON object
+ *
+ * Returns:
+ * A number
+ */
+ getLon: function(obj){ return obj.lon },
+
+ read: function(json, type, filter) {
+ var results = null;
+ var obj = null;
+ if (typeof json == "string") {
+ obj = OpenLayers.Format.JSON.prototype.read.apply(this,
+ [json, filter]);
+ } else {
+ obj = json;
+ }
+ if(!obj) {
+ OpenLayers.Console.error("Bad JSON: " + json);
+ return;
+ }
+ var res = this.getResultArray(obj);
+ if (!res){
+ OpenLayers.Console.error("Can't find the results Array : "+ json);
+ return;
+ }
+ results = this.parseFeatures(res,this);
+ return results;
+ },
+
+ parseFeatures: function(obj,format){
+ var features = [];
+ for (var i = 0; i< obj.length; i++){
+ var feat = this.parseFeature(obj[i],format);
+ if (feat) features.push(feat);
+ }
+ return features;
+ },
+
+
+ /**
+ * Method: parseFeature
+ * Convert an object item from a JSON into an
+ * <OpenLayers.Feature.Vector>.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {<OpenLayers.Feature.Vector>} A feature.
+ */
+ parseFeature: function(obj,format) {
+ var feature, geometry, attributes, bbox;
+ attributes=new Object();
+ for (att in obj){
+ attributes[att]=obj[att];
+ }
+
+ try {
+ var lat, lon;
+ lat = format.getLat(obj);
+ lon = format.getLon(obj);
+ if (isNaN(parseFloat(lat)) || !isFinite(lat)) return;
+ if (isNaN(parseFloat(lon)) || !isFinite(lon)) return;
+ geometry = new OpenLayers.Geometry.Point(lon,lat);
+ } catch(err) {
+ // deal with bad geometries
+ //throw err;
+ return;
+ }
+
+ bbox = (geometry && geometry.bbox) || obj.bbox;
+
+ feature = new OpenLayers.Feature.Vector(geometry, attributes);
+ if(bbox) {
+ feature.bounds = OpenLayers.Bounds.fromArray(bbox);
+ }
+ if(obj.id) {
+ feature.fid = obj.id;
+ }
+ return feature;
+ },
+
+
+ CLASS_NAME: "OpenLayers.Format.FlatJSON"
+
+});
@@ -12,5 +12,8 @@ def show
group_threads = ThreadList.recent_from_groups(current_user.groups, 8)
@group_threads = ThreadListDecorator.decorate(group_threads)
+
+ deadline_threads = ThreadList.with_upcoming_deadlines(current_user, 8)
+ @deadline_threads = ThreadListDecorator.decorate(deadline_threads)
end
end
@@ -1,12 +1,21 @@
class ThreadListDecorator < ApplicationDecorator
+ MESSAGE_ICON_MAP = {
+ "photo_message" => "image",
+ "link_message" => "link",
+ "deadline_message" => "cal",
+ "library_item_message" => "library_document",
+ "document_message" => "library_document",
+ "message" => "library_note"
+ }
+
def thread
@model
end
def latest_activity
latest = thread.latest_message
h.content_tag(:ul, class: "content-icon-list") do
- h.content_tag(:li) do
+ h.content_tag(:li, class: MESSAGE_ICON_MAP[latest.component_name]) do
creator_link = h.link_to_profile(latest.created_by)
h.t("dashboards.show.posted.#{latest.component_name}_html", creator_link: creator_link)
end
@@ -100,4 +100,8 @@ def add_location_layer(name, url, strategy, map, page)
strategies: [strategy]))
page << map.add_layer(locationlayer)
end
+
+ def add_collision_layer(map, page)
+ page << 'MapCollisions.init(map)'
+ end
end
@@ -56,6 +56,18 @@ def self.order_by_latest_message
rel.order("latest.created_at DESC")
end
+ def self.with_upcoming_deadlines
+ rel = joins("JOIN (SELECT m.thread_id, MIN(deadline) AS deadline
+ FROM messages m
+ JOIN deadline_messages dm ON m.component_id = dm.id
+ WHERE m.component_type = 'DeadlineMessage'
+ AND dm.deadline > now()
+ GROUP BY m.thread_id)
+ AS m2
+ ON m2.thread_id = message_threads.id")
+ rel.order("m2.deadline ASC")
+ end
+
def add_subscriber(user)
found = user.thread_subscriptions.to(self)
if found
@@ -119,6 +131,13 @@ def latest_activity_at
messages.empty? ? updated_at : messages.last.updated_at
end
+ def upcoming_deadline_messages
+ messages.except(:order).joins("JOIN deadline_messages dm ON messages.component_id = dm.id").
+ where("messages.component_type = 'DeadlineMessage'").
+ where("dm.deadline > now()").
+ order("dm.deadline ASC")
+ end
+
def priority_for(user)
user_priorities.where(user_id: user.id).first
end
@@ -3,6 +3,7 @@
%ul.tabs.styled-tabs
%li= link_to t(".my_threads"), "#my-threads"
%li= link_to t(".issues_nearby"), "#issues-nearby"
+ %li= link_to t(".deadlines"), "#deadlines"
.panes
#my-threads
@@ -50,3 +51,24 @@
%p= link_to t(".update_your_locations"), user_locations_path
%ul.issue-list
= issues_list @relevant_issues, collapsed: true
+ #deadlines
+ %section.threads
+ - if @deadline_threads.empty?
+ %p= t ".no_upcoming_deadline_threads"
+ - else
+ %table.dashboard-threads
+ %tbody
+ - @deadline_threads.each do |thread|
+ %tr
+ %td.title
+ %h4= link_to thread.title, thread_path(thread)
+ - if thread.has_issue?
+ %p= thread.issue_link
+ %td.activity
+ %ul.content-icon-list
+ - thread.upcoming_deadline_messages.each do |message|
+ %li.cal
+ %h4= l message.component.deadline.to_date, format: :long
+ %p= message.component.title
+ %td.meta
+ = thread.latest_activity_date
@@ -1,3 +1,4 @@
- m = display_map(issue, geometry_issue_path(issue, :json)) do |map, page|
+ - add_collision_layer(map, page)
%div#map
!= m.to_html
@@ -4,4 +4,7 @@ module Geo
MAP_SEARCH_ZOOM = 14
# If you see this place, then there's a better choice of places to use.
NOWHERE_IN_PARTICULAR = RGeo::Geos::Factory.create({has_z_coordinate: true}).point(-1, 53, 6)
+
+ COLLISIONS_API_KEY = 'b7af2f6899b5d784'
+ COLLISIONS_URL = 'http://cyclestreets.net/api/collisions.json'
end
@@ -1,4 +1,6 @@
module MapLayers
- OPENCYCLEMAP = OpenLayers::Layer::OSM.new("OpenCycleMap", ["a", "b", "c"].map {|k| "http://#{k}.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"}, {opacity: 0.8})
- OS_STREETVIEW = OpenLayers::Layer::OSM.new("OS StreetView", ["a", "b", "c"].map {|k| "http://#{k}.os.openstreetmap.org/sv/${z}/${x}/${y}.png"}, {opacity: 0.8})
+ OPENCYCLEMAP = OpenLayers::Layer::OSM.new("OpenCycleMap", ["a", "b", "c"].map {|k| "http://#{k}.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"},
+ {opacity: 0.8, tileOptions: {crossOriginKeyword: :null}})
+ OS_STREETVIEW = OpenLayers::Layer::OSM.new("OS StreetView", ["a", "b", "c"].map {|k| "http://#{k}.os.openstreetmap.org/sv/${z}/${x}/${y}.png"},
+ {opacity: 0.8, tileOptions: {crossOriginKeyword: :null}})
end
Oops, something went wrong.

0 comments on commit ec9bc6f

Please sign in to comment.