diff options
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Reports.pm | 30 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Bromley.pm | 131 | ||||
-rw-r--r-- | t/cobrand/bromley.t | 12 | ||||
-rw-r--r-- | templates/web/bromley/about/heatmap-list.html | 28 | ||||
-rwxr-xr-x | templates/web/bromley/about/heatmap.html | 50 | ||||
-rw-r--r-- | templates/web/bromley/footer_extra_js.html | 5 | ||||
-rw-r--r-- | web/cobrands/bromley/HeatmapLayer.js | 294 | ||||
-rw-r--r-- | web/cobrands/bromley/js.js | 75 | ||||
-rw-r--r-- | web/js/map-OpenLayers.js | 9 |
9 files changed, 623 insertions, 11 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 85848a181..b77e89d0e 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -118,7 +118,7 @@ sub ward : Path : Args(2) { $c->forward('/auth/get_csrf_token'); - my @wards = split /\|/, $ward || ""; + my @wards = $c->get_param('wards') ? $c->get_param_list('wards', 1) : split /\|/, $ward || ""; $c->forward( 'body_check', [ $body ] ); # If viewing multiple wards, rewrite the url from @@ -154,7 +154,8 @@ sub ward : Path : Args(2) { $c->forward( 'load_and_group_problems' ); if ($c->get_param('ajax')) { - $c->detach('ajax', [ 'reports/_problem-list.html' ]); + my $ajax_template = $c->stash->{ajax_template} || 'reports/_problem-list.html'; + $c->detach('ajax', [ $ajax_template ]); } $c->stash->{rss_url} = '/rss/reports/' . $body_short; @@ -164,14 +165,14 @@ sub ward : Path : Args(2) { $c->stash->{stats} = $c->cobrand->get_report_stats(); my @categories = $c->stash->{body}->contacts->not_deleted->search( undef, { - columns => [ 'category', 'extra' ], + columns => [ 'id', 'category', 'extra' ], distinct => 1, order_by => [ 'category' ], } )->all; $c->stash->{filter_categories} = \@categories; $c->stash->{filter_category} = { map { $_ => 1 } $c->get_param_list('filter_category', 1) }; - my $pins = $c->stash->{pins}; + my $pins = $c->stash->{pins} || []; my %map_params = ( latitude => @$pins ? $pins->[0]{latitude} : 0, @@ -554,7 +555,7 @@ sub load_and_group_problems : Private { my $states = $c->stash->{filter_problem_states}; my $where = { - state => [ keys %$states ] + 'me.state' => [ keys %$states ] }; $c->forward('check_non_public_reports_permission', [ $where ] ); @@ -613,12 +614,21 @@ sub load_and_group_problems : Private { $where->{longitude} = { '>=', $min_lon, '<', $max_lon }; } - $problems = $problems->search( - $where, - $filter - )->include_comment_counts->page( $page ); + my $cobrand_problems = $c->cobrand->call_hook('munge_load_and_group_problems', $where, $filter); - $c->stash->{pager} = $problems->pager; + # JS will request the same (or more) data client side + return if $c->get_param('js'); + + if ($cobrand_problems) { + $problems = $cobrand_problems; + } else { + $problems = $problems->search( + $where, + $filter + )->include_comment_counts->page( $page ); + + $c->stash->{pager} = $problems->pager; + } my ( %problems, @pins ); while ( my $problem = $problems->next ) { diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index 3ae8ef140..55a366b60 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -3,6 +3,7 @@ use parent 'FixMyStreet::Cobrand::UKCouncils'; use strict; use warnings; +use utf8; use DateTime::Format::W3CDTF; sub council_area_id { return 2482; } @@ -139,6 +140,39 @@ sub tweak_all_reports_map { $c->stash->{map}->{any_zoom} = 0; $c->stash->{map}->{zoom} = 11; } + + # A place where this can happen + return unless $c->stash->{template} && $c->stash->{template} eq 'about/heatmap.html'; + + my $children = $c->stash->{body}->first_area_children; + foreach (values %$children) { + $_->{url} = $c->uri_for( $c->stash->{body_url} + . '/' . $c->cobrand->short_name( $_ ) + ); + } + $c->stash->{children} = $children; + + my %subcats = $self->subcategories; + my $filter = $c->stash->{filter_categories}; + my @new_contacts; + foreach (@$filter) { + push @new_contacts, $_; + foreach (@{$subcats{$_->id}}) { + push @new_contacts, { + category => $_->{key}, + category_display => (" " x 4) . $_->{name}, + }; + } + } + $c->stash->{filter_categories} = \@new_contacts; + + if (!%{$c->stash->{filter_category}}) { + my $cats = $c->user->categories; + my $subcats = $c->user->get_extra_metadata('subcategories') || []; + $c->stash->{filter_category} = { map { $_ => 1 } @$cats, @$subcats } if @$cats || @$subcats; + } + + $c->stash->{ward_hash} = { map { $_->{id} => 1 } @{$c->stash->{wards}} } if $c->stash->{wards}; } sub title_list { @@ -300,5 +334,102 @@ sub add_admin_subcategories { return \@new_contacts; } +sub about_hook { + my $self = shift; + my $c = $self->{c}; + + # Display a special custom dashboard page, with heatmap + if ($c->stash->{template} eq 'about/heatmap.html') { + $c->forward('/dashboard/check_page_allowed'); + # We want a special sidebar + $c->stash->{ajax_template} = "about/heatmap-list.html"; + $c->set_param('js', 1) unless $c->get_param('ajax'); # Want to load pins client-side + $c->forward('/reports/body', [ 'Bromley' ]); + } +} + +# On heatmap page, include querying on subcategories, wards, dates, provided +sub munge_load_and_group_problems { + my ($self, $where, $filter) = @_; + my $c = $self->{c}; + + return unless $c->stash->{template} && $c->stash->{template} eq 'about/heatmap.html'; + + if (!$where->{category}) { + my $cats = $c->user->categories; + my $subcats = $c->user->get_extra_metadata('subcategories') || []; + $where->{category} = [ @$cats, @$subcats ] if @$cats || @$subcats; + } + + my %subcats = $self->subcategories; + my $subcat; + my %chosen = map { $_ => 1 } @{$where->{category} || []}; + my @subcat = grep { $chosen{$_} } map { $_->{key} } map { @$_ } values %subcats; + if (@subcat) { + my %chosen = map { $_ => 1 } @subcat; + $where->{'-or'} = { + category => [ grep { !$chosen{$_} } @{$where->{category}} ], + subcategory => \@subcat, + }; + delete $where->{category}; + } + + # Wards + my @areas = @{$c->user->area_ids || []}; + # Want to get everything if nothing given in an ajax call + if (!$c->stash->{wards} && @areas) { + $c->stash->{wards} = [ map { { id => $_ } } @areas ]; + $where->{areas} = [ + map { { 'like', '%,' . $_ . ',%' } } @areas + ]; + } + + # Date range + my $dtf = $c->model('DB')->storage->datetime_parser; + my $start_default = DateTime->today(time_zone => FixMyStreet->time_zone || FixMyStreet->local_time_zone)->subtract(months => 3); + $c->stash->{start_date} = $c->get_param('start_date') || $start_default->strftime('%Y-%m-%d'); + $c->stash->{end_date} = $c->get_param('end_date'); + + my $start_date = $dtf->parse_datetime($c->stash->{start_date}); + $where->{'me.confirmed'} = { '>=', $dtf->format_datetime($start_date) }; + if (my $end_date = $c->stash->{end_date}) { + my $one_day = DateTime::Duration->new( days => 1 ); + $end_date = $dtf->parse_datetime($end_date) + $one_day; + $where->{'me.confirmed'} = [ -and => $where->{'me.confirmed'}, { '<', $dtf->format_datetime($end_date) } ]; + } + + delete $filter->{rows}; + + # Load the relevant stuff for the sidebar as well + my $problems = $self->problems->search($where, $filter); + + $c->stash->{five_newest} = [ $problems->search(undef, { + rows => 5, + order_by => { -desc => 'confirmed' }, + })->all ]; + + $c->stash->{ten_oldest} = [ $problems->search({ + 'me.state' => [ FixMyStreet::DB::Result::Problem->open_states() ], + }, { + rows => 10, + order_by => 'lastupdate', + })->all ]; + + my $params = { map { my $n = $_; s/me\./problem\./; $_ => $where->{$n} } keys %$where }; + my @c = $c->model('DB::Comment')->to_body($self->body)->search({ + %$params, + 'me.user_id' => { -not_in => [ $c->user->id, $self->body->comment_user_id ] }, + 'me.state' => 'confirmed', + }, { + columns => 'problem_id', + group_by => 'problem_id', + order_by => { -desc => \'max(me.confirmed)' }, + rows => 5, + })->all; + $c->stash->{five_commented} = [ map { $_->problem } @c ]; + + return $problems; +} + 1; diff --git a/t/cobrand/bromley.t b/t/cobrand/bromley.t index 129531fcb..a9f9fb144 100644 --- a/t/cobrand/bromley.t +++ b/t/cobrand/bromley.t @@ -228,4 +228,16 @@ subtest 'check special subcategories in admin' => sub { is_deeply $user->get_extra_metadata('subcategories'), [ 'BLUE' ]; }; +subtest 'check heatmap page' => sub { + $user->update({ area_ids => [ 60705 ] }); + FixMyStreet::override_config { + ALLOWED_COBRANDS => 'bromley', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->log_in_ok($user->email); + $mech->get_ok('/about/heatmap?end_date=2018-12-31'); + $mech->get_ok('/about/heatmap?filter_category=RED&ajax=1'); + }; +}; + done_testing(); diff --git a/templates/web/bromley/about/heatmap-list.html b/templates/web/bromley/about/heatmap-list.html new file mode 100644 index 000000000..e04df8581 --- /dev/null +++ b/templates/web/bromley/about/heatmap-list.html @@ -0,0 +1,28 @@ +<h3>Five newest reports</h3> +[% INCLUDE column problems = five_newest %] + +<h3>Five most recent commented reports<br> +<small>Not from yourself or Confirm</small> +</h3> +[% INCLUDE column problems = five_commented %] + +<h3>Ten least recently updated open reports</h3> +[% INCLUDE column problems = ten_oldest %] + +[% BLOCK column %] +<section class="full-width"> + <ul class="item-list item-list--reports"> + [% IF problems.size %] + [% FOREACH problem IN problems %] + [% INCLUDE 'reports/_list-entry.html' include_sentinfo = 1 include_lastupdate = 1 %] + [% END %] + [% ELSE %] + <li class="item-list__item item-list__item--empty"> + <p>[% loc('There are no reports to show.') %]</p> + </li> + [% END %] + </ul> +</section> +[% END %] + + diff --git a/templates/web/bromley/about/heatmap.html b/templates/web/bromley/about/heatmap.html new file mode 100755 index 000000000..0cb8bfb1b --- /dev/null +++ b/templates/web/bromley/about/heatmap.html @@ -0,0 +1,50 @@ +[% + map_js.push(version('/cobrands/bromley/HeatmapLayer.js')); + PROCESS "maps/${map.type}.html"; + SET bodyclass = 'mappage'; + INCLUDE 'header.html', + title = tprintf(loc('%s - Summary reports'), body.name) +%] + +[% map_html %] + +</div> +<div id="map_sidebar"> + <div id="side"> + + <h1 id="reports_heading"> + [% body.name %] + </h1> + +<div class="full-width"> + +[% INCLUDE "reports/_list-filters.html", use_form_wrapper = 1 %] + +<p class="report-list-filters" style='padding-top:0.25em'> + From <input type="date" id="start_date" name="start_date" class="form-control" value="[% start_date | html %]"> + To <input type="date" id="end_date" name="end_date" class="form-control" value="[% end_date | html %]"> + +<p class="report-list-filters"> +In wards <select class="form-control js-multiple" multiple id="wards" name="wards"> + [% FOR child IN children.values.sort('name') %] + [% SET aid = child.id %] + <option value="[% child.name | html %]"[% ' selected' IF ward_hash.$aid %]>[% child.name %]</option> + [% END %] +</select> + +</div> + +<p class="segmented-control segmented-control--radio"> + <input type="radio" name="heatmap" id="heatmap_yes" value="Yes"> + <label class="btn" for="heatmap_yes">Heatmap</label> + <input type="radio" name="heatmap" id="heatmap_no" value="No" checked> + <label class="btn" for="heatmap_no">Pin map</label> +</p> + +<div id="js-reports-list"> + [% INCLUDE 'about/heatmap-list.html' %] +</div> + + </div> +</div> +[% INCLUDE 'footer.html' %] diff --git a/templates/web/bromley/footer_extra_js.html b/templates/web/bromley/footer_extra_js.html index 3c8711c39..27eb438ca 100644 --- a/templates/web/bromley/footer_extra_js.html +++ b/templates/web/bromley/footer_extra_js.html @@ -3,3 +3,8 @@ version('/cobrands/bromley/a-z-nav.js'), version('/cobrands/fixmystreet-uk-councils/council_validation_rules.js'), ) %] +[% IF bodyclass.match('mappage'); + scripts.push( + version('/cobrands/bromley/js.js'), + ); +END %] diff --git a/web/cobrands/bromley/HeatmapLayer.js b/web/cobrands/bromley/HeatmapLayer.js new file mode 100644 index 000000000..93f3e84b2 --- /dev/null +++ b/web/cobrands/bromley/HeatmapLayer.js @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2010 Bjoern Hoehrmann <http://bjoern.hoehrmann.de/>. + * This module is licensed under the same terms as OpenLayers itself. + * + */ + +Heatmap = {}; + +/** + * Class: Heatmap.Source + */ +Heatmap.Source = OpenLayers.Class({ + + /** + * APIProperty: lonlat + * {OpenLayers.LonLat} location of the heat source + */ + lonlat: null, + + /** + * APIProperty: radius + * {Number} Heat source radius + */ + radius: null, + + /** + * APIProperty: intensity + * {Number} Heat source intensity + */ + intensity: null, + + /** + * Constructor: Heatmap.Source + * Create a heat source. + * + * Parameters: + * lonlat - {OpenLayers.LonLat} Coordinates of the heat source + * radius - {Number} Optional radius + * intensity - {Number} Optional intensity + */ + initialize: function(lonlat, radius, intensity) { + this.lonlat = lonlat; + this.radius = radius; + this.intensity = intensity; + }, + + CLASS_NAME: 'Heatmap.Source' +}); + +/** + * Class: Heatmap.Layer + * + * Inherits from: + * - <OpenLayers.Layer> + */ +Heatmap.Layer = OpenLayers.Class(OpenLayers.Layer, { + + /** + * APIProperty: isBaseLayer + * {Boolean} Heatmap layer is never a base layer. + */ + isBaseLayer: false, + + /** + * Property: points + * {Array(<Heatmap.Source>)} internal coordinate list + */ + points: null, + + /** + * Property: cache + * {Object} Hashtable with CanvasGradient objects + */ + cache: null, + + /** + * Property: gradient + * {Array(Number)} RGBA gradient map used to colorize the intensity map. + */ + gradient: null, + + /** + * Property: canvas + * {DOMElement} Canvas element. + */ + canvas: null, + + /** + * APIProperty: defaultRadius + * {Number} Heat source default radius + */ + defaultRadius: null, + + /** + * APIProperty: defaultIntensity + * {Number} Heat source default intensity + */ + defaultIntensity: null, + + /** + * Constructor: Heatmap.Layer + * Create a heatmap layer. + * + * Parameters: + * name - {String} Name of the Layer + * options - {Object} Hashtable of extra options to tag onto the layer + */ + initialize: function(name, options) { + OpenLayers.Layer.prototype.initialize.apply(this, arguments); + this.points = []; + this.cache = {}; + this.canvas = document.createElement('canvas'); + this.canvas.style.position = 'absolute'; + this.defaultRadius = 20; + this.defaultIntensity = 0.2; + this.setGradientStops({ + 0.00: 0x00d7ff00, // transparent cyan + 0.10: 0x00d7ffff, // cyan + 0.25: 0x008cffff, // royal blue + 0.40: 0x6600ffff, // purple + 0.55: 0xca00ffff, // fuschia pink + 0.70: 0xff0000ff, // red + 0.80: 0xff5b00ff, // orange + 0.90: 0xffff00ff, // yellow + 1.00: 0xffff00ff // yellow + }); + + // For some reason OpenLayers.Layer.setOpacity assumes there is + // an additional div between the layer's div and its contents. + var sub = document.createElement('div'); + sub.appendChild(this.canvas); + this.div.appendChild(sub); + }, + + /** + * APIMethod: setGradientStops + * ... + * + * Parameters: + * stops - {Object} Hashtable with stop position as keys and colors + * as values. Stop positions are numbers between 0 + * and 1, color values numbers in 0xRRGGBBAA form. + */ + setGradientStops: function(stops) { + + // There is no need to perform the linear interpolation manually, + // it is sufficient to let the canvas implementation do that. + + var ctx = document.createElement('canvas').getContext('2d'); + var grd = ctx.createLinearGradient(0, 0, 256, 0); + + for (var i in stops) { + grd.addColorStop(i, 'rgba(' + + ((stops[i] >> 24) & 0xFF) + ',' + + ((stops[i] >> 16) & 0xFF) + ',' + + ((stops[i] >> 8) & 0xFF) + ',' + + ((stops[i] >> 0) & 0xFF) + ')'); + } + + ctx.fillStyle = grd; + ctx.fillRect(0, 0, 256, 1); + this.gradient = ctx.getImageData(0, 0, 256, 1).data; + }, + + /** + * APIMethod: addSource + * Adds a heat source to the layer. + * + * Parameters: + * source - {<Heatmap.Source>} + */ + addSource: function(source) { + this.points.push(source); + }, + + /** + * APIMethod: removeSource + * Removes a heat source from the layer. + * + * Parameters: + * source - {<Heatmap.Source>} + */ + removeSource: function(source) { + if (this.points && this.points.length) { + OpenLayers.Util.removeItem(this.points, source); + } + }, + + /** + * Method: moveTo + * + * Parameters: + * bounds - {<OpenLayers.Bounds>} + * zoomChanged - {Boolean} + * dragging - {Boolean} + */ + moveTo: function(bounds, zoomChanged, dragging) { + + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); + + // The code is too slow to update the rendering during dragging. + if (dragging) + return; + + // Pick some point on the map and use it to determine the offset + // between the map's 0,0 coordinate and the layer's 0,0 position. + var someLoc = new OpenLayers.LonLat(0,0); + var offsetX = this.map.getViewPortPxFromLonLat(someLoc).x - + this.map.getLayerPxFromLonLat(someLoc).x; + var offsetY = this.map.getViewPortPxFromLonLat(someLoc).y - + this.map.getLayerPxFromLonLat(someLoc).y; + + this.canvas.width = this.map.getSize().w; + this.canvas.height = this.map.getSize().h; + + var ctx = this.canvas.getContext('2d'); + + ctx.save(); // Workaround for a bug in Google Chrome + ctx.fillStyle = 'transparent'; + ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + ctx.restore(); + + for (var i in this.points) { + + var src = this.points[i]; + var rad = src.radius || this.defaultRadius; + var int = src.intensity || this.defaultIntensity; + var pos = this.map.getLayerPxFromLonLat(src.lonlat); + var x = pos.x - rad + offsetX; + var y = pos.y - rad + offsetY; + + if (!this.cache[int]) { + this.cache[int] = {}; + } + + if (!this.cache[int][rad]) { + var grd = ctx.createRadialGradient(rad, rad, 0, rad, rad, rad); + grd.addColorStop(0.0, 'rgba(0, 0, 0, ' + int + ')'); + grd.addColorStop(1.0, 'transparent'); + this.cache[int][rad] = grd; + } + + ctx.fillStyle = this.cache[int][rad]; + ctx.translate(x, y); + ctx.fillRect(0, 0, 2 * rad, 2 * rad); + ctx.translate(-x, -y); + } + + var dat = ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); + var dim = this.canvas.width * this.canvas.height * 4; + var pix = dat.data; + + for (var p = 0; p < dim; /* */) { + var a = pix[ p + 3 ] * 4; + pix[ p++ ] = this.gradient[ a++ ]; + pix[ p++ ] = this.gradient[ a++ ]; + pix[ p++ ] = this.gradient[ a++ ]; + pix[ p++ ] = this.gradient[ a++ ]; + } + + ctx.putImageData(dat, 0, 0); + + // Unfortunately OpenLayers does not currently support layers that + // remain in a fixed position with respect to the screen location + // of the base layer, so this puts this layer manually back into + // that position using one point's offset as determined earlier. + this.canvas.style.left = (-offsetX) + 'px'; + this.canvas.style.top = (-offsetY) + 'px'; + }, + + /** + * APIMethod: getDataExtent + * Calculates the max extent which includes all of the heat sources. + * + * Returns: + * {<OpenLayers.Bounds>} + */ + getDataExtent: function () { + var maxExtent = null; + + if (this.points && (this.points.length > 0)) { + maxExtent = new OpenLayers.Bounds(); + for(var i = 0, len = this.points.length; i < len; ++i) { + var point = this.points[i]; + maxExtent.extend(point.lonlat); + } + } + + return maxExtent; + }, + + CLASS_NAME: 'Heatmap.Layer' + +}); diff --git a/web/cobrands/bromley/js.js b/web/cobrands/bromley/js.js new file mode 100644 index 000000000..8ff314189 --- /dev/null +++ b/web/cobrands/bromley/js.js @@ -0,0 +1,75 @@ +if (window.Heatmap) { + // We do want heatmap page to run on load... Bit cheeky + OpenLayers.Strategy.FixMyStreetNoLoad = OpenLayers.Strategy.FixMyStreet; + OpenLayers.Strategy.FixMyStreetHeatmap = OpenLayers.Class(OpenLayers.Strategy.FixMyStreet, { + // Same as update, but doesn't check layer visibility (as running when markers invisible) + update: function(options) { + var mapBounds = this.getMapBounds(); + if (mapBounds !== null && ((options && options.force) || + (this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { + this.calculateBounds(mapBounds); + this.resolution = this.layer.map.getResolution(); + this.triggerRead(options); + } + }, + CLASS_NAME: 'OpenLayers.Strategy.FixMyStreetHeatmap' + }); +} + +fixmystreet.protocol_params.wards = 'wards'; +fixmystreet.protocol_params.start_date = 'start_date'; +fixmystreet.protocol_params.end_date = 'end_date'; + +$(function(){ + if (!window.Heatmap) { + return; + } + + var heat_layer = new Heatmap.Layer("Heatmap"); + heat_layer.setOpacity(0.7); + heat_layer.setVisibility(false); + + var s = new OpenLayers.Strategy.FixMyStreetHeatmap(); + s.setLayer(heat_layer); + s.activate(); + // Now it's listening on heat layer, set it to update markers layer + s.layer = fixmystreet.markers; + + function create_heat_layer() { + heat_layer.points = []; + for (var i = 0; i < fixmystreet.markers.features.length; i++) { + var m = fixmystreet.markers.features[i]; + var ll = new OpenLayers.LonLat(m.geometry.x, m.geometry.y); + heat_layer.addSource(new Heatmap.Source(ll)); + } + heat_layer.redraw(); + } + + fixmystreet.markers.events.register('loadend', null, create_heat_layer); + create_heat_layer(); + fixmystreet.map.addLayer(heat_layer); + + $('#heatmap_yes').on('click', function() { + fixmystreet.markers.setVisibility(false); + heat_layer.setVisibility(true); + }); + + $('#heatmap_no').on('click', function() { + heat_layer.setVisibility(false); + fixmystreet.markers.setVisibility(true); + }); + + $('#sort').closest('.report-list-filters').hide(); + + $("#wards, #start_date, #end_date").on("change.filters", function() { + // If the category or status has changed we need to re-fetch map markers + fixmystreet.markers.events.triggerEvent("refresh", {force: true}); + }); + $("#filter_categories, #statuses").on("change.filters", function() { + if (!fixmystreet.markers.getVisibility()) { + // If not visible, still want to trigger change for heatmap + fixmystreet.markers.events.triggerEvent("refresh", {force: true}); + } + }); + +}); diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 984f4b098..ae86269c9 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -1055,6 +1055,13 @@ OpenLayers.Strategy.FixMyStreetFixed = OpenLayers.Class(OpenLayers.Strategy.Fixe // is dragged (modulo a buffer extending outside the viewport). // This subclass is required so we can pass the 'filter_category' and 'status' query // params to /around?ajax if the user has filtered the map. + +fixmystreet.protocol_params = { + filter_category: 'filter_categories', + status: 'statuses', + sort: 'sort' +}; + OpenLayers.Protocol.FixMyStreet = OpenLayers.Class(OpenLayers.Protocol.HTTP, { initial_page: null, use_page: false, @@ -1062,7 +1069,7 @@ OpenLayers.Protocol.FixMyStreet = OpenLayers.Class(OpenLayers.Protocol.HTTP, { read: function(options) { // Pass the values of the category, status, and sort fields as query params options.params = options.params || {}; - $.each({ filter_category: 'filter_categories', status: 'statuses', sort: 'sort' }, function(key, id) { + $.each(fixmystreet.protocol_params, function(key, id) { var val = $('#' + id).val(); if (val && val.length) { options.params[key] = val.join ? fixmystreet.utils.array_to_csv_line(val) : val; |