diff options
Diffstat (limited to 'perllib/FixMyStreet')
25 files changed, 526 insertions, 61 deletions
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index fda9d665c..9c7aba9e5 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -274,9 +274,8 @@ sub send_email { my $template = shift; my $extra_stash_values = shift || {}; - my $sender = $c->cobrand->contact_email; + my $sender = $c->config->{DO_NOT_REPLY_EMAIL}; my $sender_name = $c->cobrand->contact_name; - $sender =~ s/team/fms-DO-NOT-REPLY/; # create the vars to pass to the email template my $vars = { diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 926a3f2a5..c8b0c2046 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -184,7 +184,7 @@ generally required to stash sub setup_request : Private { my ( $self, $c ) = @_; - $c->stash->{contact_email} = $c->cobrand->contact_email( 'contact' ); + $c->stash->{contact_email} = $c->cobrand->contact_email; $c->stash->{contact_email} =~ s/\@/@/; for my $param (qw/em subject message/) { @@ -206,7 +206,7 @@ Sends the email sub send_email : Private { my ( $self, $c ) = @_; - my $recipient = $c->cobrand->contact_email( 'contact' ); + my $recipient = $c->cobrand->contact_email; my $recipient_name = $c->cobrand->contact_name(); $c->stash->{host} = $c->req->header('HOST'); diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm index e8bf2cd1c..fd3fadd9f 100644 --- a/perllib/FixMyStreet/App/Controller/Location.pm +++ b/perllib/FixMyStreet/App/Controller/Location.pm @@ -5,6 +5,7 @@ use namespace::autoclean; BEGIN {extends 'Catalyst::Controller'; } use Encode; +use FixMyStreet::Geocode; =head1 NAME diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm index 040b0d3e6..3382c0cea 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -100,7 +100,7 @@ sub error : Private { sub get_discovery : Private { my ( $self, $c ) = @_; - my $contact_email = $c->config->{CONTACT_EMAIL}; + my $contact_email = $c->cobrand->contact_email; my $prod_url = 'http://www.fiksgatami.no/open311'; my $test_url = 'http://fiksgatami-dev.nuug.no/open311'; my $prod_changeset = '2011-04-08T00:00:00Z'; diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 169c3d152..9194f5318 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -4,7 +4,6 @@ use Moose; use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } -use FixMyStreet::Geocode; use Encode; use List::MoreUtils qw(uniq); use POSIX 'strcoll'; @@ -788,6 +787,7 @@ sub process_report : Private { 'category', # 'subcategory', # 'partial', # + 'service', # ); # load the report @@ -812,6 +812,9 @@ sub process_report : Private { } $report->detail( $detail ); + # mobile device type + $report->service( $params{service} ) if $params{service}; + # set these straight from the params $report->category( _ $params{category} ) if $params{category}; @@ -936,6 +939,13 @@ sub check_for_errors : Private { $c->stash->{field_errors} ||= {}; my %field_errors = $c->cobrand->report_check_for_errors( $c ); + # Zurich, we don't care about title or name + # There is no title, and name is optional + if ( $c->cobrand->moniker eq 'zurich' ) { + delete $field_errors{title}; + delete $field_errors{name}; + } + # FIXME: need to check for required bromley fields here # if they're got the login details wrong when signing in then diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index c33135673..0d4894aa8 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -29,7 +29,7 @@ sub disambiguate_location { my $town = 'Bromley'; # Bing turns High St Bromley into Bromley High St which is in # Bromley by Bow. - if ( $string =~ /high\+st/i ) { + if ( $string =~ /high\s+st/i ) { $town .= ', BR1'; } return { @@ -80,9 +80,7 @@ sub process_extras { sub contact_email { my $self = shift; - my $type = shift || ''; - return join( '@', 'info', 'bromley.gov.uk' ) if $type eq 'contact'; - return $self->next::method(); + return join( '@', 'info', 'bromley.gov.uk' ); } sub contact_name { 'Bromley Council (do not reply)'; } diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 34f9d0b1d..1f3b4d4bb 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -4,6 +4,7 @@ use base 'FixMyStreet::Cobrand::Base'; use strict; use warnings; use FixMyStreet; +use FixMyStreet::Geocode::Bing; use Encode; use URI; use Digest::MD5 qw(md5_hex); @@ -747,6 +748,16 @@ until the contacts/area/body handling is rewritten to be better. sub reports_by_body { 0; } +=head2 default_show_name + +Returns true if the show name checkbox should be ticked by default. + +=cut + +sub default_show_name { + 1; +} + =head2 report_check_for_errors Perform validation for new reports. Takes Catalyst context object as an argument diff --git a/perllib/FixMyStreet/Cobrand/FixMyBarangay.pm b/perllib/FixMyStreet/Cobrand/FixMyBarangay.pm index c53b8e971..8ccbb57f5 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyBarangay.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyBarangay.pm @@ -43,5 +43,12 @@ sub can_support_problems { sub reports_by_body { 1 } +sub default_show_name { + my $self = shift; + + return 0 if $self->{c}->user->from_council; + return 1; +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm new file mode 100644 index 000000000..745123fdb --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -0,0 +1,65 @@ +package FixMyStreet::Cobrand::Oxfordshire; +use base 'FixMyStreet::Cobrand::UKCouncils'; + +use strict; +use warnings; + +sub council_id { return 2237; } +sub council_area { return 'Oxfordshire'; } +sub council_name { return 'Oxfordshire County Council'; } +sub council_url { return 'oxfordshire'; } + +sub base_url { + return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + return 'http://fixmystreet.oxfordshire.gov.uk'; +} + +# Different to councils parent due to this being a two-tier council. If we get +# more, this can be genericised in the parent. +sub problems_clause { + return { council => { like => '%2237%' } }; +} + +sub path_to_web_templates { + my $self = shift; + return [ + FixMyStreet->path_to( 'templates/web', $self->moniker )->stringify, + FixMyStreet->path_to( 'templates/web/fixmystreet' )->stringify + ]; +} + +sub enter_postcode_text { + my ($self) = @_; + return 'Enter an Oxfordshire postcode, or street name and area'; +} + +sub disambiguate_location { + my $self = shift; + my $string = shift; + return { + %{ $self->SUPER::disambiguate_location() }, + centre => '51.765765,-1.322324', + span => '0.154963,0.24347', # NB span is not correct + bounds => [ 51.459413, -1.719500, 52.168471, -0.870066 ], + }; +} + +sub example_places { + return ( 'OX20 1SZ', 'Park St, Woodstock' ); +} + +# If we ever link to a district problem report, needs to be to main FixMyStreet +sub base_url_for_report { + my ( $self, $report ) = @_; + my %councils = map { $_ => 1 } @{$report->councils}; + if ( $councils{2237} ) { + return $self->base_url; + } else { + return FixMyStreet->config('BASE_URL'); + } +} + +sub default_show_name { 0 } + +1; + diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm new file mode 100644 index 000000000..e5d646c8b --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -0,0 +1,16 @@ +package FixMyStreet::Cobrand::Zurich; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +sub enter_postcode_text { + my ( $self ) = @_; + return _('Enter a Zürich street name'); +} + +sub example_places { + return [ 'Langstrasse', 'Basteiplatz' ]; +} + +1; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 02e5adb7d..dd09ad3c2 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -583,7 +583,8 @@ sub body { # Note: this only makes sense when called on a problem that has been sent! sub can_display_external_id { my $self = shift; - if ($self->external_id && $self->send_method_used && $self->send_method_used eq 'barnet') { + if ($self->external_id && $self->send_method_used && + ($self->send_method_used eq 'barnet' || $self->council =~ /2237/)) { return 1; } return 0; @@ -603,7 +604,7 @@ sub processed_summary_string { my ( $problem, $c ) = @_; my ($duration_clause, $external_ref_clause); if ($problem->whensent) { - $duration_clause = $problem->duration_string($c) + $duration_clause = $problem->duration_string($c); } if ($problem->can_display_external_id) { if ($duration_clause) { diff --git a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm index 468df2654..d903f8eb2 100644 --- a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm +++ b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm @@ -230,13 +230,12 @@ sub _send_aggregated_alert_email(%) { unless -e $template; $template = Utils::read_file($template); - my $sender = $cobrand->contact_email; - (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX + my $sender = FixMyStreet->config('DO_NOT_REPLY_EMAIL'); my $result = FixMyStreet::App->send_email_cron( { _template_ => $template, _parameters_ => \%data, - From => [ $from, _($cobrand->contact_name) ], + From => [ $sender, _($cobrand->contact_name) ], To => $data{alert_email}, }, $sender, diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 0d40220b2..faed3b8ac 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -377,8 +377,33 @@ sub send_reports { if (mySociety::Config::get('STAGING_SITE')) { # on a staging server send emails to ourselves rather than the councils + # however, we can configure a list of councils that we use non email + # delivery, e.g. Open311, for testing purposes. For those we want to + # send using the non email method and for everyone else we want to use + # email my @testing_councils = split( '\|', mySociety::Config::get('TESTING_COUNCILS') ); - unless ( grep { $row->council eq $_ } @testing_councils ) { + + # we only care about non missing councils so we get the missing ones + # and then essentially throw them away as we're not going to have + # configured them to do anything. + my %councils = map { $_ => 1 } @{ $row->councils }; + + # We now take the councils that we have contact details for and if any of them + # are in the list of testing councils we look a bit harder otherwise we throw + # away all the non email delivery methods + if ( grep { $councils{ $_ } } @testing_councils ) { + my %tc = map { $_ => 1 } @testing_councils; + my @non_matching = grep { !$tc{$_} } keys %councils; + for my $sender ( keys %reporters ) { + next if $sender =~ /FixMyStreet::SendReport::(Email|NI)/; + for my $council ( @non_matching ) { + $reporters{$sender}->delete_council( $council ); + } + } + if ( @non_matching ) { + $reporters{'FixMyStreet::SendReport::Email'} = FixMyStreet::SendReport::Email->new(); + } + } else { %reporters = map { $_ => $reporters{$_} } grep { /FixMyStreet::SendReport::(Email|NI)/ } keys %reporters; unless (%reporters) { %reporters = ( 'FixMyStreet::SendReport::Email' => FixMyStreet::SendReport::Email->new() ); diff --git a/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm b/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm index d6b3eb5cb..1b9521a9f 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Questionnaire.pm @@ -89,9 +89,8 @@ sub send_questionnaires_period { } ); $h{url} = $cobrand->base_url($row->cobrand_data) . '/Q/' . $token->token; - my $sender = $cobrand->contact_email; + my $sender = FixMyStreet->config('DO_NOT_REPLY_EMAIL'); my $sender_name = _($cobrand->contact_name); - $sender =~ s/team/fms-DO-NOT-REPLY/; print "Sending questionnaire " . $questionnaire->id . ", problem " . $row->id . ", token " . $token->token . " to " diff --git a/perllib/FixMyStreet/EmailSend.pm b/perllib/FixMyStreet/EmailSend.pm index 61d8a70c2..8b6eed462 100644 --- a/perllib/FixMyStreet/EmailSend.pm +++ b/perllib/FixMyStreet/EmailSend.pm @@ -2,10 +2,7 @@ package FixMyStreet::EmailSend; use base Email::Send::SMTP; sub get_env_sender { - # Should really use cobrand's contact_email function, but not sure how - # best to access that from in here. - my $sender = FixMyStreet->config('CONTACT_EMAIL'); - $sender =~ s/team/fms-DO-NOT-REPLY/; + my $sender = FixMyStreet->config('DO_NOT_REPLY_EMAIL'); return $sender; } diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm index f92e9cc9a..6cfd960ed 100644 --- a/perllib/FixMyStreet/Geocode.pm +++ b/perllib/FixMyStreet/Geocode.pm @@ -13,6 +13,7 @@ use URI::Escape; use FixMyStreet::Geocode::Bing; use FixMyStreet::Geocode::Google; use FixMyStreet::Geocode::OSM; +use FixMyStreet::Geocode::Zurich; # lookup STRING CONTEXT # Given a user-inputted string, try and convert it into co-ordinates using either @@ -33,18 +34,27 @@ sub lookup { # Canonicalises, and then passes to some external API to look stuff up. sub string { my ($s, $c) = @_; + + my $service = $c->config->{GEOCODER}; + $service = $service->{type} if ref $service; + $service = 'OSM' unless $service =~ /^(Bing|Google|OSM|Zurich)$/; + $service = 'OSM' if $service eq 'Bing' && !FixMyStreet->config('BING_MAPS_API_KEY'); + $service = "FixMyStreet::Geocode::${service}::string"; + + no strict 'refs'; + return &$service($s, $c); +} + +# escape STRING CONTEXT +# Escapes string for putting in URL geocoding call +sub escape { + my ($s, $c) = @_; $s = lc($s); $s =~ s/[^-&\w ']/ /g; $s =~ s/\s+/ /g; $s = URI::Escape::uri_escape_utf8($s); $s =~ s/%20/+/g; - my $params = $c->cobrand->disambiguate_location($s); - return FixMyStreet::Geocode::Bing::string($s, $c, $params) - if FixMyStreet->config('BING_MAPS_API_KEY'); - # Fall back to Google API, which allow access with and without a key - return FixMyStreet::Geocode::Google::string($s, $c, $params) - if FixMyStreet->config('GOOGLE_MAPS_API_KEY'); - return FixMyStreet::Geocode::OSM::string($s, $c, $params); + return $s; } 1; diff --git a/perllib/FixMyStreet/Geocode/Bing.pm b/perllib/FixMyStreet/Geocode/Bing.pm index 18e6b56ce..85eef3d0f 100644 --- a/perllib/FixMyStreet/Geocode/Bing.pm +++ b/perllib/FixMyStreet/Geocode/Bing.pm @@ -15,14 +15,21 @@ use File::Path (); use LWP::Simple; use Digest::MD5 qw(md5_hex); +use mySociety::Locale; + # string STRING CONTEXT # Looks up on Bing Maps API, and caches, a user-inputted location. # Returns array of (LAT, LON, ERROR), where ERROR is either undef, a string, or # an array of matches if there are more than one. The information in the query # may be used to disambiguate the location in cobranded versions of the site. sub string { - my ( $s, $c, $params ) = @_; + my ( $s, $c ) = @_; + + my $params = $c->cobrand->disambiguate_location($s); + + $s = FixMyStreet::Geocode::escape($s); $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i; + my $url = "http://dev.virtualearth.net/REST/v1/Locations?q=$s"; $url .= '&userMapView=' . join(',', @{$params->{bounds}}) if $params->{bounds}; diff --git a/perllib/FixMyStreet/Geocode/Google.pm b/perllib/FixMyStreet/Geocode/Google.pm index db3a8ae91..fd65b89b1 100644 --- a/perllib/FixMyStreet/Geocode/Google.pm +++ b/perllib/FixMyStreet/Geocode/Google.pm @@ -14,6 +14,7 @@ use File::Slurp; use File::Path (); use LWP::Simple; use Digest::MD5 qw(md5_hex); +use mySociety::Locale; # string STRING CONTEXT # Looks up on Google Maps API, and caches, a user-inputted location. @@ -21,7 +22,11 @@ use Digest::MD5 qw(md5_hex); # an array of matches if there are more than one. The information in the query # may be used to disambiguate the location in cobranded versions of the site. sub string { - my ( $s, $c, $params ) = @_; + my ( $s, $c ) = @_; + + my $params = $c->cobrand->disambiguate_location($s); + + $s = FixMyStreet::Geocode::escape($s); my $url = 'http://maps.google.com/maps/geo?q=' . $s; $url .= '&ll=' . $params->{centre} if $params->{centre}; diff --git a/perllib/FixMyStreet/Geocode/OSM.pm b/perllib/FixMyStreet/Geocode/OSM.pm index d96338c16..fd14b0acc 100644 --- a/perllib/FixMyStreet/Geocode/OSM.pm +++ b/perllib/FixMyStreet/Geocode/OSM.pm @@ -29,8 +29,13 @@ my $nominatimbase = "http://nominatim.openstreetmap.org/"; # an array of matches if there are more than one. The information in the query # may be used to disambiguate the location in cobranded versions of the site. sub string { - my ( $s, $c, $params ) = @_; + my ( $s, $c ) = @_; + + my $params = $c->cobrand->disambiguate_location($s); + + $s = FixMyStreet::Geocode::escape($s); $s .= '+' . $params->{town} if $params->{town} and $s !~ /$params->{town}/i; + my $url = "${nominatimbase}search?"; my %query_params = ( q => $s, diff --git a/perllib/FixMyStreet/Geocode/Zurich.pm b/perllib/FixMyStreet/Geocode/Zurich.pm new file mode 100644 index 000000000..5aaca2c8e --- /dev/null +++ b/perllib/FixMyStreet/Geocode/Zurich.pm @@ -0,0 +1,107 @@ +#!/usr/bin/perl +# +# FixMyStreet::Geocode::Zurich +# Geocoding with Zurich web service. +# +# Thanks to http://msdn.microsoft.com/en-us/library/ms995764.aspx +# and http://noisemore.wordpress.com/2009/03/19/perl-soaplite-wsse-web-services-security-soapheader/ +# for SOAP::Lite pointers +# +# Copyright (c) 2012 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ + +package FixMyStreet::Geocode::Zurich; + +use strict; +use Digest::MD5 qw(md5_hex); +use File::Path (); +use Geo::Coordinates::CH1903; +use SOAP::Lite; +use Storable; +use mySociety::Locale; + +my ($soap, $method, $security); + +sub setup_soap { + return if $soap; + + # Variables for the SOAP web service + my $geocoder = FixMyStreet->config('GEOCODER'); + my $url = $geocoder->{url}; + my $username = $geocoder->{username}; + my $password = $geocoder->{password}; + my $attr = 'http://ch/geoz/fixmyzuerich/service'; + my $action = "$attr/IFixMyZuerich/"; + + # Set up the SOAP handler + $security = SOAP::Header->name("Security")->attr({ + 'mustUnderstand' => 'true', + 'xmlns' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' + })->value( + \SOAP::Header->name( + "UsernameToken" => \SOAP::Header->value( + SOAP::Header->name('Username', $username), + SOAP::Header->name('Password', $password) + ) + ) + ); + $soap = SOAP::Lite->on_action( sub { $action . $_[1]; } )->proxy($url); + $method = SOAP::Data->name('getLocation')->attr({ xmlns => $attr }); +} + +# string STRING CONTEXT +# Looks up on Zurich web service a user-inputted location. +# Returns array of (LAT, LON, ERROR), where ERROR is either undef, a string, or +# an array of matches if there are more than one. The information in the query +# may be used to disambiguate the location in cobranded versions of the site. +sub string { + my ( $s, $c ) = @_; + + setup_soap(); + + my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'zurich/'; + my $cache_file = $cache_dir . md5_hex($s); + my $result; + if (-s $cache_file) { + $result = retrieve($cache_file); + } else { + my $search = SOAP::Data->name('search' => $s)->type(''); + my $count = SOAP::Data->name('count' => 10)->type(''); + eval { + $result = $soap->call($method, $security, $search, $count); + }; + if ($@) { + return { error => 'The geocoder appears to be down.' }; + } + $result = $result->result; + File::Path::mkpath($cache_dir); + store $result, $cache_file if $result; + } + + if (!$result || !$result->{Location}) { + return { error => _('Sorry, we could not parse that location. Please try again.') }; + } + + my $results = $result->{Location}; + $results = [ $results ] unless ref $results eq 'ARRAY'; + + my ( $error, @valid_locations, $latitude, $longitude ); + foreach (@$results) { + ($latitude, $longitude) = Geo::Coordinates::CH1903::to_latlon($_->{easting}, $_->{northing}); + mySociety::Locale::in_gb_locale { + push (@$error, { + address => $_->{text}, + latitude => sprintf('%0.6f', $latitude), + longitude => sprintf('%0.6f', $longitude) + }); + }; + push (@valid_locations, $_); + last if lc($_->{text}) eq lc($s); + } + + return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; + return { error => $error }; +} + +1; + diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm index 587c63d25..a1876e150 100644 --- a/perllib/FixMyStreet/Map.pm +++ b/perllib/FixMyStreet/Map.pm @@ -114,7 +114,26 @@ sub _map_features { } sub map_pins { - return $map_class->map_pins(@_); + my ($c, $interval) = @_; + + my $bbox = $c->req->param('bbox'); + my ( $min_lon, $min_lat, $max_lon, $max_lat ) = split /,/, $bbox; + + my ( $around_map, $around_map_list, $nearby, $dist ) = + FixMyStreet::Map::map_features_bounds( $c, $min_lon, $min_lat, $max_lon, $max_lat, $interval ); + + # create a list of all the pins + my @pins = map { + # Here we might have a DB::Problem or a DB::Nearby, we always want the problem. + my $p = (ref $_ eq 'FixMyStreet::App::Model::DB::Nearby') ? $_->problem : $_; + my $colour = $c->cobrand->pin_colour( $p, 'around' ); + [ $p->latitude, $p->longitude, + $colour, + $p->id, $p->title + ] + } @$around_map, @$nearby; + + return (\@pins, $around_map_list, $nearby, $dist); } sub click_to_wgs84 { diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm index 6b3bebba2..d8abc9dd6 100644 --- a/perllib/FixMyStreet/Map/OSM.pm +++ b/perllib/FixMyStreet/Map/OSM.pm @@ -94,29 +94,6 @@ sub display_map { }; } -sub map_pins { - my ($self, $c, $interval) = @_; - - my $bbox = $c->req->param('bbox'); - my ( $min_lon, $min_lat, $max_lon, $max_lat ) = split /,/, $bbox; - - my ( $around_map, $around_map_list, $nearby, $dist ) = - FixMyStreet::Map::map_features_bounds( $c, $min_lon, $min_lat, $max_lon, $max_lat, $interval ); - - # create a list of all the pins - my @pins = map { - # Here we might have a DB::Problem or a DB::Nearby, we always want the problem. - my $p = (ref $_ eq 'FixMyStreet::App::Model::DB::Nearby') ? $_->problem : $_; - my $colour = $c->cobrand->pin_colour( $p, 'around' ); - [ $p->latitude, $p->longitude, - $colour, - $p->id, $p->title - ] - } @$around_map, @$nearby; - - return (\@pins, $around_map_list, $nearby, $dist); -} - sub compass { my ( $x, $y, $z ) = @_; return { diff --git a/perllib/FixMyStreet/Map/Zurich.pm b/perllib/FixMyStreet/Map/Zurich.pm new file mode 100644 index 000000000..d2f7a35af --- /dev/null +++ b/perllib/FixMyStreet/Map/Zurich.pm @@ -0,0 +1,177 @@ +#!/usr/bin/perl +# +# FixMyStreet:Map::Zurich +# Zurich have their own tileserver. +# +# Copyright (c) 2012 UK Citizens Online Democracy. All rights reserved. +# Email: steve@mysociety.org; WWW: http://www.mysociety.org/ + +package FixMyStreet::Map::Zurich; + +use strict; +use Geo::Coordinates::CH1903; +use Math::Trig; +use Utils; + +use constant ZOOM_LEVELS => 10; +use constant DEFAULT_ZOOM => 7; +use constant MIN_ZOOM_LEVEL => 0; + +sub map_tiles { + my ( $self, %params ) = @_; + my ( $col, $row, $z ) = ( $params{x_tile}, $params{y_tile}, $params{matrix_id} ); + my $tile_url = $self->base_tile_url(); + return [ + "$tile_url/$z/" . ($row - 1) . "/" . ($col - 1) . ".jpg", + "$tile_url/$z/" . ($row - 1) . "/$col.jpg", + "$tile_url/$z/$row/" . ($col - 1) . ".jpg", + "$tile_url/$z/$row/$col.jpg", + ]; +} + +sub base_tile_url { + return 'http://www.wmts.stadt-zuerich.ch/Luftbild/MapServer/WMTS/tile/1.0.0/Luftbild/default/nativeTileMatrixSet'; +} + +sub copyright { + return '© Stadt Zürich'; +} + +# display_map C PARAMS +# PARAMS include: +# latitude, longitude for the centre point of the map +# CLICKABLE is set if the map is clickable +# PINS is array of pins to show, location and colour +sub display_map { + my ($self, $c, %params) = @_; + + my $numZoomLevels = ZOOM_LEVELS; + my $zoomOffset = MIN_ZOOM_LEVEL; +# if ($params{any_zoom}) { +# $numZoomLevels = 10; +# $zoomOffset = 0; +# } + + # TODO Adjust zoom level dependent upon population density + my $default_zoom = DEFAULT_ZOOM; + + # Map centre may be overridden in the query string + $params{latitude} = Utils::truncate_coordinate($c->req->params->{lat} + 0) + if defined $c->req->params->{lat}; + $params{longitude} = Utils::truncate_coordinate($c->req->params->{lon} + 0) + if defined $c->req->params->{lon}; + + my $zoom = defined $c->req->params->{zoom} ? $c->req->params->{zoom} + 0 : $default_zoom; + $zoom = $numZoomLevels - 1 if $zoom >= $numZoomLevels; + $zoom = 0 if $zoom < 0; + $params{zoom_act} = $zoomOffset + $zoom; + + ($params{x_tile}, $params{y_tile}, $params{matrix_id}) = latlon_to_tile_with_adjust($params{latitude}, $params{longitude}, $params{zoom_act}); + + foreach my $pin (@{$params{pins}}) { + ($pin->{px}, $pin->{py}) = latlon_to_px($pin->{latitude}, $pin->{longitude}, $params{x_tile}, $params{y_tile}, $params{zoom_act}); + } + + $c->stash->{map} = { + %params, + type => 'zurich', + map_type => 'OpenLayers.Layer.WMTS', + tiles => $self->map_tiles( %params ), + copyright => $self->copyright(), + zoom => $zoom, + zoomOffset => $zoomOffset, + numZoomLevels => $numZoomLevels, + }; +} + +# Given a lat/lon, convert it to Zurch tile co-ordinates (precise). +sub latlon_to_tile($$$) { + my ($lat, $lon, $zoom) = @_; + + my ($x, $y) = Geo::Coordinates::CH1903::from_latlon($lat, $lon); + + my $matrix_id = $zoom - 1; + $matrix_id = 0 if $matrix_id < 0; + + my @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500' ); + my $tileOrigin = { lat => 30814423, lon => -29386322 }; + my $tileSize = 256; + my $res = $scales[$zoom] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH + + my $fx = ( $x - $tileOrigin->{lon} ) / ($res * $tileSize); + my $fy = ( $tileOrigin->{lat} - $y ) / ($res * $tileSize); + + return ( $fx, $fy, $matrix_id ); +} + +# Given a lat/lon, convert it to OSM tile co-ordinates (nearest actual tile, +# adjusted so the point will be near the centre of a 2x2 tiled map). +sub latlon_to_tile_with_adjust($$$) { + my ($lat, $lon, $zoom) = @_; + my ($x_tile, $y_tile, $matrix_id) = latlon_to_tile($lat, $lon, $zoom); + + # Try and have point near centre of map + if ($x_tile - int($x_tile) > 0.5) { + $x_tile += 1; + } + if ($y_tile - int($y_tile) > 0.5) { + $y_tile += 1; + } + + return ( int($x_tile), int($y_tile), $matrix_id ); +} + +sub tile_to_latlon { + my ($fx, $fy, $zoom) = @_; + + my @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500' ); + my $tileOrigin = { lat => 30814423, lon => -29386322 }; + my $tileSize = 256; + my $res = $scales[$zoom] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH + + my $x = $fx * $res * $tileSize + $tileOrigin->{lon}; + my $y = $tileOrigin->{lat} - $fy * $res * $tileSize; + + my ($lat, $lon) = Geo::Coordinates::CH1903::to_latlon($x, $y); + + return ( $lat, $lon ); +} + +# Given a lat/lon, convert it to pixel co-ordinates from the top left of the map +sub latlon_to_px($$$$$) { + my ($lat, $lon, $x_tile, $y_tile, $zoom) = @_; + my ($pin_x_tile, $pin_y_tile) = latlon_to_tile($lat, $lon, $zoom); + my $pin_x = tile_to_px($pin_x_tile, $x_tile); + my $pin_y = tile_to_px($pin_y_tile, $y_tile); + return ($pin_x, $pin_y); +} + +# Convert tile co-ordinates to pixel co-ordinates from top left of map +# C is centre tile reference of displayed map +sub tile_to_px { + my ($p, $c) = @_; + $p = 256 * ($p - $c + 1); + $p = int($p + .5 * ($p <=> 0)); + return $p; +} + +sub click_to_tile { + my ($pin_tile, $pin) = @_; + $pin -= 256 while $pin > 256; + $pin += 256 while $pin < 0; + return $pin_tile + $pin / 256; +} + +# Given some click co-ords (the tile they were on, and where in the +# tile they were), convert to WGS84 and return. +# XXX Note use of MIN_ZOOM_LEVEL here. (Copied from OSM, needed here?) +sub click_to_wgs84 { + my ($self, $c, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_; + my $tile_x = click_to_tile($pin_tile_x, $pin_x); + my $tile_y = click_to_tile($pin_tile_y, $pin_y); + my $zoom = MIN_ZOOM_LEVEL + (defined $c->req->params->{zoom} ? $c->req->params->{zoom} : DEFAULT_ZOOM); + my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom); + return ( $lat, $lon ); +} + +1; diff --git a/perllib/FixMyStreet/SendReport.pm b/perllib/FixMyStreet/SendReport.pm index 9ba507862..f679d826e 100644 --- a/perllib/FixMyStreet/SendReport.pm +++ b/perllib/FixMyStreet/SendReport.pm @@ -44,5 +44,12 @@ sub add_council { $self->councils->{ $council } = { info => $info, config => $config }; } +sub delete_council { + my $self = shift; + my $council = shift; + + delete $self->councils->{$council}; +} + 1; diff --git a/perllib/FixMyStreet/SendReport/Open311.pm b/perllib/FixMyStreet/SendReport/Open311.pm index 8d7a418af..efd172640 100644 --- a/perllib/FixMyStreet/SendReport/Open311.pm +++ b/perllib/FixMyStreet/SendReport/Open311.pm @@ -9,6 +9,9 @@ use FixMyStreet::App; use mySociety::Config; use DateTime::Format::W3CDTF; use Open311; +use Readonly; + +Readonly::Scalar my $COUNCIL_ID_OXFORDSHIRE => 2237; sub should_skip { my $self = shift; @@ -34,7 +37,7 @@ sub send { my $send_notpinpointed = 0; my $use_service_as_deviceid = 0; - my $basic_desc = 0; + my $extended_desc = 1; # Extra bromley fields if ( $row->council =~ /2482/ ) { @@ -64,7 +67,22 @@ sub send { push @$extra, { name => 'last_name', value => $lastname }; } - $basic_desc = 1; + $extended_desc = 0; + } + + # extra Oxfordshire fields: send nearest street, postcode, northing and easting, and the FMS id + if ( $row->council =~ /$COUNCIL_ID_OXFORDSHIRE/ ) { + + my $extra = $row->extra; + push @$extra, { name => 'external_id', value => $row->id }; + push @$extra, { name => 'closest_address', value => $h->{closest_address} } if $h->{closest_address}; + if ( $row->used_map || ( !$row->used_map && !$row->postcode ) ) { + push @$extra, { name => 'northing', value => $h->{northing} }; + push @$extra, { name => 'easting', value => $h->{easting} }; + } + $row->extra( $extra ); + + $extended_desc = 'oxfordshire'; } # FIXME: we've already looked this up before @@ -81,7 +99,7 @@ sub send { always_send_latlong => $always_send_latlong, send_notpinpointed => $send_notpinpointed, use_service_as_deviceid => $use_service_as_deviceid, - basic_description => $basic_desc, + extended_description => $extended_desc, ); # non standard west berks end points @@ -89,20 +107,25 @@ sub send { $open311->endpoints( { services => 'Services', requests => 'Requests' } ); } + # non-standard Oxfordshire endpoint (because it's just a script, not a full Open311 service) + if ( $row->council =~ /$COUNCIL_ID_OXFORDSHIRE/ ) { + $open311->endpoints( { requests => 'open311_service_request.cgi' } ); + } + # required to get round issues with CRM constraints if ( $row->council =~ /2218/ ) { $row->user->name( $row->user->id . ' ' . $row->user->name ); } if ($row->cobrand eq 'fixmybarangay') { - # FixMyBarangay endpoints expect external_id as an attribute + # FixMyBarangay endpoints expect external_id as an attribute, as do Oxfordshire $row->extra( [ { 'name' => 'external_id', 'value' => $row->id } ] ); } my $resp = $open311->send_service_request( $row, $h, $contact->email ); # make sure we don't save user changes from above - if ( $row->council =~ /2218/ || $row->council =~ /2482/ || $row->cobrand eq 'fixmybarangay') { + if ( $row->council =~ /(2218|2482|$COUNCIL_ID_OXFORDSHIRE)/ || $row->cobrand eq 'fixmybarangay') { $row->discard_changes(); } |