diff options
Diffstat (limited to 'perllib/FixMyStreet')
36 files changed, 733 insertions, 719 deletions
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index 90f1f24bc..68bfc728b 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -180,7 +180,11 @@ sub setup_request { Memcached::set_namespace( FixMyStreet->config('BCI_DB_NAME') . ":" ); - my $map = $host =~ /^osm\./ ? 'OSM' : $c->req->param('map'); + my $map = $host =~ /^osm\./ ? 'OSM' : $c->req->param('map_override'); + #if ($c->sessionid) { + # $map = $c->session->{map}; + # $map = undef unless $map eq 'OSM'; + #} FixMyStreet::Map::set_map_class( $map ); return $c; diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index f38181be6..63d892fc4 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -62,6 +62,7 @@ sub index : Path : Args(0) { $c->stash->{problems} = \%prob_counts; $c->stash->{total_problems_live} += $prob_counts{$_} for ( FixMyStreet::DB::Result::Problem->visible_states() ); + $c->stash->{total_problems_users} = $c->cobrand->problems->unique_users; my $comments = $c->model('DB::Comment')->summary_count( $site_restriction ); @@ -108,6 +109,8 @@ sub index : Path : Args(0) { : _('n/a'); $c->stash->{questionnaires} = \%questionnaire_counts; + $c->stash->{categories} = $c->cobrand->problems->categories_summary(); + return 1; } @@ -120,6 +123,7 @@ sub timeline : Path( 'timeline' ) : Args(0) { my %time; $c->model('DB')->schema->storage->sql_maker->quote_char( '"' ); + $c->model('DB')->schema->storage->sql_maker->name_sep( '.' ); my $probs = $c->cobrand->problems->timeline; @@ -172,15 +176,22 @@ sub questionnaire : Path('questionnaire') : Args(0) { my %questionnaire_counts = map { $_->get_column( 'reported' ) => $_->get_column( 'questionnaire_count' ) } $questionnaires->all; - $questionnaire_counts{1} ||= 0; $questionnaire_counts{0} ||= 0; - $questionnaire_counts{total} = $questionnaire_counts{0} + $questionnaire_counts{1}; - $c->stash->{reported_pc} = ( 100 * $questionnaire_counts{1} ) / $questionnaire_counts{total}; - $c->stash->{not_reported_pc} = ( 100 * $questionnaire_counts{0} ) / $questionnaire_counts{total}; $c->stash->{questionnaires} = \%questionnaire_counts; + $c->stash->{state_changes_count} = $c->model('DB::Questionnaire')->search( + { whenanswered => \'is not null' } + )->count; + $c->stash->{state_changes} = $c->model('DB::Questionnaire')->search( + { whenanswered => \'is not null' }, + { + group_by => [ 'old_state', 'new_state' ], + columns => [ 'old_state', 'new_state', { c => { count => 'id' } } ], + }, + ); + return 1; } @@ -419,6 +430,7 @@ sub search_reports : Path('search_reports') { # makes PostgreSQL unhappy elsewhere so we only want to do # it for this query and then switch it off afterwards. $c->model('DB')->schema->storage->sql_maker->quote_char( '"' ); + $c->model('DB')->schema->storage->sql_maker->name_sep( '.' ); my $problems = $c->cobrand->problems->search( { diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index 4ca559fad..660585454 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -239,6 +239,11 @@ sub ajax : Path('/ajax') { my $all_pins = $c->req->param('all_pins') ? 1 : undef; my $interval = $all_pins ? undef : $c->cobrand->on_map_default_max_pin_age; + # Need to be the class that can handle it + if ($c->req->param('bbox')) { + FixMyStreet::Map::set_map_class( 'OSM' ); + } + # extract the data from the map my ( $pins, $on_map, $around_map, $dist ) = FixMyStreet::Map::map_pins( $c, $interval ); diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index ebb3510d0..c67de692a 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -36,7 +36,8 @@ sub general : Path : Args(0) { return unless $req->method eq 'POST'; # decide which action to take - $c->detach('email_sign_in') if $req->param('email_sign_in'); + $c->detach('email_sign_in') if $req->param('email_sign_in') + || $c->req->param('name') || $c->req->param('password_register'); $c->forward( 'sign_in' ) && $c->detach( 'redirect_on_signin', [ $req->param('r') ] ); @@ -107,6 +108,11 @@ sub email_sign_in : Private { return; } + my $user_params = {}; + $user_params->{password} = $c->req->param('password_register') + if $c->req->param('password_register'); + my $user = $c->model('DB::User')->new( $user_params ); + my $token_obj = $c->model('DB::Token') # ->create( { @@ -115,7 +121,7 @@ sub email_sign_in : Private { email => $good_email, r => $c->req->param('r'), name => $c->req->param('name'), - password => $c->req->param('password_register'), + password => $user->password, } } ); @@ -157,9 +163,8 @@ sub token : Path('/M') : Args(1) { # find or create the user related to the token. my $user = $c->model('DB::User')->find_or_create( { email => $data->{email} } ); $user->name( $data->{name} ) if $data->{name}; - $user->password( $data->{password} ) if $data->{password}; + $user->password( $data->{password}, 1 ) if $data->{password}; $user->update; - $c->authenticate( { email => $user->email }, 'no_password' ); # send the user to their page diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm index d9baeaaf8..f3607341a 100644 --- a/perllib/FixMyStreet/App/Controller/JSON.pm +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -39,6 +39,7 @@ sub problems : Local { # gather the parameters my $start_date = $c->req->param('start_date') || ''; my $end_date = $c->req->param('end_date') || ''; + my $category = $c->req->param('category') || ''; my $yyyy_mm_dd = qr{^\d{4}-\d\d-\d\d$}; if ( $start_date !~ $yyyy_mm_dd @@ -73,25 +74,29 @@ sub problems : Local { my ( @state, $date_col ); if ( $type eq 'new_problems' ) { @state = FixMyStreet::DB::Result::Problem->open_states(); - $date_col = 'created'; + $date_col = 'confirmed'; } elsif ( $type eq 'fixed_problems' ) { @state = FixMyStreet::DB::Result::Problem->fixed_states(); $date_col = 'lastupdate'; } my $one_day = DateTime::Duration->new( days => 1 ); - my @problems = $c->cobrand->problems->search( { + my $query = { $date_col => { '>=' => $start_dt, '<=' => $end_dt + $one_day, }, state => [ @state ], - }, { + }; + $query->{category} = $category if $category; + my @problems = $c->cobrand->problems->search( $query, { order_by => { -asc => 'confirmed' }, columns => [ 'id', 'title', 'council', 'category', 'detail', 'name', 'anonymous', 'confirmed', 'whensent', 'service', + 'latitude', 'longitude', 'used_map', + 'state', 'lastupdate', ] } ); diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index b1359ae03..19b3ffee0 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -25,9 +25,6 @@ sub my : Path : Args(0) { $c->detach( '/auth/redirect' ) unless $c->user; - # Even though front end doesn't yet have it, have it on this page, it's better! - FixMyStreet::Map::set_map_class( 'FMS' ); - my $p_page = $c->req->params->{p} || 1; my $u_page = $c->req->params->{u} || 1; @@ -56,6 +53,7 @@ sub my : Path : Args(0) { $c->stash->{updates} = \@updates; $c->stash->{updates_pager} = $rs->pager; + $c->stash->{page} = 'my'; FixMyStreet::Map::display_map( $c, latitude => $pins->[0]{latitude}, diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm index 8de935dd7..d9bdb7108 100755 --- a/perllib/FixMyStreet/App/Controller/Questionnaire.pm +++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm @@ -289,6 +289,7 @@ sub display : Private { { order_by => 'confirmed' } ); + $c->stash->{page} = 'questionnaire'; FixMyStreet::Map::display_map( $c, latitude => $problem->latitude, diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index a15ee993f..6596615c6 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -108,8 +108,6 @@ sub format_problem_for_display : Private { map { Utils::truncate_coordinate($_) } ( $problem->latitude, $problem->longitude ); - $c->stash->{report_name} = $c->req->param('name'); - unless ( $c->req->param('submit_update') ) { $c->stash->{add_alert} = 1; } @@ -124,6 +122,7 @@ sub generate_map_tags : Private { my $problem = $c->stash->{problem}; + $c->stash->{page} = 'report'; FixMyStreet::Map::display_map( $c, latitude => $problem->latitude, diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 3a7d18a1c..346dfb377 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -220,13 +220,15 @@ sub report_import : Path('/import') { anonymous => 0, category => '', areas => '', + cobrand => $c->cobrand->moniker, + lang => $c->stash->{lang_code}, } ); # If there was a photo add that too - if ( my $fileid = $c->stash->{upload_fileid} ) { - my $file = file( $c->config->{UPLOAD_CACHE}, "$fileid.jpg" ); + if ( $photo ) { + my $file = file( $c->config->{UPLOAD_CACHE}, "$photo.jpg" ); my $blob = $file->slurp; $file->remove; $report->photo($blob); @@ -489,7 +491,7 @@ sub setup_categories_and_councils : Private { next # TODO - move this to the cobrand if $c->cobrand->moniker eq 'southampton' - && $contact->category eq 'Street lighting'; + && $contact->category =~ /Street lighting|Traffic lights/; next if $contact->category eq _('Other'); @@ -571,9 +573,9 @@ sub process_user : Private { unless $report->user; # The user is trying to sign in. We only care about email from the params. - if ( $c->req->param('submit_sign_in') ) { + if ( $c->req->param('submit_sign_in') || $c->req->param('password_sign_in') ) { unless ( $c->forward( '/auth/sign_in' ) ) { - $c->stash->{field_errors}->{password} = _('There was a problem with your email/password combination. Please try again.'); + $c->stash->{field_errors}->{password} = _('There was a problem with your email/password combination. Passwords and user accounts are a brand <strong>new</strong> service, so you probably do not have one yet – please fill in the right hand side of this form to get one.'); return 1; } my $user = $c->user->obj; @@ -608,6 +610,7 @@ sub process_report : Private { map { $_ => scalar $c->req->param($_) } # ( 'title', 'detail', 'pc', # + 'detail_size', 'detail_depth', 'may_show_name', # 'category', # 'partial', # @@ -626,8 +629,14 @@ sub process_report : Private { # clean up text before setting $report->title( Utils::cleanup_text( $params{title} ) ); - $report->detail( - Utils::cleanup_text( $params{detail}, { allow_multiline => 1 } ) ); + + my $detail = Utils::cleanup_text( $params{detail}, { allow_multiline => 1 } ); + for my $w ('depth', 'size') { + next unless $params{"detail_$w"}; + next if $params{"detail_$w"} eq '-- Please select --'; + $detail .= "\n\n\u$w: " . $params{"detail_$w"}; + } + $report->detail( $detail ); # set these straight from the params $report->category( _ $params{category} ); @@ -845,9 +854,13 @@ sub save_user_and_report : Private { $report->confirm; } else { - - # user exists and we are not logged in as them. Throw away changes to - # the name and phone. TODO - propagate changes using tokens. + # User exists and we are not logged in as them. + # Store changes in token for when token is validated. + $c->stash->{token_data} = { + name => $report->user->name, + phone => $report->user->phone, + password => $report->user->password, + }; $report->user->discard_changes(); } @@ -895,6 +908,7 @@ sub generate_map : Private { # Don't do anything if the user skipped the map unless ( $c->req->param('skipped') ) { + $c->stash->{page} = 'new'; FixMyStreet::Map::display_map( $c, latitude => $latitude, @@ -932,9 +946,11 @@ sub redirect_or_confirm_creation : Private { } # otherwise create a confirm token and email it to them. + my $data = $c->stash->{token_data} || {}; my $token = $c->model("DB::Token")->create( { scope => 'problem', data => { + %$data, id => $report->id } } ); diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 2916e1ee1..add9d1371 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -120,9 +120,9 @@ sub process_user : Private { unless $update->user; # The user is trying to sign in. We only care about email from the params. - if ( $c->req->param('submit_sign_in') ) { + if ( $c->req->param('submit_sign_in') || $c->req->param('password_sign_in') ) { unless ( $c->forward( '/auth/sign_in', [ $email ] ) ) { - $c->stash->{field_errors}->{password} = _('There was a problem with your email/password combination. Please try again.'); + $c->stash->{field_errors}->{password} = _('There was a problem with your email/password combination. Passwords and user accounts are a brand <strong>new</strong> service, so you probably do not have one yet – please fill in the right hand side of this form to get one.'); return 1; } my $user = $c->user->obj; @@ -180,8 +180,7 @@ sub process_update : Private { if ( $params{state} ) { $params{state} = 'fixed - council' - if $params{state} eq 'fixed' && $c->user && $c->user->from_council - && $c->user->from_council == $update->problem->council; + if $params{state} eq 'fixed' && $c->user && $c->user->belongs_to_council( $update->problem->council ); $update->problem_state( $params{state} ); } @@ -205,7 +204,7 @@ sub check_for_errors : Private { # they have to be an authority user to update the state if ( $c->req->param('state') ) { my $error = 0; - $error = 1 unless $c->user && $c->user->from_council && $c->user->from_council == $c->stash->{update}->problem->council; + $error = 1 unless $c->user && $c->user->belongs_to_council( $c->stash->{update}->problem->council ); my $state = $c->req->param('state'); $error = 1 unless ( grep { $state eq $_ } ( qw/confirmed closed fixed investigating planned/, 'in progress', 'fixed', 'fixed - user', 'fixed - council' ) ); @@ -262,6 +261,14 @@ sub save_update : Private { # Logged in and same user, so can confirm update straight away $update->user->update; $update->confirm; + } else { + # User exists and we are not logged in as them. + # Store changes in token for when token is validated. + $c->stash->{token_data} = { + name => $update->user->name, + password => $update->user->password, + }; + $update->user->discard_changes(); } # If there was a photo add that too @@ -303,10 +310,12 @@ sub redirect_or_confirm_creation : Private { } # otherwise create a confirm token and email it to them. + my $data = $c->stash->{token_data} || {}; my $token = $c->model("DB::Token")->create( { scope => 'comment', data => { + %$data, id => $update->id, add_alert => ( $c->req->param('add_alert') ? 1 : 0 ), } diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 9cec0fdfe..821b650ed 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -34,10 +34,18 @@ sub index : Path : Args(0) { $c->response->header('Cache-Control' => 'max-age=3600'); # Fetch all areas of the types we're interested in - my @area_types = $c->cobrand->area_types; - my $areas_info = mySociety::MaPit::call('areas', \@area_types, - min_generation => $c->cobrand->area_min_generation - ); + my $areas_info; + eval { + my @area_types = $c->cobrand->area_types; + $areas_info = mySociety::MaPit::call('areas', \@area_types, + min_generation => $c->cobrand->area_min_generation + ); + }; + if ($@) { + $c->stash->{message} = _("Unable to look up areas in MaPit. Please try again later.") . ' ' . + sprintf(_('The error was: %s'), $@); + $c->stash->{template} = 'errors/generic.html'; + } # For each area, add its link and perhaps alter its name if we need to for # places with the same name. @@ -61,7 +69,8 @@ sub index : Path : Args(0) { $c->stash->{open} = $j->{open}; }; if ($@) { - $c->stash->{message} = _("There was a problem showing the All Reports page. Please try again later."); + $c->stash->{message} = _("There was a problem showing the All Reports page. Please try again later.") . ' ' . + sprintf(_('The error was: %s'), $@); $c->stash->{template} = 'errors/generic.html'; } } @@ -90,6 +99,7 @@ sub ward : Path : Args(2) { $c->forward( 'ward_check', [ $ward ] ) if $ward; $c->forward( 'load_parent' ); + $c->forward( 'check_canonical_url', [ $council ] ); $c->forward( 'load_and_group_problems' ); $c->forward( 'sort_problems' ); @@ -102,8 +112,7 @@ sub ward : Path : Args(2) { my $pins = $c->stash->{pins}; - # Even though front end doesn't yet have it, have it on this page, it's better! - FixMyStreet::Map::set_map_class( 'FMS' ); + $c->stash->{page} = 'reports'; # So the map knows to make clickable pins FixMyStreet::Map::display_map( $c, latitude => @$pins ? $pins->[0]{latitude} : 0, @@ -227,7 +236,7 @@ sub council_check : Private { return; } else { foreach (keys %$areas) { - if ($areas->{$_}->{name} eq $q_council || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/) { + if (lc($areas->{$_}->{name}) eq lc($q_council) || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/i) { $c->stash->{council} = $areas->{$_}; return; } @@ -252,6 +261,7 @@ sub ward_check : Private { $ward =~ s/\+/ /g; $ward =~ s/\.html//; + $ward =~ s{_}{/}g; my $council = $c->stash->{council}; @@ -281,6 +291,22 @@ sub load_parent : Private { } } +=head2 check_canonical_url + +Given an already found (case-insensitively) council, check what URL +we are at and redirect accordingly if different. + +=cut + +sub check_canonical_url : Private { + my ( $self, $c, $q_council ) = @_; + + my $council_short = $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} ); + my $url_short = URI::Escape::uri_escape_utf8($q_council); + $url_short =~ s/%2B/+/g; + $c->detach( 'redirect_area' ) unless $council_short eq $url_short; +} + sub load_and_group_problems : Private { my ( $self, $c ) = @_; @@ -290,9 +316,9 @@ sub load_and_group_problems : Private { state => [ FixMyStreet::DB::Result::Problem->visible_states() ] }; if ($c->stash->{ward}) { - $where->{areas} = { 'like', '%' . $c->stash->{ward}->{id} . '%' }; # FIXME Check this is secure + $where->{areas} = { 'like', '%,' . $c->stash->{ward}->{id} . ',%' }; } elsif ($c->stash->{council}) { - $where->{areas} = { 'like', '%' . $c->stash->{council}->{id} . '%' }; + $where->{areas} = { 'like', '%,' . $c->stash->{council}->{id} . ',%' }; } my $problems = $c->cobrand->problems->search( $where, @@ -369,7 +395,7 @@ sub redirect_area : Private { my $url = ''; $url .= "/rss" if $c->stash->{rss}; $url .= '/reports'; - $url .= '/' . $c->cobrand->short_name( $c->stash->{council} ); + $url .= '/' . $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} ); $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; $c->res->redirect( $c->uri_for($url) ); diff --git a/perllib/FixMyStreet/App/Controller/Rss.pm b/perllib/FixMyStreet/App/Controller/Rss.pm index db4955f06..78793d9c1 100755 --- a/perllib/FixMyStreet/App/Controller/Rss.pm +++ b/perllib/FixMyStreet/App/Controller/Rss.pm @@ -217,7 +217,7 @@ sub add_row : Private { my ( $self, $c, $row ) = @_; my $alert_type = $c->stash->{alert_type}; - $row->{name} ||= 'anonymous'; + $row->{name} = 'anonymous' if $row->{anonymous} || !$row->{name}; my $pubDate; if ($row->{confirmed}) { diff --git a/perllib/FixMyStreet/App/Controller/Tilma.pm b/perllib/FixMyStreet/App/Controller/Tilma.pm deleted file mode 100644 index 1be481949..000000000 --- a/perllib/FixMyStreet/App/Controller/Tilma.pm +++ /dev/null @@ -1,46 +0,0 @@ -package FixMyStreet::App::Controller::Tilma; -use Moose; -use namespace::autoclean; - -BEGIN { extends 'Catalyst::Controller'; } - -use LWP::UserAgent; - -=head1 NAME - -FixMyStreet::App::Controller::Tilma - Tilma proxy - -=head1 DESCRIPTION - -A tilma proxy - only intended to be used during dev. In production the webserver should do this proxying. - -=head1 METHODS - -=head2 default - -Proxy everything through to the tilma servers. - -=cut - -sub default : Path { - my ( $self, $c ) = @_; - - my $path = $c->req->uri->path_query; - $path =~ s{/tilma/}{}; - - my $tilma_uri = URI->new("http://tilma.mysociety.org/$path"); - - my $tilma_res = LWP::UserAgent->new->get($tilma_uri); - - if ( $tilma_res->is_success ) { - $c->res->content_type( $tilma_res->content_type ); - $c->res->body( $tilma_res->content ); - } - else { - die sprintf "Error getting %s: %s", $tilma_uri, $tilma_res->message; - } -} - -__PACKAGE__->meta->make_immutable; - -1; diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index 1fef0f07e..9abef591d 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -32,7 +32,8 @@ sub confirm_problem : Path('/P') { $c->forward( 'load_auth_token', [ $token_code, 'problem' ] ); # Load the problem - my $problem_id = $auth_token->data->{id}; + my $data = $auth_token->data; + my $problem_id = $data->{id}; my $problem = $c->cobrand->problems->find( { id => $problem_id } ) || $c->detach('token_error'); $c->stash->{problem} = $problem; @@ -59,6 +60,11 @@ sub confirm_problem : Path('/P') { $c->forward( '/report/new/create_reporter_alert' ); # log the problem creation user in to the site + if ( $data->{name} || $data->{password} ) { + $problem->user->name( $data->{name} ) if $data->{name}; + $problem->user->password( $data->{password}, 1 ) if $data->{password}; + $problem->user->update; + } $c->authenticate( { email => $problem->user->email }, 'no_password' ); $c->set_session_cookie_expire(0); @@ -133,8 +139,9 @@ sub confirm_update : Path('/C') { $c->forward( 'load_auth_token', [ $token_code, 'comment' ] ); # Load the problem - my $comment_id = $auth_token->data->{id}; - $c->stash->{add_alert} = $auth_token->data->{add_alert}; + my $data = $auth_token->data; + my $comment_id = $data->{id}; + $c->stash->{add_alert} = $data->{add_alert}; my $comment = $c->model('DB::Comment')->find( { id => $comment_id } ) || $c->detach('token_error'); @@ -146,6 +153,11 @@ sub confirm_update : Path('/C') { return; } + if ( $data->{name} || $data->{password} ) { + $comment->user->name( $data->{name} ) if $data->{name}; + $comment->user->password( $data->{password}, 1 ) if $data->{password}; + $comment->user->update; + } $c->authenticate( { email => $comment->user->email }, 'no_password' ); $c->set_session_cookie_expire(0); diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm index 358e280c3..df2d0ac20 100644 --- a/perllib/FixMyStreet/App/View/Web.pm +++ b/perllib/FixMyStreet/App/View/Web.pm @@ -21,6 +21,9 @@ __PACKAGE__->config( 'loc', 'nget', 'tprintf', 'display_crosssell_advert', 'prettify_epoch', 'add_links', ], + FILTERS => { + escape_js => \&escape_js, + }, ); =head1 NAME @@ -120,5 +123,24 @@ sub add_links { return $text; } +=head2 escape_js + +Used to escape strings that are going to be put inside JavaScript. + +=cut + +sub escape_js { + my $text = shift; + my %lookup = ( + '\\' => 'u005c', + '"' => 'u0022', + "'" => 'u0027', + '<' => 'u003c', + '>' => 'u003e', + ); + $text =~ s/([\\"'<>])/\\$lookup{$1}/g; + return $text; +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/Barnet.pm b/perllib/FixMyStreet/Cobrand/Barnet.pm index 9496570ea..a12fa6d06 100644 --- a/perllib/FixMyStreet/Cobrand/Barnet.pm +++ b/perllib/FixMyStreet/Cobrand/Barnet.pm @@ -76,9 +76,5 @@ sub recent_photos { return $self->problems->recent_photos( $num, $lat, $lon, $dist ); } -sub tilma_mid_point { - return 189; -} - 1; diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 29061ebd8..69718f613 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -7,6 +7,7 @@ use URI; use Carp; use mySociety::MaPit; +use mySociety::PostcodeUtil; =head2 new @@ -405,12 +406,9 @@ sub uri { (my $map_class = $FixMyStreet::Map::map_class) =~ s/^FixMyStreet::Map:://; return $uri unless $map_class =~ /OSM|FMS/; - $uri = URI->new( $uri ); $uri->query_param( zoom => 3 ) if $uri->query_param('lat') && !$uri->query_param('zoom'); - # $uri->query_param( map => $map_class ); # FIXME Only on /around, /report? - return $uri; } @@ -490,6 +488,46 @@ allowing them to report them as offensive. sub allow_update_reporting { return 0; } +=head2 geocode_postcode + +Given a QUERY, return LAT/LON and/or ERROR. + +=cut + +sub geocode_postcode { + my ( $self, $s ) = @_; + + if ($s =~ /^\d+$/) { + return { + error => 'FixMyStreet is a UK-based website that currently works in England, Scotland, and Wales. Please enter either a postcode, or a Great British street name and area.' + }; + } elsif (mySociety::PostcodeUtil::is_valid_postcode($s)) { + my $location = mySociety::MaPit::call('postcode', $s); + if ($location->{error}) { + return { + error => $location->{code} =~ /^4/ + ? _('That postcode was not recognised, sorry.') + : $location->{error} + }; + } + my $island = $location->{coordsyst}; + if (!$island) { + return { + error => _("Sorry, that appears to be a Crown dependency postcode, which we don't cover.") + }; + } elsif ($island eq 'I') { + return { + error => _("We do not currently cover Northern Ireland, I'm afraid.") + }; + } + return { + latitude => $location->{wgs84_lat}, + longitude => $location->{wgs84_lon}, + }; + } + return {}; +} + =head2 geocoded_string_check Parameters are LOCATION, QUERY. Return a boolean indicating whether the @@ -499,6 +537,44 @@ string LOCATION passes the cobrands checks. sub geocoded_string_check { return 1; } +=head2 find_closest + +Used by send-reports to attach nearest things to the bottom of the report + +=cut + +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + my $str = ''; + + # Get nearest road-type thing from Bing + my $key = mySociety::Config::get('BING_MAPS_API_KEY', ''); + if ($key) { + my $url = "http://dev.virtualearth.net/REST/v1/Locations/$latitude,$longitude?c=en-GB&key=$key"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{resourceSets}[0]{resources}[0]{name}) { + $str .= sprintf(_("Nearest road to the pin placed on the map (automatically generated by Bing Maps): %s"), + $j->{resourceSets}[0]{resources}[0]{name}) . "\n\n"; + } + } + } + + # Get nearest postcode from Matthew's random gazetteer (put in MaPit? Or elsewhere?) + my $url = "http://gazetteer.dracos.vm.bytemark.co.uk/point/$latitude,$longitude.json"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{postcode}) { + $str .= sprintf(_("Nearest postcode to the pin placed on the map (automatically generated): %s (%sm away)"), + $j->{postcode}[0], $j->{postcode}[1]) . "\n\n"; + } + } + + return $str; +} + =head2 council_check Paramters are COUNCILS, QUERY, CONTEXT. Return a boolean indicating whether @@ -558,16 +634,6 @@ The MaPit types this site handles sub area_types { return qw(DIS LBO MTD UTA CTY COI); } sub area_min_generation { 10 } -=head2 tilma_mid_point - -If the map is smaller than the usual, return half its size. - -=cut - -sub tilma_mid_point { - return 0; -} - =head2 contact_name, contact_email Return the contact name or email for the cobranded version of the site (to be @@ -672,6 +738,7 @@ sub short_name { $name =~ s/ (Borough|City|District|County) Council$//; $name =~ s/ Council$//; $name =~ s/ & / and /; + $name =~ s{/}{_}g; $name = URI::Escape::uri_escape_utf8($name); $name =~ s/%20/+/g; return $name; @@ -687,6 +754,7 @@ Generate a set of options for council rss alerts. sub council_rss_alert_options { my $self = shift; my $all_councils = shift; + my $c = shift; my %councils = map { $_ => 1 } $self->area_types(); @@ -713,7 +781,7 @@ sub council_rss_alert_options { id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ), text => sprintf( _('Problems within %s'), $council->{name}), rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}), - uri => $self->uri( '/rss/reports/' . $council->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} ), }; push @options, { @@ -721,7 +789,7 @@ sub council_rss_alert_options { id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ), rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}), text => sprintf( _('Problems within %s ward'), $ward->{name}), - uri => $self->uri( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), } if $ward; } elsif ( $num_councils == 4 ) { # # Two-tier council @@ -750,28 +818,28 @@ sub council_rss_alert_options { id => sprintf( 'area:%s:%s', $district->{id}, $district->{id_name} ), text => $district_name, rss_text => sprintf( _('RSS feed for %s'), $district_name ), - uri => $self->uri( '/rss/areas/' . $district->{short_name} ) + uri => $c->uri_for( '/rss/areas/' . $district->{short_name} ) }, { type => 'area', id => sprintf( 'area:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ), text => sprintf( _('%s ward, %s'), $d_ward_name, $district_name ), rss_text => sprintf( _('RSS feed for %s ward, %s'), $d_ward_name, $district_name ), - uri => $self->uri( '/rss/areas/' . $district->{short_name} . '/' . $d_ward->{short_name} ) + uri => $c->uri_for( '/rss/areas/' . $district->{short_name} . '/' . $d_ward->{short_name} ) }, { type => 'area', id => sprintf( 'area:%s:%s', $county->{id}, $county->{id_name} ), text => $county_name, rss_text => sprintf( _('RSS feed for %s'), $county_name ), - uri => $self->uri( '/rss/areas/' . $county->{short_name} ) + uri => $c->uri_for( '/rss/areas/' . $county->{short_name} ) }, { type => 'area', id => sprintf( 'area:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ), text => sprintf( _('%s ward, %s'), $c_ward_name, $county_name ), rss_text => sprintf( _('RSS feed for %s ward, %s'), $c_ward_name, $county_name ), - uri => $self->uri( '/rss/areas/' . $county->{short_name} . '/' . $c_ward->{short_name} ) + uri => $c->uri_for( '/rss/areas/' . $county->{short_name} . '/' . $c_ward->{short_name} ) }; push @reported_to_options, @@ -780,28 +848,28 @@ sub council_rss_alert_options { id => sprintf( 'council:%s:%s', $district->{id}, $district->{id_name} ), text => $district->{name}, rss_text => sprintf( _('RSS feed of %s'), $district->{name}), - uri => $self->uri( '/rss/reports/' . $district->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $district->{short_name} ), }, { type => 'ward', id => sprintf( 'ward:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ), rss_text => sprintf( _('RSS feed of %s, within %s ward'), $district->{name}, $d_ward->{name}), text => sprintf( _('%s, within %s ward'), $district->{name}, $d_ward->{name}), - uri => $self->uri( '/rss/reports/' . $district->{short_name} . '/' . $d_ward->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $district->{short_name} . '/' . $d_ward->{short_name} ), }, { type => 'council', id => sprintf( 'council:%s:%s', $county->{id}, $county->{id_name} ), text => $county->{name}, rss_text => sprintf( _('RSS feed of %s'), $county->{name}), - uri => $self->uri( '/rss/reports/' . $county->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $county->{short_name} ), }, { type => 'ward', id => sprintf( 'ward:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ), rss_text => sprintf( _('RSS feed of %s, within %s ward'), $county->{name}, $c_ward->{name}), text => sprintf( _('%s, within %s ward'), $county->{name}, $c_ward->{name}), - uri => $self->uri( '/rss/reports/' . $county->{short_name} . '/' . $c_ward->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $county->{short_name} . '/' . $c_ward->{short_name} ), }; diff --git a/perllib/FixMyStreet/Cobrand/EmptyHomes.pm b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm index 3694d984b..189daee0c 100644 --- a/perllib/FixMyStreet/Cobrand/EmptyHomes.pm +++ b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm @@ -138,6 +138,7 @@ Generate a set of options for council rss alerts. sub council_rss_alert_options { my $self = shift; my $all_councils = shift; + my $c = shift; my %councils = map { $_ => 1 } $self->area_types(); @@ -160,14 +161,14 @@ sub council_rss_alert_options { id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ), text => sprintf( _('Problems within %s'), $council->{name}), rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}), - uri => $self->uri( '/rss/reports/' . $council->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} ), }; push @options, { type => 'ward', id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ), rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}), text => sprintf( _('Problems within %s ward'), $ward->{name}), - uri => $self->uri( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), }; return ( \@options, @reported_to_options ? \@reported_to_options : undef ); diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm index 3d855cf8e..4f3b975b3 100644 --- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -6,6 +6,7 @@ use warnings; use Carp; use mySociety::MaPit; +use FixMyStreet::Geocode::OSM; sub country { return 'NO'; @@ -35,7 +36,7 @@ sub disambiguate_location { } sub area_types { - return ( 'NKO', 'NFY' ); + return ( 'NKO', 'NFY', 'NRA' ); } sub area_min_generation { @@ -55,19 +56,60 @@ sub writetothem_url { sub uri { my ( $self, $uri ) = @_; - $uri = URI->new( $uri ); $uri->query_param( zoom => 3 ) if $uri->query_param('lat') && !$uri->query_param('zoom'); return $uri; } +sub geocode_postcode { + my ( $self, $s ) = @_; + + if ($s =~ /^\d{4}$/) { + my $location = mySociety::MaPit::call('postcode', $s); + if ($location->{error}) { + return { + error => $location->{code} =~ /^4/ + ? _('That postcode was not recognised, sorry.') + : $location->{error} + }; + } + return { + latitude => $location->{wgs84_lat}, + longitude => $location->{wgs84_lon}, + }; + } + return {}; +} + sub geocoded_string_check { my ( $self, $s ) = @_; return 1 if $s =~ /, Norge/; return 0; } +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + return FixMyStreet::Geocode::OSM::closest_road_text( $self, $latitude, $longitude ); +} + +# Used by send-reports, calling find_closest, calling OSM geocoding +sub guess_road_operator { + my ( $self, $inforef ) = @_; + + my $highway = $inforef->{highway} || "unknown"; + my $refs = $inforef->{ref} || "unknown"; + + return "Statens vegvesen" + if $highway eq "trunk" || $highway eq "primary"; + + for my $ref (split(/;/, $refs)) { + return "Statens vegvesen" + if $ref =~ m/E ?\d+/ || $ref =~ m/Fv\d+/i; + } + return ''; +} + sub remove_redundant_councils { my $self = shift; my $all_councils = shift; diff --git a/perllib/FixMyStreet/Cobrand/Lichfield.pm b/perllib/FixMyStreet/Cobrand/Lichfield.pm new file mode 100644 index 000000000..9865bfa7b --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Lichfield.pm @@ -0,0 +1,81 @@ +package FixMyStreet::Cobrand::Lichfield; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +use Carp; +use URI::Escape; +use mySociety::VotingArea; + +sub site_restriction { + return ( "and council like '%2434%'", 'lichfield', { council => '2434' } ); +} + +sub problems_clause { + return { council => { like => '%2434%' } }; +} + +sub problems { + my $self = shift; + return $self->{c}->model('DB::Problem')->search( $self->problems_clause ); +} + +sub base_url { + my $base_url = mySociety::Config::get('BASE_URL'); + if ( $base_url !~ /lichfield/ ) { + $base_url =~ s{http://(?!www\.)}{http://lichfield.}g; + $base_url =~ s{http://www\.}{http://lichfield.}g; + } + return $base_url; +} + +sub site_title { + my ($self) = @_; + return 'Lichfield Council FixMyStreet'; +} + +sub enter_postcode_text { + my ($self) = @_; + return 'Enter a Lichfield postcode, or street name and area'; +} + +sub council_check { + my ( $self, $params, $context ) = @_; + + my $councils = $params->{all_councils}; + my $council_match = defined $councils->{2434}; + if ($council_match) { + return 1; + } + my $url = 'http://www.fixmystreet.com/'; + $url .= 'alert' if $context eq 'alert'; + $url .= '?pc=' . URI::Escape::uri_escape( $self->{c}->req->param('pc') ) + if $self->{c}->req->param('pc'); + my $error_msg = "That location is not covered by Lichfield. +Please visit <a href=\"$url\">the main FixMyStreet site</a>."; + return ( 0, $error_msg ); +} + +# All reports page only has the one council. +sub all_councils_report { + return 0; +} + +# FIXME - need to double check this is all correct +sub disambiguate_location { + return { + centre => '52.688198,-1.804966', + span => '0.1196,0.218675', + bounds => [ '52.807793,-1.586291', '52.584891,-1.963232' ], + }; +} + +sub recent_photos { + my ( $self, $num, $lat, $lon, $dist ) = @_; + $num = 2 if $num == 3; + return $self->problems->recent_photos( $num, $lat, $lon, $dist ); +} + +1; + diff --git a/perllib/FixMyStreet/Cobrand/Southampton.pm b/perllib/FixMyStreet/Cobrand/Southampton.pm index abf6e3082..bd461f5e2 100644 --- a/perllib/FixMyStreet/Cobrand/Southampton.pm +++ b/perllib/FixMyStreet/Cobrand/Southampton.pm @@ -76,9 +76,5 @@ sub recent_photos { return $self->problems->recent_photos( $num, $lat, $lon, $dist ); } -sub tilma_mid_point { - return 189; -} - 1; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index ada19a406..ceced7267 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -151,4 +151,23 @@ sub council { return $result; } +=head2 belongs_to_council + + $belongs_to_council = $user->belongs_to_council( $council_list ); + +Returns true if the user belongs to the comma seperated list of council ids passed in + +=cut + +sub belongs_to_council { + my $self = shift; + my $council = shift; + + my %councils = map { $_ => 1 } split ',', $council; + + return 1 if $self->from_council && $councils{ $self->from_council }; + + return 0; +} + 1; diff --git a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm index a7e6d9736..bf085e32a 100644 --- a/perllib/FixMyStreet/DB/ResultSet/AlertType.pm +++ b/perllib/FixMyStreet/DB/ResultSet/AlertType.pm @@ -27,7 +27,8 @@ sub email_alerts ($) { alert.cobrand_data as alert_cobrand_data, alert.parameter as alert_parameter, alert.parameter2 as alert_parameter2, '; if ($head_table) { $query .= " - $item_table.id as item_id, $item_table.name as item_name, $item_table.text as item_text, + $item_table.id as item_id, $item_table.text as item_text, + $item_table.name as item_name, $item_table.anonymous as item_anonymous, $head_table.* from alert inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id @@ -85,7 +86,7 @@ sub email_alerts ($) { my $url = $cobrand->base_url_for_emails( $row->{alert_cobrand_data} ); if ($row->{item_text}) { $data{problem_url} = $url . "/report/" . $row->{id}; - $data{data} .= $row->{item_name} . ' : ' if $row->{item_name}; + $data{data} .= $row->{item_name} . ' : ' if $row->{item_name} && !$row->{item_anonymous}; $data{data} .= $row->{item_text} . "\n\n------\n\n"; } else { $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n"; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 4de8f2e5f..89f17fa60 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -161,10 +161,10 @@ sub timeline { } sub summary_count { - my ( $rs, $restriction ) = @_; + my ( $rs ) = @_; return $rs->search( - $restriction, + undef, { group_by => ['state'], select => [ 'state', { count => 'id' } ], @@ -173,4 +173,31 @@ sub summary_count { ); } +sub unique_users { + my ( $rs ) = @_; + + return $rs->search( { + state => [ 'confirmed', 'fixed' ], + }, { + select => [ { count => { distinct => 'user_id' } } ], + as => [ 'count' ] + } )->first->get_column('count'); +} + +sub categories_summary { + my ( $rs ) = @_; + + my $categories = $rs->search( { + state => [ 'confirmed', 'fixed' ], + whensent => { '<' => \"NOW() - INTERVAL '4 weeks'" }, + }, { + select => [ 'category', { count => 'id' }, { count => \"case when state='fixed' then 1 else null end" } ], + as => [ 'category', 'c', 'fixed' ], + group_by => [ 'category' ], + result_class => 'DBIx::Class::ResultClass::HashRefInflator' + } ); + my %categories = map { $_->{category} => { total => $_->{c}, fixed => $_->{fixed} } } $categories->all; + return \%categories; +} + 1; diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm index 286a10105..4ae3df368 100644 --- a/perllib/FixMyStreet/Geocode.pm +++ b/perllib/FixMyStreet/Geocode.pm @@ -9,53 +9,24 @@ package FixMyStreet::Geocode; use strict; -use Encode; -use File::Slurp; -use File::Path (); -use LWP::Simple; -use Digest::MD5 qw(md5_hex); use URI::Escape; - -use mySociety::Locale; -use mySociety::MaPit; -use mySociety::PostcodeUtil; -use mySociety::Web qw(NewURL); +use FixMyStreet::Geocode::Bing; +use FixMyStreet::Geocode::Google; # lookup STRING CONTEXT # Given a user-inputted string, try and convert it into co-ordinates using either -# MaPit if it's a postcode, or Google Maps API otherwise. Returns an array of +# MaPit if it's a postcode, or some web API otherwise. Returns an array of # data, including an error if there is one (which includes a location being in # Northern Ireland). The information in the query may be used by cobranded versions # of the site to diambiguate locations. sub lookup { my ($s, $c) = @_; - my ($latitude, $longitude, $error); - if ( $c->cobrand->country eq 'GB') { - if ($s =~ /^\d+$/) { - $error = 'FixMyStreet is a UK-based website that currently works in England, Scotland, and Wales. Please enter either a postcode, or a Great British street name and area.'; - } elsif (mySociety::PostcodeUtil::is_valid_postcode($s)) { - my $location = mySociety::MaPit::call('postcode', $s); - unless ( $error = mapit_check_error( $c, $location ) ) { - $latitude = $location->{wgs84_lat}; - $longitude = $location->{wgs84_lon}; - } - } - } elsif ( $c->cobrand->country eq 'NO') { - if ($s =~ /^\d{4}$/) { - my $location = mySociety::MaPit::call('postcode', $s); - unless ( $error = mapit_check_error( $c, $location ) ) { - $latitude = $location->{wgs84_lat}; - $longitude = $location->{wgs84_lon}; - } - } - } - unless ($error || defined $latitude) { - ($latitude, $longitude, $error) = FixMyStreet::Geocode::string($s, $c); - } - unless ($error || defined $latitude) { - $error = _('Sorry, we could not find that location.'); - } - return ($latitude, $longitude, $error); + my $data = $c->cobrand->geocode_postcode($s); + $data = string($s, $c) + unless $data->{error} || defined $data->{latitude}; + $data->{error} = _('Sorry, we could not find that location.') + unless $data->{error} || defined $data->{latitude}; + return ( $data->{latitude}, $data->{longitude}, $data->{error} ); } # string STRING CONTEXT @@ -68,148 +39,10 @@ sub string { $s = URI::Escape::uri_escape_utf8($s); $s =~ s/%20/+/g; my $params = $c->cobrand->disambiguate_location(); - if ( FixMyStreet->config('BING_MAPS_API_KEY') ) { - my $lookup = FixMyStreet::Geocode::string_bing($s, $c, $params); - return ( $lookup->{latitude}, $lookup->{longitude}, $lookup->{error} ); - } - if ( FixMyStreet->config('GOOGLE_MAPS_API_KEY') ) { - my $lookup = FixMyStreet::Geocode::string_google($s, $c, $params); - return ( $lookup->{latitude}, $lookup->{longitude}, $lookup->{error} ); - } -} - -# string_google STRING CONTEXT -# Looks up on Google 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_google { - my ( $s, $c, $params ) = @_; - - my $url = 'http://maps.google.com/maps/geo?q=' . $s; - $url .= '&ll=' . $params->{centre} if $params->{centre}; - $url .= '&spn=' . $params->{span} if $params->{span}; - $url .= '&gl=' . $params->{country} if $params->{country}; - $url .= '&hl=' . $params->{lang} if $params->{lang}; - - my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'google/'; - my $cache_file = $cache_dir . md5_hex($url); - my $js; - if (-s $cache_file) { - $js = File::Slurp::read_file($cache_file); - } else { - # For some reason adding gl=uk is no longer sufficient to make google - # think we are in the UK for some locations so we explictly add UK to - # the address. We do it here so as not to invalidate existing cache - # entries - if ( $c->cobrand->country eq 'GB' - && $url !~ /,\+UK/ - && $url !~ /united\++kingdom$/ ) - { - if ( $url =~ /&/ ) { - $url =~ s/&/,+UK&/; - } else { - $url .= ',+UK'; - } - } - $url .= '&sensor=false&key=' . FixMyStreet->config('GOOGLE_MAPS_API_KEY'); - $js = LWP::Simple::get($url); - $js = encode_utf8($js) if utf8::is_utf8($js); - File::Path::mkpath($cache_dir); - File::Slurp::write_file($cache_file, $js) if $js && $js !~ /"code":6[12]0/; - } - - if (!$js) { - return { error => _('Sorry, we could not parse that location. Please try again.') }; - } elsif ($js =~ /BT\d/) { - # Northern Ireland, hopefully - return { error => _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.") }; - } - - $js = JSON->new->utf8->allow_nonref->decode($js); - if ($js->{Status}->{code} ne '200') { - return { error => _('Sorry, we could not find that location.') }; - } - - my $results = $js->{Placemark}; - my ( $error, @valid_locations, $latitude, $longitude ); - foreach (@$results) { - next unless $_->{AddressDetails}->{Accuracy} >= 4; - my $address = $_->{address}; - next unless $c->cobrand->geocoded_string_check( $address ); - ( $longitude, $latitude ) = @{ $_->{Point}->{coordinates} }; - push (@$error, $address); - push (@valid_locations, $_); - } - return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; - return { error => $error }; -} - -# string_bing 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_bing { - my ( $s, $c, $params ) = @_; - my $url = "http://dev.virtualearth.net/REST/v1/Locations?q=$s&c=en-GB"; # FIXME nb-NO for Norway - $url .= '&mapView=' . $params->{bounds}[0] . ',' . $params->{bounds}[1] - if $params->{bounds}; - $url .= '&userLocation=' . $params->{centre} if $params->{centre}; - - my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'bing/'; - my $cache_file = $cache_dir . md5_hex($url); - my $js; - if (-s $cache_file) { - $js = File::Slurp::read_file($cache_file); - } else { - $url .= '&key=' . FixMyStreet->config('BING_MAPS_API_KEY'); - $js = LWP::Simple::get($url); - $js = encode_utf8($js) if utf8::is_utf8($js); - File::Path::mkpath($cache_dir); - File::Slurp::write_file($cache_file, $js) if $js; - } - - if (!$js) { - return { error => _('Sorry, we could not parse that location. Please try again.') }; - } elsif ($js =~ /BT\d/) { - return { error => _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.") }; - } - - $js = JSON->new->utf8->allow_nonref->decode($js); - if ($js->{statusCode} ne '200') { - return { error => _('Sorry, we could not find that location.') }; - } - - my $results = $js->{resourceSets}->[0]->{resources}; - my ( $error, @valid_locations, $latitude, $longitude ); - foreach (@$results) { - my $address = $_->{name}; - next unless $_->{address}->{countryRegion} eq 'United Kingdom'; # FIXME This is UK only - ( $latitude, $longitude ) = @{ $_->{point}->{coordinates} }; - push (@$error, $address); - push (@valid_locations, $_); - } - return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; - return { error => $error }; -} - -sub mapit_check_error { - my ( $c, $location ) = @_; - if ($location->{error}) { - return _('That postcode was not recognised, sorry.') if $location->{code} =~ /^4/; - return $location->{error}; - } - if ( $c->cobrand->country eq 'GB') { - my $island = $location->{coordsyst}; - if (!$island) { - return _("Sorry, that appears to be a Crown dependency postcode, which we don't cover."); - } - if ($island eq 'I') { - return _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region."); - } - } - return 0; + return FixMyStreet::Geocode::Bing::string($s, $c, $params) + if FixMyStreet->config('BING_MAPS_API_KEY'); + return FixMyStreet::Geocode::Google::string($s, $c, $params) + if FixMyStreet->config('GOOGLE_MAPS_API_KEY'); } 1; diff --git a/perllib/FixMyStreet/Geocode/Bing.pm b/perllib/FixMyStreet/Geocode/Bing.pm new file mode 100644 index 000000000..cfeffc856 --- /dev/null +++ b/perllib/FixMyStreet/Geocode/Bing.pm @@ -0,0 +1,67 @@ +#!/usr/bin/perl +# +# FixMyStreet::Geocode::Bing +# Geocoding with Bing for FixMyStreet. +# +# Copyright (c) 2011 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ + +package FixMyStreet::Geocode::Bing; + +use strict; +use Encode; +use File::Slurp; +use File::Path (); +use LWP::Simple; +use Digest::MD5 qw(md5_hex); + +# 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 $url = "http://dev.virtualearth.net/REST/v1/Locations?q=$s&c=en-GB"; # FIXME nb-NO for Norway + $url .= '&mapView=' . $params->{bounds}[0] . ',' . $params->{bounds}[1] + if $params->{bounds}; + $url .= '&userLocation=' . $params->{centre} if $params->{centre}; + + my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'bing/'; + my $cache_file = $cache_dir . md5_hex($url); + my $js; + if (-s $cache_file) { + $js = File::Slurp::read_file($cache_file); + } else { + $url .= '&key=' . FixMyStreet->config('BING_MAPS_API_KEY'); + $js = LWP::Simple::get($url); + $js = encode_utf8($js) if utf8::is_utf8($js); + File::Path::mkpath($cache_dir); + File::Slurp::write_file($cache_file, $js) if $js; + } + + if (!$js) { + return { error => _('Sorry, we could not parse that location. Please try again.') }; + } elsif ($js =~ /BT\d/) { + return { error => _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.") }; + } + + $js = JSON->new->utf8->allow_nonref->decode($js); + if ($js->{statusCode} ne '200') { + return { error => _('Sorry, we could not find that location.') }; + } + + my $results = $js->{resourceSets}->[0]->{resources}; + my ( $error, @valid_locations, $latitude, $longitude ); + foreach (@$results) { + my $address = $_->{name}; + next unless $_->{address}->{countryRegion} eq 'United Kingdom'; # FIXME This is UK only + ( $latitude, $longitude ) = @{ $_->{point}->{coordinates} }; + push (@$error, $address); + push (@valid_locations, $_); + } + return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; + return { error => $error }; +} + +1; diff --git a/perllib/FixMyStreet/Geocode/Google.pm b/perllib/FixMyStreet/Geocode/Google.pm new file mode 100644 index 000000000..c37a750a2 --- /dev/null +++ b/perllib/FixMyStreet/Geocode/Google.pm @@ -0,0 +1,85 @@ +#!/usr/bin/perl +# +# FixMyStreet::Geocode +# The geocoding functions for FixMyStreet. +# +# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. +# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ + +package FixMyStreet::Geocode::Google; + +use strict; +use Encode; +use File::Slurp; +use File::Path (); +use LWP::Simple; +use Digest::MD5 qw(md5_hex); + +# string STRING CONTEXT +# Looks up on Google 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 $url = 'http://maps.google.com/maps/geo?q=' . $s; + $url .= '&ll=' . $params->{centre} if $params->{centre}; + $url .= '&spn=' . $params->{span} if $params->{span}; + $url .= '&gl=' . $params->{country} if $params->{country}; + $url .= '&hl=' . $params->{lang} if $params->{lang}; + + my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'google/'; + my $cache_file = $cache_dir . md5_hex($url); + my $js; + if (-s $cache_file) { + $js = File::Slurp::read_file($cache_file); + } else { + # For some reason adding gl=uk is no longer sufficient to make google + # think we are in the UK for some locations so we explictly add UK to + # the address. We do it here so as not to invalidate existing cache + # entries + if ( $c->cobrand->country eq 'GB' + && $url !~ /,\+UK/ + && $url !~ /united\++kingdom$/ ) + { + if ( $url =~ /&/ ) { + $url =~ s/&/,+UK&/; + } else { + $url .= ',+UK'; + } + } + $url .= '&sensor=false&key=' . FixMyStreet->config('GOOGLE_MAPS_API_KEY'); + $js = LWP::Simple::get($url); + $js = encode_utf8($js) if utf8::is_utf8($js); + File::Path::mkpath($cache_dir); + File::Slurp::write_file($cache_file, $js) if $js && $js !~ /"code":6[12]0/; + } + + if (!$js) { + return { error => _('Sorry, we could not parse that location. Please try again.') }; + } elsif ($js =~ /BT\d/) { + # Northern Ireland, hopefully + return { error => _("We do not currently cover Northern Ireland, I'm afraid.") }; + } + + $js = JSON->new->utf8->allow_nonref->decode($js); + if ($js->{Status}->{code} ne '200') { + return { error => _('Sorry, we could not find that location.') }; + } + + my $results = $js->{Placemark}; + my ( $error, @valid_locations, $latitude, $longitude ); + foreach (@$results) { + next unless $_->{AddressDetails}->{Accuracy} >= 4; + my $address = $_->{address}; + next unless $c->cobrand->geocoded_string_check( $address ); + ( $longitude, $latitude ) = @{ $_->{Point}->{coordinates} }; + push (@$error, $address); + push (@valid_locations, $_); + } + return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; + return { error => $error }; +} + +1; diff --git a/perllib/FixMyStreet/Geocode/OSM.pm b/perllib/FixMyStreet/Geocode/OSM.pm new file mode 100644 index 000000000..b1becaa7a --- /dev/null +++ b/perllib/FixMyStreet/Geocode/OSM.pm @@ -0,0 +1,116 @@ +#!/usr/bin/perl +# +# FixMyStreet:Geocode::OSM +# OpenStreetmap forward and reverse geocoding for FixMyStreet. +# +# Copyright (c) 2011 Petter Reinholdtsen. Some rights reserved. +# Email: pere@hungry.com + +package FixMyStreet::Geocode::OSM; + +use warnings; +use strict; + +use Memcached; +use mySociety::Config; +use LWP::Simple; +use XML::Simple; + +my $osmapibase = "http://www.openstreetmap.org/api/"; +my $nominatimbase = "http://nominatim.openstreetmap.org/"; + + +sub lookup_location { + my ($latitude, $longitude, $zoom) = @_; + my $url = + "${nominatimbase}reverse?format=xml&zoom=$zoom&lat=$latitude&lon=$longitude"; + my $key = "OSM:lookup_location:$url"; + my $result = Memcached::get($key); + unless ($result) { + my $j = LWP::Simple::get($url); + if ($j) { + Memcached::set($key, $j, 3600); + my $ref = XMLin($j); + return $ref; + } else { + print STDERR "No reply from $url\n"; + } + return undef; + } + return XMLin($result); +} + +sub _osmxml_to_hash { + my ($xml, $type) = @_; + my $ref = XMLin($xml); + my %tags; + if ('ARRAY' eq ref $ref->{$type}->{tag}) { + map { $tags{$_->{'k'}} = $_->{'v'} } @{$ref->{$type}->{tag}}; + return \%tags; + } else { + return undef; + } +} + +sub get_object_tags { + my ($type, $id) = @_; + my $url = "${osmapibase}0.6/$type/$id"; + my $key = "OSM:get_object_tags:$url"; + my $result = Memcached::get($key); + unless ($result) { + my $j = LWP::Simple::get($url); + if ($j) { + Memcached::set($key, $j, 3600); + return _osmxml_to_hash($j, $type); + } else { + print STDERR "No reply from $url\n"; + } + return undef; + } + return _osmxml_to_hash($result, $type); +} + +# A better alternative might be +# http://www.geonames.org/maps/osm-reverse-geocoder.html#findNearbyStreetsOSM +sub get_nearest_road_tags { + my ( $cobrand, $latitude, $longitude ) = @_; + my $inforef = lookup_location($latitude, $longitude, 16); + if (exists $inforef->{result}->{osm_type} + && 'way' eq $inforef->{result}->{osm_type}) { + my $osmtags = get_object_tags('way', + $inforef->{result}->{osm_id}); + unless ( exists $osmtags->{operator} ) { + $osmtags->{operatorguess} = $cobrand->guess_road_operator( $osmtags ); + } + return $osmtags; + } + return undef; +} + +sub closest_road_text { + my ( $cobrand, $latitude, $longitude ) = @_; + my $str = ''; + my $osmtags = get_nearest_road_tags( $cobrand, $latitude, $longitude ); + if ($osmtags) { + my ($name, $ref) = ('',''); + $name = $osmtags->{name} if exists $osmtags->{name}; + $ref = " ($osmtags->{ref})" if exists $osmtags->{ref}; + if ($name || $ref) { + $str .= _('The following information about the nearest road might be inaccurate or irrelevant, if the problem is close to several roads or close to a road without a name registered in OpenStreetMap.') . "\n\n"; + $str .= sprintf(_("Nearest named road to the pin placed on the map (automatically generated using OpenStreetMap): %s%s"), + $name, $ref) . "\n\n"; + + if (my $operator = $osmtags->{operator}) { + $str .= sprintf(_("Road operator for this named road (from OpenStreetMap): %s"), + $operator) . "\n\n"; + } elsif ($operator = $osmtags->{operatorguess}) { + $str .= sprintf(_("Road operator for this named road (derived from road reference number and type): %s"), + $operator) . "\n\n"; + } + } + } + return $str; +} + +1; + diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm index 6b5a811a6..825e1cd19 100644 --- a/perllib/FixMyStreet/Map.pm +++ b/perllib/FixMyStreet/Map.pm @@ -13,7 +13,6 @@ use strict; use Module::Pluggable sub_name => 'maps', search_path => __PACKAGE__, - except => 'FixMyStreet::Map::Tilma::Original', require => 1; # Get the list of maps we want and load map classes at compile time @@ -23,6 +22,7 @@ use mySociety::Config; use mySociety::Gaze; use mySociety::Locale; use mySociety::Web qw(ent); +use Utils; =head2 allowed_maps @@ -124,8 +124,19 @@ sub click_to_wgs84 { return $map_class->click_to_wgs84(@_); } +=head2 tile_xy_to_wgs84 + +Takes the tile x,y and converts to lat, lon. Legacy to deal with old URLs, +hence hard-coded things. + +=cut + sub tile_xy_to_wgs84 { - return $map_class->tile_xy_to_wgs84(@_); + my ( $x, $y ) = @_; + my $easting = int( $x * (5000/31) + 0.5 ); + my $northing = int( $y * (5000/31) + 0.5 ); + my ( $lat, $lon ) = Utils::convert_en_to_latlon( $easting, $northing ); + return ( $lat, $lon ); } 1; diff --git a/perllib/FixMyStreet/Map/FMS.pm b/perllib/FixMyStreet/Map/FMS.pm index c54ee8f3b..2e40bfde3 100644 --- a/perllib/FixMyStreet/Map/FMS.pm +++ b/perllib/FixMyStreet/Map/FMS.pm @@ -41,17 +41,17 @@ sub map_tiles { my ($self, $x, $y, $z) = @_; if ($z >= 16) { return [ - "http://a.os.openstreetmap.org/sv/$z/" . ($x-1) . "/" . ($y-1) . ".png", - "http://b.os.openstreetmap.org/sv/$z/$x/" . ($y-1) . ".png", - "http://c.os.openstreetmap.org/sv/$z/" . ($x-1) . "/$y.png", - "http://os.openstreetmap.org/sv/$z/$x/$y.png", + "http://a.tilma.mysociety.org/sv/$z/" . ($x-1) . "/" . ($y-1) . ".png", + "http://b.tilma.mysociety.org/sv/$z/$x/" . ($y-1) . ".png", + "http://c.tilma.mysociety.org/sv/$z/" . ($x-1) . "/$y.png", + "http://tilma.mysociety.org/sv/$z/$x/$y.png", ]; } else { return [ - "http://ecn.t0.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y-1, $z) . ".png?g=587&productSet=mmOS", - "http://ecn.t1.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y-1, $z) . ".png?g=587&productSet=mmOS", - "http://ecn.t2.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y, $z) . ".png?g=587&productSet=mmOS", - "http://ecn.t3.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y, $z) . ".png?g=587&productSet=mmOS", + "http://ecn.t0.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y-1, $z) . ".png?g=701&productSet=mmOS", + "http://ecn.t1.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y-1, $z) . ".png?g=701&productSet=mmOS", + "http://ecn.t2.tiles.virtualearth.net/tiles/r" . get_quadkey($x-1, $y, $z) . ".png?g=701&productSet=mmOS", + "http://ecn.t3.tiles.virtualearth.net/tiles/r" . get_quadkey($x, $y, $z) . ".png?g=701&productSet=mmOS", ]; } } diff --git a/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm b/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm deleted file mode 100644 index 34df8dc8b..000000000 --- a/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map::Tilma::1_10k_OL -# Using tilma.mysociety.org with OpenLayers -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::OL::1_10k; -use base 'FixMyStreet::Map::Tilma::OpenLayers'; - -use strict; - -sub tile_width { return 254; } -sub tif_size_m { return 5000; } -sub tif_size_px { return 7874; } -sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); } -sub tile_type { return '10k-full'; } - -sub copyright { - return _('© Crown copyright. All rights reserved. Ministry of Justice 100037819 2008.'); -} - -sub watermark { - return 1; -} - -1; diff --git a/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm b/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm deleted file mode 100644 index 2a531766c..000000000 --- a/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map::TilmaXY -# Using tilma.mysociety.org but accessing images directly. -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::OL::StreetView; -use base 'FixMyStreet::Map::Tilma::OpenLayers'; - -use strict; - -sub tile_width { return 250; } -sub tif_size_m { return 5000; } -sub tif_size_px { return 5000; } -sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); } -sub tile_type { return 'streetview'; } - -sub copyright { - return _('Map contains Ordnance Survey data © Crown copyright and database right 2010.'); -} - -sub watermark { - return 0; -} - -1; diff --git a/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm b/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm deleted file mode 100644 index 31e9eb096..000000000 --- a/perllib/FixMyStreet/Map/Tilma/OpenLayers.pm +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map::Tilma::1_10k_OL -# Using tilma.mysociety.org with OpenLayers -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::OpenLayers; - -use strict; - -sub TILE_WIDTH() { return $FixMyStreet::Map::map_class->tile_width; } -sub SCALE_FACTOR() { return $FixMyStreet::Map::map_class->scale_factor; } -sub TILE_TYPE() { return $FixMyStreet::Map::map_class->tile_type; } - -# display_map C PARAMS -# PARAMS include: -# latitude, longitude for the centre point of the map -# TYPE is 1 if the map is clickable, 0 otherwise. -# PINS is array of pins to show, location and colour -sub display_map { - my ($self, $c, %params) = @_; - $c->stash->{map} = { - %params, - type => 'tilma/openlayers', - tile_type => TILE_TYPE, - tilewidth => TILE_WIDTH, - watermark => $self->watermark(), - copyright => $self->copyright(), - maxResolution => SCALE_FACTOR / TILE_WIDTH, - }; -} - -1; diff --git a/perllib/FixMyStreet/Map/Tilma/Original.pm b/perllib/FixMyStreet/Map/Tilma/Original.pm deleted file mode 100644 index 46cf3ad40..000000000 --- a/perllib/FixMyStreet/Map/Tilma/Original.pm +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map -# Adding the ability to have different maps on FixMyStreet. -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::Original; - -use strict; -use LWP::Simple; - -use mySociety::GeoUtil; -use mySociety::Locale; -use mySociety::Web qw(ent NewURL); -use Utils; -use RABX; - -sub TILE_WIDTH() { return $FixMyStreet::Map::map_class->tile_width; } -sub SCALE_FACTOR() { return $FixMyStreet::Map::map_class->scale_factor; } -sub TILE_TYPE() { return $FixMyStreet::Map::map_class->tile_type; } - -sub _ll_to_en { - my ($lat, $lon) = @_; - return Utils::convert_latlon_to_en( $lat, $lon ); -} - -# 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 $mid_point = TILE_WIDTH; # Map is 2 TILE_WIDTHs in size, square. - if (my $mp = $c->cobrand->tilma_mid_point) { - $mid_point = $mp; - } - - # convert map center point to easting, northing - ( $params{easting}, $params{northing} ) = - _ll_to_en( $params{latitude}, $params{longitude} ); - - # X/Y tile co-ords may be overridden in the query string - my @vars = qw(x y); - my %input = map { $_ => $c->req->params->{$_} || '' } @vars; - ($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0; - ($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0; - - my ($x, $y, $px, $py) = os_to_px_with_adjust($c, $params{easting}, $params{northing}, $input{x}, $input{y}); - - foreach my $pin (@{$params{pins}}) { - my ( $e, $n ) = _ll_to_en( $pin->{latitude}, $pin->{longitude} ); - $pin->{px} = os_to_px($e, $x); - $pin->{py} = os_to_px($n, $y, 1); - } - - $px = defined($px) ? $mid_point - $px : 0; - $py = defined($py) ? $mid_point - $py : 0; - $x = int($x)<=0 ? 0 : $x; - $y = int($y)<=0 ? 0 : $y; - my $url = 'http://tilma.mysociety.org/tileserver/' . TILE_TYPE . '/'; - my $tiles_url = $url . ($x-1) . '-' . $x . ',' . ($y-1) . '-' . $y . '/RABX'; - my $tiles = LWP::Simple::get($tiles_url); - my $tileids = RABX::unserialise($tiles); - $c->stash->{map} = { - %params, - type => 'tilma/original', - tiles => $tiles, - url => $url, - tileids => $tileids, - x => $x, - y => $y, - px => $px, - py => $py, - tile_type => TILE_TYPE, - tilewidth => TILE_WIDTH, - watermark => $self->watermark(), - copyright => $self->copyright(), - }; -} - -sub display_pin { - my ($c, $px, $py, $col, $id, $title, $num) = @_; - $num = '' if !$num || $num > 9; - my $host = $c->cobrand->base_url_with_lang; - my %cols = (red=>'R', green=>'G', blue=>'B', purple=>'P'); - my $out = '<img class="pin" src="' . $host . '/i/pin' . $cols{$col} - . $num . '.gif" alt="' . _('Problem') . '" style="top:' . ($py-59) - . 'px; left:' . ($px) . 'px; position: absolute;">'; - return $out unless $id; - my $url = $c->uri_for( '/report/' . $id ); - $out = '<a title="' . ent($title) . '" href="' . $url . '">' . $out . '</a>'; - return $out; -} - -sub map_pins { - my ($self, $c, $interval) = @_; - - # Our current X/Y middle of visible map - my $x = ( $c->req->param('x') || 0 ) + 0; - my $y = ( $c->req->param('y') || 0 ) + 0; - - # Where we started as that's the (0,0) we have to work to - my $sx = ( $c->req->param('sx') || 0 ) + 0; - my $sy = ( $c->req->param('sy') || 0 ) + 0; - - my $e = tile_to_os($x); - my $n = tile_to_os($y); - - my ( $lat, $lon ) = Utils::convert_en_to_latlon( $e, $n ); - - my ( $around_map, $around_map_list, $nearby, $dist ) = - FixMyStreet::Map::map_features( $c, $lat, $lon, $interval ); - - my $pins = ''; - foreach (@$around_map) { - my ( $easting, $northing ) = - _ll_to_en( $_->latitude, $_->longitude ); - my $px = os_to_px($easting, $sx); - my $py = os_to_px($northing, $sy, 1); - my $col = exists FixMyStreet::DB::Result::Problem->fixed_states()->{$_->state} ? 'green' : 'red'; - $pins .= display_pin($c, $px, $py, $col, $_->id, $_->title); - } - - foreach (@$nearby) { - my $p = $_->problem; - my ( $easting, $northing ) = - _ll_to_en( $p->latitude, $p->longitude ); - my $px = os_to_px($easting, $sx); - my $py = os_to_px($northing, $sy, 1); - my $col = exists FixMyStreet::DB::Result::Problem->fixed_states()->{$p->state} ? 'green' : 'red'; - $pins .= display_pin($c, $px, $py, $col, $p->id, $p->title); - } - - return ($pins, $around_map_list, $nearby, $dist); -} - -# P is easting or northing -# C is centre tile reference of displayed map -sub os_to_px { - my ($p, $c, $invert) = @_; - return tile_to_px(os_to_tile($p), $c, $invert); -} - -# 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, $invert) = @_; - $p = TILE_WIDTH * ($p - $c + 1); - $p = 2 * TILE_WIDTH - $p if $invert; - $p = int($p + .5 * ($p <=> 0)); - return $p; -} - -# Tile co-ordinates are linear scale of OS E/N -# Will need more generalising when more zooms appear -sub os_to_tile { - return $_[0] / SCALE_FACTOR; -} - -sub tile_to_os { - return int($_[0] * SCALE_FACTOR + 0.5); -} - -=head2 tile_xy_to_wgs84 - - ($lat, $lon) = tile_xy_to_wgs84( $x, $y ); - -Takes the tile x,y and converts to lat, lon. - -=cut - -sub tile_xy_to_wgs84 { - my ( $self, $x, $y ) = @_; - - my $easting = tile_to_os($x); - my $northing = tile_to_os($y); - - my ( $lat, $lon ) = Utils::convert_en_to_latlon( $easting, $northing ); - return ( $lat, $lon ); -} - - -sub click_to_tile { - my ($pin_tile, $pin, $invert) = @_; - $pin -= TILE_WIDTH while $pin > TILE_WIDTH; - $pin += TILE_WIDTH while $pin < 0; - $pin = TILE_WIDTH - $pin if $invert; # image submits measured from top down - return $pin_tile + $pin / TILE_WIDTH; -} - -# Given some click co-ords (the tile they were on, and where in the -# tile they were), convert to OSGB36 and return. -sub click_to_os { - my ($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, 1); - my $easting = tile_to_os($tile_x); - my $northing = tile_to_os($tile_y); - return ($easting, $northing); -} - -# Given some click co-ords (the tile they were on, and where in the -# tile they were), convert to WGS84 and return. -sub click_to_wgs84 { - my $self = shift; - my $c = shift; - my ( $easting, $northing ) = click_to_os(@_); - my ( $lat, $lon ) = mySociety::GeoUtil::national_grid_to_wgs84( $easting, $northing, 'G' ); - return ( $lat, $lon ); -} - -# Given (E,N) and potential override (X,Y), return the X/Y tile for the centre -# of the map (either to get the point near the middle, or the override X,Y), -# and the pixel co-ords of the point, relative to that map. -sub os_to_px_with_adjust { - my ($c, $easting, $northing, $in_x, $in_y) = @_; - - my $x = os_to_tile($easting); - my $y = os_to_tile($northing); - my $x_tile = $in_x || int($x); - my $y_tile = $in_y || int($y); - - # Try and have point near centre of map - if (!$in_x && $x - $x_tile > 0.5) { - $x_tile += 1; - } - if (!$in_y && $y - $y_tile > 0.5) { - $y_tile += 1; - } - - my $px = os_to_px($easting, $x_tile); - my $py = os_to_px($northing, $y_tile, 1); - if ($c->cobrand->tilma_mid_point == 189) { # Map is 380px, so might need to adjust - if (!$in_x && $px > 380) { - $x_tile++; - $px = os_to_px($easting, $x_tile); - } - if (!$in_y && $py > 380) { - $y_tile--; - $py = os_to_px($northing, $y_tile, 1); - } - } - - return ($x_tile, $y_tile, $px, $py); -} - -1; diff --git a/perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm b/perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm deleted file mode 100644 index 722df2a46..000000000 --- a/perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map -# Adding the ability to have different maps on FixMyStreet. -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::Original::1_10k; -use base 'FixMyStreet::Map::Tilma::Original'; - -use strict; - -sub tile_width { return 254; } -sub tif_size_m { return 5000; } -sub tif_size_px { return 7874; } -sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); } -sub tile_type { return '10k-full'; } - -sub copyright { - return _('© Crown copyright. All rights reserved. Ministry of Justice 100037819 2008.'); -} - -sub watermark { - return 1; -} - -1; diff --git a/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm b/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm deleted file mode 100644 index fe03fdb00..000000000 --- a/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -# -# FixMyStreet:Map -# Adding the ability to have different maps on FixMyStreet. -# -# Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/ - -package FixMyStreet::Map::Tilma::Original::StreetView; -use base 'FixMyStreet::Map::Tilma::Original'; - -use strict; - -sub tile_width { return 250; } -sub tif_size_m { return 5000; } -sub tif_size_px { return 5000; } -sub scale_factor { return tif_size_m() / (tif_size_px() / tile_width()); } -sub tile_type { return 'streetview'; } - -sub copyright { - return _('Map contains Ordnance Survey data © Crown copyright and database right 2010.'); -} - -sub watermark { - return 0; -} - -1; |