aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/import-flickr14
-rwxr-xr-xbin/send-reports46
-rwxr-xr-xbin/test-run79
-rwxr-xr-xbin/update-areas4
-rw-r--r--conf/general-example3
-rw-r--r--conf/httpd.conf14
-rw-r--r--db/migrate_from_osgb36_to_wgs84.pl201
-rw-r--r--db/schema.sql40
-rw-r--r--notes/INSTALL4
-rw-r--r--notes/osgb36_to_wgs84_notes.txt77
-rw-r--r--perllib/Carp/Always.pm162
-rw-r--r--perllib/Cobrands/Barnet/Util.pm4
-rw-r--r--perllib/FixMyStreet/Alert.pm14
-rw-r--r--perllib/FixMyStreet/Geocode.pm36
-rw-r--r--perllib/FixMyStreet/Map.pm93
-rw-r--r--perllib/FixMyStreet/Map/Tilma/Original.pm57
-rw-r--r--perllib/Page.pm5
-rw-r--r--perllib/Problems.pm30
-rw-r--r--perllib/Utils.pm70
-rwxr-xr-xt/Page.t27
-rw-r--r--t/utils.t41
-rw-r--r--templates/emails/submit-brent4
-rw-r--r--templates/emails/submit-council4
-rwxr-xr-xweb-admin/index.cgi6
-rwxr-xr-xweb/alert.cgi54
-rwxr-xr-xweb/import.cgi31
-rwxr-xr-xweb/index.cgi235
-rwxr-xr-xweb/questionnaire.cgi4
-rwxr-xr-xweb/rss.cgi33
29 files changed, 1099 insertions, 293 deletions
diff --git a/bin/import-flickr b/bin/import-flickr
index 992a6ac8e..21ea5822a 100755
--- a/bin/import-flickr
+++ b/bin/import-flickr
@@ -72,19 +72,19 @@ while ($result =~ /<photo id="([^"]*)" owner="([^"]*)" secret="([^"]*)" server="
sub problem_create {
my ($photo_id, $owner, $title, $lat, $lon, $image) = @_;
my ($name, $email) = dbh()->selectrow_array("select name, email from partial_user where service='flickr' and nsid=?", {}, $owner);
- my ($easting, $northing) = (0,0);
+
+ # set some defaults
$name ||= '';
- if ($lat && $lon) {
- # XXX This appears to be going wrong :(
- ($easting, $northing) = mySociety::GeoUtil::wgs84_to_national_grid($lat, $lon, 'G');
- }
+ $lat ||= 0;
+ $lon ||= 0;
+
my $id = dbh()->selectrow_array("select nextval('problem_id_seq')");
Utils::workaround_pg_bytea("insert into problem
- (id, postcode, easting, northing, title, detail, name,
+ (id, postcode, latitude, longitude, title, detail, name,
email, phone, photo, state, used_map, anonymous, category, areas)
values
(?, '', ?, ?, ?, '', ?, ?, '', ?, 'partial', 't', 'f', '', '')", 7,
- $id, $easting, $northing, $title, $name, $email, $image
+ $id, $latitude, $longitude, $title, $name, $email, $image
);
dbh()->do('insert into flickr_imported (id, problem_id) values (?, ?)', {}, $photo_id, $id);
diff --git a/bin/send-reports b/bin/send-reports
index 9da9b6238..6254337c6 100755
--- a/bin/send-reports
+++ b/bin/send-reports
@@ -27,6 +27,7 @@ use mySociety::Config;
use mySociety::DBHandle qw(dbh);
use mySociety::Email;
use mySociety::EmailUtil;
+use mySociety::GeoUtil;
use mySociety::Locale;
use mySociety::MaPit;
use mySociety::Random qw(random_bytes);
@@ -49,10 +50,12 @@ my $base_url = mySociety::Config::get('BASE_URL');
my $site = CronFns::site($base_url);
my $query = "SELECT id, council, category, title, detail, name, email, phone,
- used_map, easting, northing, (photo is not null) as has_photo, lang,
+ used_map, latitude, longitude, (photo is not null) as has_photo, lang,
cobrand, cobrand_data
- FROM problem WHERE state in ('confirmed','fixed') AND whensent IS NULL
- AND council IS NOT NULL";
+ FROM problem
+ WHERE state in ('confirmed','fixed')
+ AND whensent IS NULL
+ AND council IS NOT NULL";
my $unsent = dbh()->selectall_arrayref($query, { Slice => {} });
my (%notgot, %note);
@@ -81,7 +84,7 @@ foreach my $row (@$unsent) {
# Template variables for the email
my $email_base_url = Cobrand::base_url_for_emails($cobrand, $cobrand_data);
- my %h = map { $_ => $row->{$_} } qw/title detail name email phone category easting northing/;
+ my %h = map { $_ => $row->{$_} } qw/title detail name email phone category latitude longitude/;
$h{url} = $email_base_url . '/report/' . $row->{id};
$h{phone_line} = $h{phone} ? _('Phone:') . " $h{phone}\n\n" : '';
if ($row->{has_photo}) {
@@ -95,6 +98,21 @@ foreach my $row (@$unsent) {
: _('The user could not locate the problem on a map, but to see the area around the location they entered');
$h{closest_address} = '';
$h{closest_address_machine} = '';
+
+ # If we are in the UK include eastings and northings
+ $h{easting_northing} = '';
+ if ( mySociety::Config::get('COUNTRY') eq 'GB' ) {
+
+ ( $h{easting}, $h{northing} ) =
+ mySociety::GeoUtil::wgs84_to_national_grid( #
+ $h{latitude}, $h{longitude}, 'G'
+ );
+
+ # email templates don't have conditionals so we need to farmat this here
+ $h{easting_northing} #
+ = "Easting: $h{easting}\n\n" #
+ . "Northing: $h{northing}\n\n";
+ }
my (@to, @recips, $template, $areas_info);
if ($site eq 'emptyhomes') {
@@ -137,8 +155,8 @@ foreach my $row (@$unsent) {
my ($council_email, $confirmed, $note) = dbh()->selectrow_array(
"SELECT email,confirmed,note FROM contacts WHERE deleted='f'
and area_id=? AND category=?", {}, $council, $row->{category});
- $council_email = essex_contact($row->{easting}, $row->{northing}) if $council == 2225;
- $council_email = oxfordshire_contact($row->{easting}, $row->{northing}) if $council == 2237 && $council_email eq 'SPECIAL';
+ $council_email = essex_contact($row->{latitude}, $row->{longitude}) if $council == 2225;
+ $council_email = oxfordshire_contact($row->{latitude}, $row->{longitude}) if $council == 2237 && $council_email eq 'SPECIAL';
unless ($confirmed) {
$all_confirmed = 0;
$note = 'Council ' . $row->{council} . ' deleted'
@@ -255,13 +273,19 @@ if ($verbose) {
}
}
+sub _get_district_for_contact {
+ my ( $lat, $lon ) = @_;
+ my $district =
+ mySociety::MaPit::call( 'point', "4326/$lon,$lat", type => 'DIS' );
+ ($district) = keys %$district;
+ return $district;
+}
+
# Essex has different contact addresses depending upon the district
# Might be easier if we start storing in the db all areas covered by a point
# Will do for now :)
sub essex_contact {
- my ($E, $N) = @_;
- my $district = mySociety::MaPit::call('point', "27700/$E,$N", type => 'DIS');
- ($district) = keys %$district;
+ my $district = _get_district_for_contact(@_);
my $email;
$email = 'eastarea' if $district == 2315 || $district == 2312;
$email = 'midarea' if $district == 2317 || $district == 2314 || $district == 2316;
@@ -273,9 +297,7 @@ sub essex_contact {
# Oxfordshire has different contact addresses depending upon the district
sub oxfordshire_contact {
- my ($E, $N) = @_;
- my $district = mySociety::MaPit::call('point', "27700/$E,$N", type => 'DIS');
- ($district) = keys %$district;
+ my $district = _get_district_for_contact(@_);
my $email;
$email = 'northernarea' if $district == 2419 || $district == 2420 || $district == 2421;
$email = 'southernarea' if $district == 2417 || $district == 2418;
diff --git a/bin/test-run b/bin/test-run
index 15f9c3ed2..85ce87797 100755
--- a/bin/test-run
+++ b/bin/test-run
@@ -27,11 +27,14 @@ use FindBin;
use mySociety::Config;
mySociety::Config::set_file('../conf/general');
use mySociety::DBHandle qw(dbh);
+use mySociety::GeoUtil;
use mySociety::WebTestHarness;
use Test::Harness;
use File::Find;
use lib "$FindBin::Bin/../perllib";
use Cobrand;
+use FixMyStreet::Geocode;
+use Utils;
my @actions = ('report', 'update', 'questionnaire', 'alert', 'static', 'cobrand', 'unit', 'eha_alert', 'import', 'rss');
my %actions_desc = (
@@ -213,6 +216,11 @@ sub submit_postcode{
sub submit_report {
my ($postcode, $x, $y, $easting, $northing, $user_num, $council, $texts, $cobrand ) = @_;
my @messages = @{$texts};
+
+ # convert easting, northing to lat lon
+ ( $latitude, $longitude ) =
+ Utils::convert_en_to_latlon( $easting, $northing );
+
submit_postcode($cobrand, $postcode, $messages[0]);
{
# Writing values to hidden fields, so switching
@@ -235,8 +243,8 @@ sub submit_report {
y => $y,
pc => $postcode,
council => -1,
- easting => $easting,
- northing => $northing,
+ latitude => $latitude,
+ longitude => $longitude,
title => 'My test problem',
detail => 'Detail of my test problem',
anonymous => 1,
@@ -377,13 +385,23 @@ sub do_alert {
# sign up for alerts in an area
my $postcode = 'EH1 2NG';
- my $x = 2015; my $e = 325066;
- my $y = 4175; my $n = 673533;
+
+ my $x = 2015;
+ my $y = 4175;
+
+ my $e = 325066;
+ my $n = 673533;
+
+ # get the lat,lon from the postcode so that it matches
+ my ( $lat, $lon ) =
+ map { Utils::truncate_coordinate($_) }
+ FixMyStreet::Geocode::lookup( $postcode, undef );
+
my $messages = english_fms_messages();
submit_postcode('', $postcode, 'Problems in this area');
$wth->browser_follow_link(text => 'Email me new local problems');
$wth->browser_submit_form(form_name => 'alerts',
- fields => {feed => "local:" . $e . ":" . $n,
+ fields => {feed => "local:$lat:$lon",
rznvy => email_n(2)} );
$wth->browser_check_contents('Nearly Done!');
my $confirmation_email = $wth->email_get_containing(
@@ -396,7 +414,7 @@ sub do_alert {
$wth->browser_check_contents('successfully confirmed your alert');
# create and confirm a new problem in the area
- submit_report($postcode, $x, $y, 325000, 673387.096774193, 3, undef, $messages, '');
+ submit_report($postcode, $x, $y, $e, $n, 3, undef, $messages, '');
# run the alert script
call_send_emails();
@@ -419,6 +437,12 @@ sub do_eha_alert {
my $postcode = 'EH1 2NG';
my $x = 2015; my $e = 325066;
my $y = 4175; my $n = 673533;
+
+ # get the lat,lon from the postcode so that it matches
+ my ( $lat, $lon ) =
+ map { Utils::truncate_coordinate($_) }
+ FixMyStreet::Geocode::lookup( $postcode, undef );
+
my @texts = ('Eiddo gwag yn yr ardal hon',
'Adrodd am eiddo gwag',
'Nawr, gwiriwch eich e-bost',
@@ -428,7 +452,7 @@ sub do_eha_alert {
submit_postcode('cy.emptyhomes.', $postcode, $texts[0]);
$wth->browser_follow_link(text => 'Anfonwch fanylion eiddo gwag lleol newydd ataf i drwy\'r e-bost');
$wth->browser_submit_form(form_name => 'alerts',
- fields => {feed => "local:" . $e . ":" . $n,
+ fields => {feed => "local:$lat:$lon",
rznvy => email_n(4)} );
$wth->browser_check_contents($texts[2]);
my $confirmation_email = $wth->email_get_containing(
@@ -441,7 +465,7 @@ sub do_eha_alert {
$wth->browser_check_contents('Rydych wedi cadarnhau\'ch hysbysiad yn llwyddiannus.');
# create and confirm a new problem in the area
- submit_report($postcode, $x, $y, 325000, 673387.096774193, 3, undef, \@texts, 'cy.emptyhomes.');
+ submit_report($postcode, $x, $y, $e, $n, 3, undef, \@texts, 'cy.emptyhomes.');
# run the alert script
call_send_emails();
@@ -513,16 +537,37 @@ sub do_static {
# Check RSS feeds redirect to the right places and so on.
# Just checks header, doesn't check any contents.
sub do_rss {
- $wth->browser_get($base_url . '/rss/l/52.5/-1.9');
- die "Lat/lon redirect failed" unless $wth->browser_uri() =~ m{/rss/n/406886,289126$};
- $wth->browser_get($base_url . '/rss/2524/1779');
- die "Tile redirect failed" unless $wth->browser_uri() =~ m{/rss/n/407097,286935$};
- $wth->browser_get($base_url . '/rss/n/407097,286935');
+
+ my %redirects = (
+
+ # should always go to lat lon
+ '/rss/n/406886,289126' => '/rss/l/52.499993,-1.899993',
+ '/rss/2524/1779' => '/rss/l/52.480294,-1.896931',
+ '/rss/pc/SW1A1AA' => '/rss/l/51.501010,-0.141587',
+ '/rss/l/52.5/-1.9' => '/rss/l/52.5/-1.9',
+
+ # go to reports
+ '/rss/area/Birmingham' => '/rss/reports/Birmingham',
+ '/rss/area/Birmingham/Lozells' => '/rss/reports/Birmingham/Lozells',
+ );
+
+ my $error_count = 0;
+ foreach my $from ( sort keys %redirects ) {
+ my $to = $redirects{$from};
+ $wth->browser_get( $base_url . $from );
+ my ($got) = $wth->browser_uri() =~ m{(/rss/.*)$};
+
+ next if $got eq $to;
+
+ warn "RSS redirect from '$from' to '$to' failed - got '$got'";
+ $error_count++;
+ }
+
+ die "Found errors doing redirect - aborting" if $error_count;
+
+ $wth->browser_get($base_url . '/rss/l/52.5/-1.94');
$wth->browser_check_contents('New local problems on FixMyStreet');
- $wth->browser_get($base_url . '/rss/pc/SW1A1AA');
- die "Postcode redirect failed" unless $wth->browser_uri() =~ m{/rss/n/529090,179645$};
- $wth->browser_get($base_url . '/rss/area/Birmingham');
- die "One-tier redirect failed" unless $wth->browser_uri() =~ m{/rss/reports/Birmingham$};
+
$wth->browser_get($base_url . '/rss/reports/Birmingham');
$wth->browser_check_contents('New problems to Birmingham City Council on FixMyStreet');
$wth->browser_get($base_url . '/rss/reports/Birmingham/Lozells');
diff --git a/bin/update-areas b/bin/update-areas
index ed463708a..bbc933faf 100755
--- a/bin/update-areas
+++ b/bin/update-areas
@@ -33,11 +33,11 @@ BEGIN {
}
print "Fetching problems...\n";
-my $ids = select_all("select id, easting, northing from problem where areas=''");
+my $ids = select_all("select id, latitude, longitude from problem where areas=''");
print "Updating areas...\n";
my $c = 0;
foreach (@$ids) {
- my $areas = mySociety::MaPit::get_voting_areas_by_location({easting=>$_->{easting}, northing=>$_->{northing}}, 'polygon');
+ my $areas = mySociety::MaPit::get_voting_areas_by_location({latitude=>$_->{latitude}, longitude=>$_->{longitude}}, 'polygon');
$areas = ',' . join(',', sort keys %$areas) . ',';
dbh()->do('update problem set areas=? where id=?', {}, $areas, $_->{id});
dbh()->commit();
diff --git a/conf/general-example b/conf/general-example
index 420f98bf5..20872ae28 100644
--- a/conf/general-example
+++ b/conf/general-example
@@ -27,6 +27,9 @@ define('OPTION_BCI_DB_PASS', '');
define('OPTION_BASE_URL', 'http://www.example.org');
+# Which country are you operating in? ISO3166-alpha2 code please
+define('OPTION_COUNTRY', 'GB');
+
define('OPTION_TESTING_EMAIL', 'testing@example.com');
define('OPTION_EMAIL_DOMAIN', 'example.org');
define('OPTION_CONTACT_EMAIL', 'team@'.OPTION_EMAIL_DOMAIN);
diff --git a/conf/httpd.conf b/conf/httpd.conf
index 67170bad5..8bc50645a 100644
--- a/conf/httpd.conf
+++ b/conf/httpd.conf
@@ -60,14 +60,14 @@ RewriteRule ^/[Tt]/([0-9A-Za-z]{16,18}).*$ /tms-signup.cgi?token=$1
RewriteRule ^/rss/([0-9]+)$ /rss.cgi?type=new_updates;id=$1 [QSA]
# RSS feeds for new local problems
-RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2 [QSA]
-RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2 [QSA]
-RewriteRule ^/rss/l/([0-9.-]+)[,/]([0-9.-]+)$ /rss.cgi?type=local_problems;lat=$1;lon=$2 [QSA]
-RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2;d=$3 [QSA]
-RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2;d=$3 [QSA]
+RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2 [QSA]
+RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2 [QSA]
+RewriteRule ^/rss/l/([0-9.-]+)[,/]([0-9.-]+)$ /rss.cgi?type=local_problems;lat=$1;lon=$2 [QSA]
+RewriteRule ^/rss/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;x=$1;y=$2;d=$3 [QSA]
+RewriteRule ^/rss/n/([0-9]+)[,/]([0-9]+)/([0-9]+)$ /rss.cgi?type=local_problems;e=$1;n=$2;d=$3 [QSA]
RewriteRule ^/rss/l/([0-9.-]+)[,/]([0-9.-]+)/([0-9]+)$ /rss.cgi?type=local_problems;lat=$1;lon=$2;d=$3 [QSA]
-RewriteRule ^/rss/pc/(.*)$ /rss.cgi?type=local_problems;pc=$1 [QSA]
-RewriteRule ^/rss/problems$ /rss.cgi?type=new_problems [QSA]
+RewriteRule ^/rss/pc/(.*)$ /rss.cgi?type=local_problems;pc=$1 [QSA]
+RewriteRule ^/rss/problems$ /rss.cgi?type=new_problems [QSA]
# RSS feeds for voting areas
RewriteRule ^/rss/council/([0-9]+)$ /rss/reports/$1 [R=permanent]
diff --git a/db/migrate_from_osgb36_to_wgs84.pl b/db/migrate_from_osgb36_to_wgs84.pl
new file mode 100644
index 000000000..62eac8296
--- /dev/null
+++ b/db/migrate_from_osgb36_to_wgs84.pl
@@ -0,0 +1,201 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+=head1 DESCRIPTION
+
+This script will take a FMS database with eastings and northings in and migrate
+it to latitude and longitude. It touches the following tables and functions:
+
+=cut
+
+use FindBin;
+use lib "$FindBin::Bin/../perllib";
+use lib "$FindBin::Bin/../commonlib/perllib";
+
+use mySociety::Config;
+use mySociety::DBHandle qw(dbh);
+use Utils;
+
+BEGIN {
+ mySociety::Config::set_file("$FindBin::Bin/../conf/general");
+ mySociety::DBHandle::configure(
+ Name => mySociety::Config::get('BCI_DB_NAME'),
+ User => mySociety::Config::get('BCI_DB_USER'),
+ Password => mySociety::Config::get('BCI_DB_PASS'),
+ Host => mySociety::Config::get( 'BCI_DB_HOST', undef ),
+ Port => mySociety::Config::get( 'BCI_DB_PORT', undef )
+ );
+}
+
+my $UPDATE_BATCH_SIZE = 1; # FIXME - should be ~ 500
+
+migrate_problem_table();
+migrate_problem_find_nearby_function();
+migrate_alert_table();
+
+=head2 problem table
+
+Add columns 'latitude' and 'longitude'.
+Update all entries coverting from e,n to lon,lat.
+Make the lat, lon columns not null.
+Drop the 'problem_state_easting_northing_idx' index.
+Create new index 'problem_state_latitude_longitude_idx'.
+Drop the 'easting' and 'northing' columns.
+
+=cut
+
+sub migrate_problem_table {
+
+ my $dbh = dbh();
+
+ # add columns
+ print "add latitude, longitude columns\n";
+ $dbh->do("ALTER TABLE problem ADD $_ double precision")
+ for qw(latitude longitude);
+ $dbh->commit;
+
+ # create a query for rows that need converting
+ my $rows_to_convert_query = $dbh->prepare( #
+ "SELECT id, easting, northing FROM problem"
+ . " WHERE latitude is NULL limit $UPDATE_BATCH_SIZE"
+ );
+
+ # update query
+ my $update_lat_lon_query = $dbh->prepare( #
+ "UPDATE problem SET latitude = ?, longitude = ? WHERE id = ?"
+ );
+
+ # loop through the entries in batches updating rows that need it. Do this in
+ # Perl rather than SQL for conveniance.
+ while (1) {
+ $rows_to_convert_query->execute;
+ last unless $rows_to_convert_query->rows;
+ while ( my $r = $rows_to_convert_query->fetchrow_hashref ) {
+ my ( $latitude, $longitude ) =
+ Utils::convert_en_to_latlon( $r->{easting}, $r->{northing} );
+ print "update problem $r->{id}: ( $latitude, $longitude )\n";
+ $update_lat_lon_query->execute( $latitude, $longitude, $r->{id} );
+ }
+ $dbh->commit; # every batch of updates
+ }
+
+ print "make latitude, longitude columns not null\n";
+ $dbh->do("ALTER TABLE problem ALTER COLUMN $_ SET NOT NULL")
+ for qw(latitude longitude);
+ $dbh->commit;
+
+ # drop old index, create new one
+ print "drop and create indexes\n";
+ $dbh->do("DROP INDEX problem_state_easting_northing_idx");
+ $dbh->do( "CREATE INDEX problem_state_latitude_longitude_idx "
+ . "ON problem(state, latitude, longitude)" );
+ $dbh->commit;
+
+ # drop columns
+ print "drop easting, northing columns\n";
+ $dbh->do("ALTER TABLE problem DROP $_") for qw(easting northing);
+ $dbh->commit;
+
+}
+
+=head2 problem_find_nearby function
+
+Convert to use lat and long.
+Also swap parameter order so that it is lat,lon rather than lon,lat to be consistent with pledgebank etc
+
+=cut
+
+sub migrate_problem_find_nearby_function {
+ my $dbh = dbh();
+
+ print "drop the existing problem_find_nearby function\n";
+ $dbh->do(
+"DROP FUNCTION problem_find_nearby ( double precision, double precision, double precision)"
+ );
+
+ print "create the new one\n";
+ $dbh->do(<<'SQL_END');
+ create function problem_find_nearby(double precision, double precision, double precision)
+ returns setof problem_nearby_match as
+ '
+ -- trunc due to inaccuracies in floating point arithmetic
+ select problem.id,
+ R_e() * acos(trunc(
+ (sin(radians($1)) * sin(radians(latitude))
+ + cos(radians($1)) * cos(radians(latitude))
+ * cos(radians($2 - longitude)))::numeric, 14)
+ ) as distance
+ from problem
+ where
+ longitude is not null and latitude is not null
+ and radians(latitude) > radians($1) - ($3 / R_e())
+ and radians(latitude) < radians($1) + ($3 / R_e())
+ and (abs(radians($1)) + ($3 / R_e()) > pi() / 2 -- case where search pt is near pole
+ or angle_between(radians(longitude), radians($2))
+ < $3 / (R_e() * cos(radians($1 + $3 / R_e()))))
+ -- ugly -- unable to use attribute name "distance" here, sadly
+ and R_e() * acos(trunc(
+ (sin(radians($1)) * sin(radians(latitude))
+ + cos(radians($1)) * cos(radians(latitude))
+ * cos(radians($2 - longitude)))::numeric, 14)
+ ) < $3
+ order by distance desc
+ ' language sql
+SQL_END
+
+ $dbh->commit;
+}
+
+=head2 alert table
+
+NOTE: only for alert_types 'local_problems' or 'local_problems_state'
+
+parameter: convert easting to longitude
+parameter2: convert nothing to latitude
+
+create a new column 'is_migrated' to use during migration in case of crash.
+
+=cut
+
+sub migrate_alert_table {
+ my $dbh = dbh();
+
+ print "Adding 'is_migrated' column\n";
+ $dbh->do("ALTER TABLE alert ADD COLUMN is_migrated bool DEFAULT false");
+ $dbh->commit;
+
+ # create a query for rows that need converting
+ my $rows_to_convert_query = $dbh->prepare( #
+ "SELECT id, parameter, parameter2 FROM alert"
+ . " WHERE alert_type IN ('local_problems','local_problems_state')"
+ . " AND is_migrated = false"
+ . " LIMIT $UPDATE_BATCH_SIZE"
+ );
+
+ # update query
+ my $update_lat_lon_query = $dbh->prepare( #
+ "UPDATE alert SET parameter2 = ?, parameter = ?, is_migrated = true"
+ . " WHERE id = ?"
+ );
+
+ # loop through the entries in batches updating rows that need it. Do this in
+ # Perl rather than SQL for conveniance.
+ while (1) {
+ $rows_to_convert_query->execute;
+ last unless $rows_to_convert_query->rows;
+ while ( my $r = $rows_to_convert_query->fetchrow_hashref ) {
+ my ( $latitude, $longitude ) =
+ Utils::convert_en_to_latlon( $r->{parameter}, $r->{parameter2} );
+ print "update alert $r->{id}: ( $latitude, $longitude )\n";
+ $update_lat_lon_query->execute( $latitude, $longitude, $r->{id} );
+ }
+ $dbh->commit; # every batch of updates
+ }
+
+ print "drop 'is_migrated' column\n";
+ $dbh->do("ALTER TABLE alert DROP COLUMN is_migrated");
+ $dbh->commit;
+}
+
diff --git a/db/schema.sql b/db/schema.sql
index 53d188244..fbff047fb 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -121,8 +121,8 @@ create table problem (
-- Problem details
postcode text not null,
- easting double precision not null,
- northing double precision not null,
+ latitude double precision not null,
+ longitude double precision not null,
council text, -- the council(s) we'll report this problem to
areas text not null, -- the voting areas this location is in
category text not null default 'Other',
@@ -155,7 +155,7 @@ create table problem (
whensent timestamp,
send_questionnaire boolean not null default 't'
);
-create index problem_state_easting_northing_idx on problem(state, easting, northing);
+create index problem_state_latitude_longitude_idx on problem(state, latitude, longitude);
create table questionnaire (
id serial not null primary key,
@@ -196,25 +196,41 @@ create type problem_nearby_match as (
distance double precision -- km
);
--- problem_find_nearby EASTING NORTHING DISTANCE
--- Find problems within DISTANCE (km) of (EASTING, NORTHING).
+-- problem_find_nearby LATITUDE LONGITUDE DISTANCE
+-- Find locations within DISTANCE (km) of (LATITUDE, LONGITUDE).
create function problem_find_nearby(double precision, double precision, double precision)
returns setof problem_nearby_match as
-- Write as SQL function so that we don't have to construct a temporary
-- table or results set in memory. That means we can't check the values of
-- the parameters, sadly.
+ -- Through sheer laziness, just use great-circle distance; that'll be off
+ -- by ~0.1%:
+ -- http://www.ga.gov.au/nmd/geodesy/datums/distance.jsp
+ -- We index locations on lat/lon so that we can select the locations which lie
+ -- within a wedge of side about 2 * DISTANCE. That cuts down substantially
+ -- on the amount of work we have to do.
'
-- trunc due to inaccuracies in floating point arithmetic
select problem.id,
- sqrt(($1 - easting) ^ 2
- + ($2 - northing) ^ 2)
- as distance
+ R_e() * acos(trunc(
+ (sin(radians($1)) * sin(radians(latitude))
+ + cos(radians($1)) * cos(radians(latitude))
+ * cos(radians($2 - longitude)))::numeric, 14)
+ ) as distance
from problem
where
+ longitude is not null and latitude is not null
+ and radians(latitude) > radians($1) - ($3 / R_e())
+ and radians(latitude) < radians($1) + ($3 / R_e())
+ and (abs(radians($1)) + ($3 / R_e()) > pi() / 2 -- case where search pt is near pole
+ or angle_between(radians(longitude), radians($2))
+ < $3 / (R_e() * cos(radians($1 + $3 / R_e()))))
-- ugly -- unable to use attribute name "distance" here, sadly
- sqrt(($1 - easting) ^ 2
- + ($2 - northing) ^ 2)
- < $3 * 1000
+ and R_e() * acos(trunc(
+ (sin(radians($1)) * sin(radians(latitude))
+ + cos(radians($1)) * cos(radians(latitude))
+ * cos(radians($2 - longitude)))::numeric, 14)
+ ) < $3
order by distance desc
' language sql; -- should be "stable" rather than volatile per default?
@@ -277,7 +293,7 @@ create table alert_type (
create table alert (
id serial not null primary key,
alert_type text not null references alert_type(ref),
- parameter text, -- e.g. Problem ID for new updates
+ parameter text, -- e.g. Problem ID for new updates, Longitude for local problem alerts
parameter2 text, -- e.g. Latitude for local problem alerts
email text not null,
confirmed integer not null default 0,
diff --git a/notes/INSTALL b/notes/INSTALL
index 5254a6982..fcb1f5579 100644
--- a/notes/INSTALL
+++ b/notes/INSTALL
@@ -82,14 +82,14 @@ mail youremail@example.com # send a test message
cp conf/general-example conf/general
nano conf/general
-# You only need to alter:
+# You may need to alter:
# * the database connection details
# * the OPTION_BASE_URL to be where your test site will run - eg 'http://localhost'
# * the OPTION_EVEL_URL to '' - this will cause some emails not to be sent but warned to STDERR instead - proper email handling is being worked on
# * the OPTION_SMTP_SMARTHOST to '' if routing mail via ssmtp as described above - otherwise your SMTP server
# * set OPTION_EMAIL_VHOST to the same as OPTION_BASE_URL minus the 'http://' - eg 'localhost'
# * set OPTION_UPLOAD_CACHE and OPTION_GEO_CACHE to your preferred values
-
+# * set OPTION_COUNTRY to your ISO3166 alpha2 code ('GB' for the United Kingdom: http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
# SETTING UP THE DATABASES
diff --git a/notes/osgb36_to_wgs84_notes.txt b/notes/osgb36_to_wgs84_notes.txt
new file mode 100644
index 000000000..3deca19de
--- /dev/null
+++ b/notes/osgb36_to_wgs84_notes.txt
@@ -0,0 +1,77 @@
+These notes are for the FMS migration from OSGB36 to WGS84 coordinates.
+
+Reference:
+
+ easting === longitude
+ northing === latitude
+
+ http://en.wikipedia.org/wiki/Ordnance_Survey_National_Grid
+ http://en.wikipedia.org/wiki/World_Geodetic_System
+
+DB:
+
+ * dump schema from live db and confirm it matches schema.sql (you never know...)
+ DONE: use PledgeBank's 'nearby' logic in problem_find_nearby() SQL function
+ DONE: in 'alerts' table convert parameter and parameter2 if alert is 'local_problems' or 'local_problems_state'
+
+CODE:
+
+ DONE: update all calls to problem_find_nearby swapping parameter order.
+ * find all occurences of 'easting', 'northing', $e, $n etc and migrate
+
+ * email me link broken on http://herring/?lat=51.4545595517093;lon=-0.23100115932431
+
+ * Cobrand.pm
+ * Cobrands/Barnet/Util.pm
+ * Cobrands/Emptyhomes/Util.pm
+ * CronFns.pm
+ * CrossSell.pm
+ * EastHantsWSDL.pm
+ * FixMyStreet/Alert.pm
+ * FixMyStreet/Geocode.pm
+ * FixMyStreet/Map.pm
+ * FixMyStreet/Map/Bing.pm
+ * FixMyStreet/Map/BingOL.pm
+ * FixMyStreet/Map/Google.pm
+ * FixMyStreet/Map/OSM.pm
+ * FixMyStreet/Map/OSM/CycleMap.pm
+ * FixMyStreet/Map/OSM/StreetView.pm
+ * FixMyStreet/Map/Tilma/OL/1_10k.pm
+ * FixMyStreet/Map/Tilma/OL/StreetView.pm
+ * FixMyStreet/Map/Tilma/Original.pm
+ * FixMyStreet/Map/Tilma/Original/1_10k.pm
+ * FixMyStreet/Map/Tilma/Original/StreetView.pm
+ * Memcached.pm
+ * Page.pm
+ * PoChange.pm
+ * Problems.pm
+ * Standard.pm
+ * Utils.pm
+
+ * web/about.cgi
+ * web/ajax.cgi
+ * web/alert.cgi
+ * web/confirm.cgi
+ * web/contact.cgi
+ * web/faq.cgi
+ * web/flickr.cgi
+ * web/fun.cgi
+ * web/import.cgi
+ * web/index.cgi
+ * web/json.cgi
+ * web/photo.cgi
+ * web/questionnaire.cgi
+ * web/reports.cgi
+ * web/rss.cgi
+ * web/test.cgi
+ * web/tms-signup.cgi
+ * web/upload.cgi
+
+ DONE: all scripts checked
+
+UI maps:
+
+ * Tilma js code will still need eastings and northings
+ * All other maps should be able to switch to latlon
+
+
diff --git a/perllib/Carp/Always.pm b/perllib/Carp/Always.pm
new file mode 100644
index 000000000..68bcaee52
--- /dev/null
+++ b/perllib/Carp/Always.pm
@@ -0,0 +1,162 @@
+
+package Carp::Always;
+
+use 5.006;
+use strict;
+use warnings;
+
+our $VERSION = '0.09';
+
+use Carp qw(verbose); # makes carp() cluck and croak() confess
+
+sub _warn {
+ if ($_[-1] =~ /\n$/s) {
+ my $arg = pop @_;
+ $arg =~ s/ at .*? line .*?\n$//s;
+ push @_, $arg;
+ }
+ warn &Carp::longmess;
+}
+
+sub _die {
+ if ($_[-1] =~ /\n$/s) {
+ my $arg = pop @_;
+ $arg =~ s/ at .*? line .*?\n$//s;
+ push @_, $arg;
+ }
+ die &Carp::longmess;
+}
+
+my %OLD_SIG;
+
+BEGIN {
+ @OLD_SIG{qw(__DIE__ __WARN__)} = @SIG{qw(__DIE__ __WARN__)};
+ $SIG{__DIE__} = \&_die;
+ $SIG{__WARN__} = \&_warn;
+}
+
+END {
+ @SIG{qw(__DIE__ __WARN__)} = @OLD_SIG{qw(__DIE__ __WARN__)};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Carp::Always - Warns and dies noisily with stack backtraces
+
+=head1 SYNOPSIS
+
+ use Carp::Always;
+
+makes every C<warn()> and C<die()> complains loudly in the calling package
+and elsewhere. More often used on the command line:
+
+ perl -MCarp::Always script.pl
+
+=head1 DESCRIPTION
+
+This module is meant as a debugging aid. It can be
+used to make a script complain loudly with stack backtraces
+when warn()ing or die()ing.
+
+Here are how stack backtraces produced by this module
+looks:
+
+ # it works for explicit die's and warn's
+ $ perl -MCarp::Always -e 'sub f { die "arghh" }; sub g { f }; g'
+ arghh at -e line 1
+ main::f() called at -e line 1
+ main::g() called at -e line 1
+
+ # it works for interpreter-thrown failures
+ $ perl -MCarp::Always -w -e 'sub f { $a = shift; @a = @$a };' \
+ -e 'sub g { f(undef) }; g'
+ Use of uninitialized value in array dereference at -e line 1
+ main::f('undef') called at -e line 2
+ main::g() called at -e line 2
+
+In the implementation, the C<Carp> module does
+the heavy work, through C<longmess()>. The
+actual implementation sets the signal hooks
+C<$SIG{__WARN__}> and C<$SIG{__DIE__}> to
+emit the stack backtraces.
+
+Oh, by the way, C<carp> and C<croak> when requiring/using
+the C<Carp> module are also made verbose, behaving
+like C<cloak> and C<confess>, respectively.
+
+=head2 EXPORT
+
+Nothing at all is exported.
+
+=head1 ACKNOWLEDGMENTS
+
+This module was born as a reaction to a release
+of L<Acme::JavaTrace> by Sébastien Aperghis-Tramoni.
+Sébastien also has a newer module called
+L<Devel::SimpleTrace> with the same code and fewer flame
+comments on docs. The pruning of the uselessly long
+docs of this module were prodded by Michael Schwern.
+
+Schwern and others told me "the module name stinked" -
+it was called C<Carp::Indeed>. After thinking long
+and not getting nowhere, I went with nuffin's suggestion
+and now it is called C<Carp::Always>.
+C<Carp::Indeed> which is now deprecate
+lives in its own distribution (which won't go anywhere
+but will stay there as a redirection to this module).
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<Carp>
+
+=item *
+
+L<Acme::JavaTrace> and L<Devel::SimpleTrace>
+
+=back
+
+Please report bugs via CPAN RT
+http://rt.cpan.org/NoAuth/Bugs.html?Dist=Carp-Always.
+
+=head1 BUGS
+
+Every (un)deserving module has its own pet bugs.
+
+=over 4
+
+=item *
+
+This module does not play well with other modules which fusses
+around with C<warn>, C<die>, C<$SIG{'__WARN__'}>,
+C<$SIG{'__DIE__'}>.
+
+=item *
+
+Test scripts are good. I should write more of these.
+
+=item *
+
+I don't know if this module name is still a bug as it was
+at the time of C<Carp::Indeed>.
+
+=back
+
+=head1 AUTHOR
+
+Adriano Ferreira, E<lt>ferreira@cpan.orgE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2005-2007 by Adriano R. Ferreira
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
diff --git a/perllib/Cobrands/Barnet/Util.pm b/perllib/Cobrands/Barnet/Util.pm
index 32973c10c..ad6fa3919 100644
--- a/perllib/Cobrands/Barnet/Util.pm
+++ b/perllib/Cobrands/Barnet/Util.pm
@@ -104,9 +104,9 @@ sub disambiguate_location {
}
sub recent_photos {
- my ($self, $num, $e, $n, $dist) = @_;
+ my ($self, $num, $lat, $lon, $dist) = @_;
$num = 2 if $num == 3;
- return Problems::recent_photos($num, $e, $n, $dist);
+ return Problems::recent_photos($num, $lat, $lon, $dist);
}
1;
diff --git a/perllib/FixMyStreet/Alert.pm b/perllib/FixMyStreet/Alert.pm
index b9c124741..90a5b1aaa 100644
--- a/perllib/FixMyStreet/Alert.pm
+++ b/perllib/FixMyStreet/Alert.pm
@@ -184,12 +184,11 @@ sub email_alerts ($) {
$query->execute();
while (my $alert = $query->fetchrow_hashref) {
next unless (Cobrand::email_host($alert->{cobrand}));
- my $e = $alert->{parameter};
- my $n = $alert->{parameter2};
+ my $longitude = $alert->{parameter};
+ my $latitude = $alert->{parameter2};
$url = Cobrand::base_url_for_emails($alert->{cobrand}, $alert->{cobrand_data});
my ($site_restriction, $site_id) = Cobrand::site_restriction($alert->{cobrand}, $alert->{cobrand_data});
- my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($e, $n, 'G');
- my $d = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
+ my $d = mySociety::Gaze::get_radius_containing_population($latitude, $longitude, 200000);
$d = int($d*10+0.5)/10;
my $testing_email_clause = "and problem.email <> '$testing_email'" if $testing_email;
my %data = ( template => $template, data => '', alert_id => $alert->{id}, alert_email => $alert->{email}, lang => $alert->{lang}, cobrand => $alert->{cobrand}, cobrand_data => $alert->{cobrand_data} );
@@ -202,7 +201,7 @@ sub email_alerts ($) {
$site_restriction
order by confirmed desc";
$q = dbh()->prepare($q);
- $q->execute($e, $n, $d, $alert->{whensubscribed}, $alert->{id}, $alert->{email});
+ $q->execute($latitude, $longitude, $d, $alert->{whensubscribed}, $alert->{id}, $alert->{email});
while (my $row = $q->fetchrow_hashref) {
dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $alert->{id}, $row->{id});
$data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
@@ -310,9 +309,8 @@ sub generate_rss ($$$;$$$$) {
}
$item{description} .= ent("\n<br><a href='$cobrand_url'>Report on FixMyStreet</a>");
- if ($row->{easting} && $row->{northing}) {
- my ($lat,$lon) = mySociety::GeoUtil::national_grid_to_wgs84($row->{easting}, $row->{northing}, 'G');
- $item{georss} = { point => "$lat $lon" };
+ if ($row->{latitude} || $row->{longitude}) {
+ $item{georss} = { point => "$row->{latitude} $row->{longitude}" };
}
$rss->add_item( %item );
}
diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm
index 4854411cf..0379169b8 100644
--- a/perllib/FixMyStreet/Geocode.pm
+++ b/perllib/FixMyStreet/Geocode.pm
@@ -37,40 +37,32 @@ BEGIN {
# of the site to diambiguate locations.
sub lookup {
my ($s, $q) = @_;
- my ($easting, $northing, $error);
+ my ($latitude, $longitude, $error);
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 = Page::mapit_check_error($location)) {
- $easting = $location->{easting};
- $northing = $location->{northing};
+ $latitude = $location->{wgs84_lat};
+ $longitude = $location->{wgs84_lon};
}
} else {
- ($easting, $northing, $error) = FixMyStreet::Geocode::string($s, $q);
+ ($latitude, $longitude, $error) = FixMyStreet::Geocode::string($s, $q);
}
- return ($easting, $northing, $error);
+ return ($latitude, $longitude, $error);
}
sub geocoded_string_coordinates {
my ($js, $q) = @_;
- my ($easting, $northing, $error);
+ my ($latitude, $longitude, $error);
my ($accuracy) = $js =~ /"Accuracy" *: *(\d)/;
if ($accuracy < 4) {
$error = _('Sorry, that location appears to be too general; please be more specific.');
- } else {
-
- $js =~ /"coordinates" *: *\[ *(.*?), *(.*?),/;
- my $lon = $1; my $lat = $2;
- try {
- ($easting, $northing) = mySociety::GeoUtil::wgs84_to_national_grid($lat, $lon, 'G');
- } catch Error::Simple with {
- $error = shift;
- $error = _('That location does not appear to be in Britain; please try again.')
- if $error =~ /out of the area covered/;
- }
- }
- return ($easting, $northing, $error);
+ } elsif ( $js =~ /"coordinates" *: *\[ *(.*?), *(.*?),/ ) {
+ $longitude = $1;
+ $latitude = $2;
+ }
+ return ($latitude, $longitude, $error);
}
# string STRING QUERY
@@ -90,7 +82,7 @@ sub string {
my $url = 'http://maps.google.com/maps/geo?' . $s;
my $cache_dir = mySociety::Config::get('GEO_CACHE');
my $cache_file = $cache_dir . md5_hex($url);
- my ($js, $error, $easting, $northing);
+ my ($js, $error, $latitude, $longitude);
if (-s $cache_file) {
$js = File::Slurp::read_file($cache_file);
} else {
@@ -123,9 +115,9 @@ sub string {
# Northern Ireland, hopefully
$error = _("We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.");
} else {
- ($easting, $northing, $error) = geocoded_string_coordinates($js, $q);
+ ($latitude, $longitude, $error) = geocoded_string_coordinates($js, $q);
}
- return ($easting, $northing, $error);
+ return ($latitude, $longitude, $error);
}
# list_choices
diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm
index 12ecf78fe..0902914dd 100644
--- a/perllib/FixMyStreet/Map.pm
+++ b/perllib/FixMyStreet/Map.pm
@@ -14,29 +14,34 @@ use Problems;
use Cobrand;
use mySociety::Config;
use mySociety::Gaze;
-use mySociety::GeoUtil;
+use mySociety::GeoUtil qw(national_grid_to_wgs84);
use mySociety::Locale;
use mySociety::Web qw(ent NewURL);
+use Utils;
# Run on module boot up
load();
# This is yucky, but no-one's taught me a better way
sub load {
- my $type = mySociety::Config::get('MAP_TYPE');
+ my $type = mySociety::Config::get('MAP_TYPE');
my $class = "FixMyStreet::Map::$type";
eval "use $class";
+
+ # If we have an error die as it is a compile error rather than runtime error
+ die $@ if $@;
}
sub header {
- my ($q, $type) = @_;
+ my ( $q, $type ) = @_;
return '' unless $type;
my $cobrand = Page::get_cobrand($q);
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'mapForm', $q);
- my $form_action = Cobrand::url($cobrand, '', $q);
+ my $cobrand_form_elements =
+ Cobrand::form_elements( $cobrand, 'mapForm', $q );
+ my $form_action = Cobrand::url( $cobrand, '', $q );
my $encoding = '';
- $encoding = ' enctype="multipart/form-data"' if $type==2;
+ $encoding = ' enctype="multipart/form-data"' if $type == 2;
my $pc = $q->param('pc') || '';
my $pc_enc = ent($pc);
return <<EOF;
@@ -47,49 +52,67 @@ $cobrand_form_elements
EOF
}
+=head2 map_features_easting_northing
+
+Wrapper around map_features which does the easting, northing to lat, lon
+conversion.
+
+=cut
+
+sub map_features_easting_northing {
+ my ( $q, $easting, $northing, $interval ) = @_;
+ my ( $lat, $lon ) = Utils::convert_en_to_latlon( $easting, $northing );
+ return map_features( $q, $lat, $lon, $interval );
+}
+
sub map_features {
- my ($q, $easting, $northing, $interval) = @_;
-
- my $min_e = $easting - 500;
- my $min_n = $northing - 500;
- my $mid_e = $easting;
- my $mid_n = $northing;
- my $max_e = $easting + 500;
- my $max_n = $northing + 500;
-
- # list of problems aoround map can be limited, but should show all pins
- my ($around_map, $around_map_list);
- if (my $around_limit = Cobrand::on_map_list_limit(Page::get_cobrand($q))) {
- $around_map_list = Problems::around_map($min_e, $max_e, $min_n, $max_n, $interval, $around_limit);
- $around_map = Problems::around_map($min_e, $max_e, $min_n, $max_n, $interval, undef);
- } else {
- $around_map = $around_map_list = Problems::around_map($min_e, $max_e, $min_n, $max_n, $interval, undef);
- }
+ my ( $q, $lat, $lon, $interval ) = @_;
+
+ # TODO - be smarter about calculating the surrounding square
+ # use deltas that are roughly 500m in the UK - so we get a 1 sq km search box
+ my $lat_delta = 0.00438;
+ my $lon_delta = 0.00736;
+
+ my $min_lat = $lat - $lat_delta;
+ my $max_lat = $lat + $lat_delta;
+
+ my $min_lon = $lon - $lon_delta;
+ my $max_lon = $lon + $lon_delta;
+
+ # list of problems around map can be limited, but should show all pins
+ my $around_limit #
+ = Cobrand::on_map_list_limit( Page::get_cobrand($q) ) || undef;
+
+ my @around_args = ( $min_lat, $max_lat, $min_lon, $max_lon, $interval );
+ my $around_map_list = Problems::around_map( @around_args, $around_limit );
+ my $around_map = Problems::around_map( @around_args, undef );
my $dist;
mySociety::Locale::in_gb_locale {
- my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($mid_e, $mid_n, 'G');
- $dist = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
+ $dist =
+ mySociety::Gaze::get_radius_containing_population( $lat, $lon,
+ 200000 );
};
- $dist = int($dist*10+0.5)/10;
+ $dist = int( $dist * 10 + 0.5 ) / 10;
- my $limit = 20;
- my @ids = map { $_->{id} } @$around_map_list;
- my $nearby = Problems::nearby($dist, join(',', @ids), $limit, $mid_e, $mid_n, $interval);
+ my $limit = 20;
+ my @ids = map { $_->{id} } @$around_map_list;
+ my $nearby = Problems::nearby( $dist, join( ',', @ids ),
+ $limit, $lat, $lon, $interval );
- return ($around_map, $around_map_list, $nearby, $dist);
+ return ( $around_map, $around_map_list, $nearby, $dist );
}
sub compass ($$$) {
- my ($q, $x, $y) = @_;
+ my ( $q, $x, $y ) = @_;
my @compass;
- for (my $i=$x-1; $i<=$x+1; $i++) {
- for (my $j=$y-1; $j<=$y+1; $j++) {
- $compass[$i][$j] = NewURL($q, x=>$i, y=>$j);
+ for ( my $i = $x - 1 ; $i <= $x + 1 ; $i++ ) {
+ for ( my $j = $y - 1 ; $j <= $y + 1 ; $j++ ) {
+ $compass[$i][$j] = NewURL( $q, x => $i, y => $j );
}
}
my $recentre = NewURL($q);
- my $host = Page::base_url_with_lang($q, undef);
+ my $host = Page::base_url_with_lang( $q, undef );
return <<EOF;
<table cellpadding="0" cellspacing="0" border="0" id="compass">
<tr valign="bottom">
diff --git a/perllib/FixMyStreet/Map/Tilma/Original.pm b/perllib/FixMyStreet/Map/Tilma/Original.pm
index 5772f6ccd..81b123b30 100644
--- a/perllib/FixMyStreet/Map/Tilma/Original.pm
+++ b/perllib/FixMyStreet/Map/Tilma/Original.pm
@@ -13,6 +13,13 @@ use LWP::Simple;
use Cobrand;
use mySociety::Web qw(ent NewURL);
+use mySociety::GeoUtil;
+use Utils;
+
+sub _ll_to_en {
+ my ($lat, $lon) = @_;
+ return mySociety::GeoUtil::wgs84_to_national_grid( $lat, $lon, 'G' );
+}
sub header_js {
return '
@@ -22,7 +29,7 @@ sub header_js {
# display_map Q PARAMS
# PARAMS include:
-# EASTING, NORTHING for the centre point of the map
+# latitude, longitude for the centre point of the map
# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
# 0 if not clickable
# PINS is array of pins to show, location and colour
@@ -36,6 +43,18 @@ sub _display_map {
$mid_point = 189;
}
+ # convert map center point to easting, northing
+ ( $params{easting}, $params{northing} ) =
+ _ll_to_en( $params{latitude}, $params{longitude} );
+
+ # FIXME - convert all pins to lat, lng
+ # all the pins are currently [lat, lng, colour] - convert them
+ foreach my $pin ( @{ $params{pins} ||= [] } ) {
+ my ( $lat, $lon ) = ( $pin->[0], $pin->[1] );
+ my ( $e, $n ) = _ll_to_en( $lat, $lon );
+ ( $pin->[0], $pin->[1] ) = ( $e, $n );
+ }
+
# X/Y tile co-ords may be overridden in the query string
my @vars = qw(x y);
my %input = map { $_ => $q->param($_) || '' } @vars;
@@ -154,10 +173,14 @@ sub map_pins {
my $e = FixMyStreet::Map::tile_to_os($x);
my $n = FixMyStreet::Map::tile_to_os($y);
- my ($around_map, $around_map_list, $nearby, $dist) = FixMyStreet::Map::map_features($q, $e, $n, $interval);
+
+ my ( $around_map, $around_map_list, $nearby, $dist ) =
+ FixMyStreet::Map::map_features_easting_northing( $q, $e, $n, $interval );
my $pins = '';
foreach (@$around_map) {
+ ( $_->{easting}, $_->{northing} ) =
+ _ll_to_en( $_->{latitude}, $_->{longitude} );
my $px = FixMyStreet::Map::os_to_px($_->{easting}, $sx);
my $py = FixMyStreet::Map::os_to_px($_->{northing}, $sy, 1);
my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
@@ -165,6 +188,8 @@ sub map_pins {
}
foreach (@$nearby) {
+ ( $_->{easting}, $_->{northing} ) =
+ _ll_to_en( $_->{latitude}, $_->{longitude} );
my $px = FixMyStreet::Map::os_to_px($_->{easting}, $sx);
my $py = FixMyStreet::Map::os_to_px($_->{northing}, $sy, 1);
my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
@@ -196,10 +221,30 @@ sub tile_to_px {
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 ( $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;
@@ -219,6 +264,14 @@ sub click_to_os {
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 ( $easting, $northing ) = FixMyStreet::Map::click_to_os(@_);
+ my ( $lat, $lon ) = 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.
diff --git a/perllib/Page.pm b/perllib/Page.pm
index b3f320f5d..fc7127a78 100644
--- a/perllib/Page.pm
+++ b/perllib/Page.pm
@@ -80,6 +80,9 @@ sub report_error {
my $trylater = sprintf(_('Please try again later, or <a href="mailto:%s">email us</a> to let us know.'), $contact_email);
my $somethingwrong = _("Sorry! Something's gone wrong.");
my $errortext = _("The text of the error was:");
+
+ my $msg_br = join '<br><br>', split m{\n}, $msg;
+
print "Status: 500\nContent-Type: text/html; charset=iso-8859-1\n\n",
qq(<html><head><title>$somethingwrong</title></head></html>),
q(<body>),
@@ -87,7 +90,7 @@ sub report_error {
qq(<p>$trylater</p>),
q(<hr>),
qq(<p>$errortext</p>),
- qq(<blockquote class="errortext">$msg</blockquote>),
+ qq(<blockquote class="errortext">$msg_br</blockquote>),
q(</body></html>);
}
diff --git a/perllib/Problems.pm b/perllib/Problems.pm
index 1556b7724..8c6eeccad 100644
--- a/perllib/Problems.pm
+++ b/perllib/Problems.pm
@@ -87,10 +87,10 @@ sub recent_new {
# Front page recent lists
sub recent_photos {
- my ($num, $e, $n, $dist) = @_;
+ my ($num, $lat, $lon, $dist) = @_;
my $probs;
- if ($e) {
- my $key = "recent_photos:$site_key:$num:$e:$n:$dist";
+ if (defined $lat) {
+ my $key = "recent_photos:$site_key:$num:$lat:$lon:$dist";
$probs = Memcached::get($key);
unless ($probs) {
$probs = select_all("select id, title
@@ -98,7 +98,7 @@ sub recent_photos {
where nearby.problem_id = problem.id
and state in ('confirmed', 'fixed') and photo is not null
$site_restriction
- order by confirmed desc limit $num", $e, $n, $dist);
+ order by confirmed desc limit $num", $lat, $lon, $dist);
Memcached::set($key, $probs, 3600);
}
} else {
@@ -170,46 +170,46 @@ sub front_stats {
# Problems around a location
sub around_map {
- my ($min_e, $max_e, $min_n, $max_n, $interval, $limit) = @_;
+ my ($min_lat, $max_lat, $min_lon, $max_lon, $interval, $limit) = @_;
my $limit_clause = '';
if ($limit) {
$limit_clause = " limit $limit";
}
mySociety::Locale::in_gb_locale { select_all(
- "select id,title,easting,northing,state,
+ "select id,title,latitude,longitude,state,
extract(epoch from confirmed) as time
from problem
where state in ('confirmed', 'fixed')
- and easting>=? and easting<? and northing>=? and northing<? " .
+ and latitude>=? and latitude<? and longitude>=? and longitude<? " .
($interval ? " and ms_current_timestamp()-lastupdate < '$interval'::interval" : '') .
" $site_restriction
order by created desc
- $limit_clause", $min_e, $max_e, $min_n, $max_n);
+ $limit_clause", $min_lat, $max_lat, $min_lon, $max_lon);
};
}
sub nearby {
- my ($dist, $ids, $limit, $mid_e, $mid_n, $interval) = @_;
+ my ($dist, $ids, $limit, $mid_lat, $mid_lon, $interval) = @_;
mySociety::Locale::in_gb_locale { select_all(
- "select id, title, easting, northing, distance, state,
+ "select id, title, latitude, longitude, distance, state,
extract(epoch from confirmed) as time
from problem_find_nearby(?, ?, $dist) as nearby, problem
where nearby.problem_id = problem.id " .
($interval ? " and ms_current_timestamp()-lastupdate < '$interval'::interval" : '') .
" and state in ('confirmed', 'fixed')" . ($ids ? ' and id not in (' . $ids . ')' : '') . "
$site_restriction
- order by distance, created desc limit $limit", $mid_e, $mid_n);
+ order by distance, created desc limit $limit", $mid_lat, $mid_lon);
}
}
sub fixed_nearby {
- my ($dist, $mid_e, $mid_n) = @_;
+ my ($dist, $mid_lat, $mid_lon) = @_;
mySociety::Locale::in_gb_locale { select_all(
- "select id, title, easting, northing, distance
+ "select id, title, latitude, longitude, distance
from problem_find_nearby(?, ?, $dist) as nearby, problem
where nearby.problem_id = problem.id and state='fixed'
$site_restriction
- order by lastupdate desc", $mid_e, $mid_n);
+ order by lastupdate desc", $mid_lat, $mid_lon);
}
}
@@ -218,7 +218,7 @@ sub fixed_nearby {
sub fetch_problem {
my $id = shift;
my $p = dbh()->selectrow_hashref(
- "select id, easting, northing, council, category, title, detail, photo,
+ "select id, latitude, longitude, council, category, title, detail, photo,
used_map, name, anonymous, extract(epoch from confirmed) as time,
state, extract(epoch from whensent-confirmed) as whensent,
extract(epoch from ms_current_timestamp()-lastupdate) as duration,
diff --git a/perllib/Utils.pm b/perllib/Utils.pm
index 24f4a6f94..d54e081c8 100644
--- a/perllib/Utils.pm
+++ b/perllib/Utils.pm
@@ -13,18 +13,76 @@ package Utils;
use strict;
use mySociety::DBHandle qw(dbh);
+use mySociety::GeoUtil;
sub workaround_pg_bytea {
- my ($st, $img_idx, @elements) = @_;
+ my ( $st, $img_idx, @elements ) = @_;
my $s = dbh()->prepare($st);
- for (my $i=1; $i<=@elements; $i++) {
- if ($i == $img_idx) {
- $s->bind_param($i, $elements[$i-1], { pg_type => DBD::Pg::PG_BYTEA });
- } else {
- $s->bind_param($i, $elements[$i-1]);
+ for ( my $i = 1 ; $i <= @elements ; $i++ ) {
+ if ( $i == $img_idx ) {
+ $s->bind_param(
+ $i,
+ $elements[ $i - 1 ],
+ { pg_type => DBD::Pg::PG_BYTEA }
+ );
+ }
+ else {
+ $s->bind_param( $i, $elements[ $i - 1 ] );
}
}
$s->execute();
}
+=head2 convert_en_to_latlon
+
+ ( $latitude, $longitude ) = Utils::convert_en_to_latlon( $easting, $northing );
+
+Takes the easting and northing and returns latitude and longitude.
+
+=cut
+
+sub convert_en_to_latlon {
+ my ( $easting, $northing ) = @_;
+
+ my ( $latitude, $longitude ) =
+
+ # map { truncate_coordinate($_) }
+ mySociety::GeoUtil::national_grid_to_wgs84( $easting, $northing, 'G' );
+
+ return ( $latitude, $longitude );
+}
+
+=head2 convert_en_to_latlon_truncated
+
+ ( $lat, $lon ) = Utils::convert_en_to_latlon( $easting, $northing );
+
+Takes the easting and northing and returns latitude and longitude (truncated
+using C<Utils::truncate_coordinate>).
+
+=cut
+
+sub convert_en_to_latlon_truncated {
+ my ( $easting, $northing ) = @_;
+
+ return
+ map { truncate_coordinate($_) }
+ convert_en_to_latlon( $easting, $northing );
+}
+
+=head2 truncate_coordinate
+
+ $short = Utils::truncate_coordinate( $long );
+
+Given a long coordinate returns a shorter one - rounded to 6 decimal places -
+which is < 1m at the equator.
+
+=cut
+
+sub truncate_coordinate {
+ my $in = shift;
+ my $out = sprintf( '%0.6f', $in );
+ $out =~ s{\.?0+\z}{} if $out =~ m{\.};
+ return $out;
+}
+
1;
diff --git a/t/Page.t b/t/Page.t
index d4ccc9f3d..f57c56092 100755
--- a/t/Page.t
+++ b/t/Page.t
@@ -38,17 +38,24 @@ sub set_lang($) {
sub test_geocode_string() {
my %params = ();
- my $q = new MockQuery('nosite', \%params);
-
- # geocode a straightforward string, expect success
- my ($easting, $northing, $error) = FixMyStreet::Geocode::string('Buckingham Palace', $q);
- ok($easting == 529068, 'example easting generated') or diag("Got $easting");
- ok($northing == 179684, 'example northing generated') or diag("Got $northing");
- ok(! defined($error), 'should not generate error for simple example') or diag("Got $error");
- # expect a failure message for Northern Ireland
- ($easting, $northing, $error) = FixMyStreet::Geocode::string('Falls Road, Belfast', $q);
- ok($error eq "We do not cover Northern Ireland, I'm afraid, as our licence doesn't include any maps for the region.", 'error message produced for NI location') or diag("Got $error");
+ my $q = new MockQuery( 'nosite', \%params );
+
+ # geocode a straightforward string, expect success
+ my ( $latitude, $longitude, $error ) =
+ FixMyStreet::Geocode::string( 'Buckingham Palace', $q );
+ is( $latitude, 51.5013639, 'example easting generated' );
+ is( $longitude, -0.1418898, 'example northing generated' );
+ is( $error, undef, 'should not generate error for simple example' );
+ # expect a failure message for Northern Ireland
+ ( $latitude, $longitude, $error ) =
+ FixMyStreet::Geocode::string( 'Falls Road, Belfast', $q );
+ is(
+ $error,
+ "We do not cover Northern Ireland, I'm afraid, as our licence doesn't "
+ . "include any maps for the region.",
+ 'error message produced for NI location'
+ );
}
sub test_header() {
diff --git a/t/utils.t b/t/utils.t
new file mode 100644
index 000000000..385c482ed
--- /dev/null
+++ b/t/utils.t
@@ -0,0 +1,41 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More tests => 9;
+
+use FindBin;
+use lib "$FindBin::Bin/../perllib";
+use lib "$FindBin::Bin/../commonlib/perllib";
+
+use Utils;
+
+my @truncate_tests = (
+ [ '1.1234567890123', '1.123457', "truncate down" ],
+ [ '1.123456', '1.123456', "leave untouched" ],
+ [ '1.12', '1.12', "don't extend" ],
+ [ '1.100000001', '1.1', "knock off trailing zeros" ],
+ [ '1.000000001', '1', "knock off trailing zeros" ],
+ [ '0.0', '0', "knock off trailing zeros" ],
+ [ '+123', '123', "drop plus sign" ],
+ [ '-123', '-123', "keep minus sign" ],
+);
+
+foreach my $test (@truncate_tests) {
+ my ( $in, $out, $msg ) = @$test;
+ is Utils::truncate_coordinate($in), $out, $msg;
+}
+
+my @convert_en_to_latlon_tests = (
+
+ # e n lat lon
+ [ 1234, 4567, 49.808509, -7.544784 ],
+);
+
+foreach my $test (@convert_en_to_latlon_tests) {
+ my ( $e, $n, $lat, $lon ) = @$test;
+ is_deeply #
+ [ Utils::convert_en_to_latlon_truncated( $e, $n ) ], #
+ [ $lat, $lon ], #
+ "convert ($e,$n) to ($lat,$lon)";
+}
diff --git a/templates/emails/submit-brent b/templates/emails/submit-brent
index 6cb226209..f3a9e5bb8 100644
--- a/templates/emails/submit-brent
+++ b/templates/emails/submit-brent
@@ -21,9 +21,9 @@ Email: <?=$values['email']?>
Details: <?=$values['detail']?>
-Easting: <?=$values['easting']?>
+<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>
-Northing: <?=$values['northing']?>
+Longitude: <?=$values['longitude']?>
<?=$values['closest_address_machine']?>----------
diff --git a/templates/emails/submit-council b/templates/emails/submit-council
index d3a4a0c64..21c89119d 100644
--- a/templates/emails/submit-council
+++ b/templates/emails/submit-council
@@ -21,9 +21,9 @@ Email: <?=$values['email']?>
Details: <?=$values['detail']?>
-Easting: <?=$values['easting']?>
+<?=$values['easting_northing']?>Latitude: <?=$values['latitude']?>
-Northing: <?=$values['northing']?>
+Longitude: <?=$values['longitude']?>
<?=$values['closest_address']?>----------
diff --git a/web-admin/index.cgi b/web-admin/index.cgi
index 2868cf2d6..1044f07c5 100755
--- a/web-admin/index.cgi
+++ b/web-admin/index.cgi
@@ -574,8 +574,8 @@ sub admin_edit_report {
my $council = $row{council} || '<em>None</em>';
(my $areas = $row{areas}) =~ s/^,(.*),$/$1/;
- my $easting = int($row{easting}+0.5);
- my $northing = int($row{northing}+0.5);
+ my $latitude = $row{latitude};
+ my $longitude = $row{longitude};
my $questionnaire = $row{send_questionnaire} ? 'Yes' : 'No';
my $used_map = $row{used_map} ? 'used map' : "didn't use map";
(my $whensent = $row{whensent} || '&nbsp;') =~ s/\..*//;
@@ -610,7 +610,7 @@ sub admin_edit_report {
<li><a href="$url">View report on site</a>
<li><label for="title">Subject:</label> <input size=60 type="text" id="title" name="title" value="$row_h{title}">
<li><label for="detail">Details:</label><br><textarea name="detail" id="detail" cols=60 rows=10>$row_h{detail}</textarea>
-<li>Co-ordinates: $easting,$northing (originally entered $row_h{postcode}, $used_map)
+<li>Co-ordinates: $latitude,$longitude (originally entered $row_h{postcode}, $used_map)
<li>For council(s): $council (other areas: $areas)
<li>$anon
<li>$state
diff --git a/web/alert.cgi b/web/alert.cgi
index 71249759e..896ca9ccc 100755
--- a/web/alert.cgi
+++ b/web/alert.cgi
@@ -24,6 +24,7 @@ use mySociety::MaPit;
use mySociety::VotingArea;
use mySociety::Web qw(ent);
use Cobrand;
+use Utils;
sub main {
my $q = shift;
@@ -56,7 +57,7 @@ EOF
} elsif ($q->param('type') && $q->param('feed')) {
$title = _('Local RSS feeds and email alerts');
$out = alert_local_form($q);
- } elsif ($q->param('pc') || ($q->param('e') && $q->param('n'))) {
+ } elsif ($q->param('pc') || ($q->param('lat') || $q->param('lon'))) {
$title = _('Local RSS feeds and email alerts');
$out = alert_list($q);
} else {
@@ -72,21 +73,25 @@ Page::do_fastcgi(\&main);
sub alert_list {
my ($q, @errors) = @_;
- my @vars = qw(pc rznvy e n);
+ my @vars = qw(pc rznvy lat lon);
my %input = map { $_ => scalar $q->param($_) } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- my($error, $e, $n);
- if ($input{e} || $input{n}) {
- $e = $input{e};
- $n = $input{n};
+ my($error, $lat, $lon);
+ if ($input{lat} || $input{lon}) {
+ $lat = $input{lat};
+ $lon = $input{lon};
} else {
try {
- ($e, $n, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
+ ($lat, $lon, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
} catch Error::Simple with {
$error = shift;
};
}
+
+ # truncate the lat,lon for nicer urls
+ ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
+
return FixMyStreet::Geocode::list_choices($error, '/alert', $q) if ref($error) eq 'ARRAY';
return alert_front_page($q, $error) if $error;
@@ -102,7 +107,7 @@ sub alert_list {
my @types = (@$mySociety::VotingArea::council_parent_types, @$mySociety::VotingArea::council_child_types);
my %councils = map { $_ => 1 } @$mySociety::VotingArea::council_parent_types;
- my $areas = mySociety::MaPit::call('point', "27700/$e,$n", type => \@types);
+ my $areas = mySociety::MaPit::call('point', "4326/$lon,$lat", type => \@types);
my $cobrand = Page::get_cobrand($q);
my ($success, $error_msg) = Cobrand::council_check($cobrand, { all_councils => $areas }, $q, 'alert');
if (!$success){
@@ -194,18 +199,17 @@ for the county council.'))) . '</div><div id="rss_buttons">';
}
} else {
# Hopefully impossible in the UK!
- throw Error::Simple('An area with three tiers of council? Impossible! '. $e . ' ' . $n . ' ' . join('|',keys %$areas));
+ throw Error::Simple('An area with three tiers of council? Impossible! '. $lat . ' ' . $lon . ' ' . join('|',keys %$areas));
}
- my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($e, $n, 'G');
my $dist = mySociety::Gaze::get_radius_containing_population($lat, $lon, 200000);
$dist = int($dist * 10 + 0.5);
$dist = $dist / 10.0;
my $checked = '';
- $checked = ' checked' if $q->param('feed') && $q->param('feed') eq "local:$e:$n";
+ $checked = ' checked' if $q->param('feed') && $q->param('feed') eq "local:$lat:$lon";
my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'alerts', $q);
- my $pics = Cobrand::recent_photos($cobrand, 5, $e, $n, $dist);
+ my $pics = Cobrand::recent_photos($cobrand, 5, $lat, $lon, $dist);
$pics = '<div id="alert_photos">' . $q->h2(_('Photos of recent nearby reports')) . $pics . '</div>' if $pics;
my $header;
if ($pretty_pc) {
@@ -231,20 +235,20 @@ feed, or enter your email address to subscribe to an email alert.'));
my $rss_label = sprintf(_('Problems within %skm of this location'), $dist);
$out .= <<EOF;
<p id="rss_local">
-<input type="radio" name="feed" id="local:$e:$n" value="local:$e:$n"$checked>
-<label for="local:$e:$n">$rss_label</label>
+<input type="radio" name="feed" id="local:$lat:$lon" value="local:$lat:$lon"$checked>
+<label for="local:$lat:$lon">$rss_label</label>
EOF
- my $rss_feed = Cobrand::url($cobrand, "/rss/n/$e,$n", $q);
- my $default_link = Cobrand::url($cobrand, "/alert?type=local;feed=local:$e:$n", $q);
+ my $rss_feed = Cobrand::url($cobrand, "/rss/l/$lat,$lon", $q);
+ my $default_link = Cobrand::url($cobrand, "/alert?type=local;feed=local:$lat:$lon", $q);
my $rss_details = _('(a default distance which covers roughly 200,000 people)');
$out .= $rss_details;
$out .= " <a href='$rss_feed'><img src='/i/feed.png' width='16' height='16' title='"
. _('RSS feed of nearby problems') . "' alt='" . _('RSS feed') . "' border='0'></a>";
$out .= '</p> <p id="rss_local_alt">' . _('(alternatively the RSS feed can be customised, within');
- my $rss_feed_2k = Cobrand::url($cobrand, "/rss/n/$e,$n/2", $q);
- my $rss_feed_5k = Cobrand::url($cobrand, "/rss/n/$e,$n/5", $q);
- my $rss_feed_10k = Cobrand::url($cobrand, "/rss/n/$e,$n/10", $q);
- my $rss_feed_20k = Cobrand::url($cobrand, "/rss/n/$e,$n/20", $q);
+ my $rss_feed_2k = Cobrand::url($cobrand, "/rss/l/$lat,$lon/2", $q);
+ my $rss_feed_5k = Cobrand::url($cobrand, "/rss/l/$lat,$lon/5", $q);
+ my $rss_feed_10k = Cobrand::url($cobrand, "/rss/l/$lat,$lon/10", $q);
+ my $rss_feed_20k = Cobrand::url($cobrand, "/rss/l/$lat,$lon/20", $q);
$out .= <<EOF;
<a href="$rss_feed_2k">2km</a> / <a href="$rss_feed_5k">5km</a>
/ <a href="$rss_feed_10k">10km</a> / <a href="$rss_feed_20k">20km</a>)
@@ -271,8 +275,8 @@ EOF
rss_feed_5k => $rss_feed_5k,
rss_feed_10k => $rss_feed_10k,
rss_feed_20k => $rss_feed_20k,
- e => $e,
- n => $n,
+ lat => $lat,
+ lon => $lon,
options => $options );
my $cobrand_page = Page::template_include('alert-options', $q, Page::template_root($q), %vars);
$out = $cobrand_page if ($cobrand_page);
@@ -526,8 +530,10 @@ sub alert_do_subscribe {
$alert_id = FixMyStreet::Alert::create($email, 'council_problems', $cobrand, $cobrand_data, $1, $1);
} elsif ($feed =~ /^ward:(\d+):(\d+)/) {
$alert_id = FixMyStreet::Alert::create($email, 'ward_problems', $cobrand, $cobrand_data, $1, $2);
- } elsif ($feed =~ /^local:(\d+):(\d+)/) {
- $alert_id = FixMyStreet::Alert::create($email, 'local_problems', $cobrand, $cobrand_data, $1, $2);
+ } elsif ($feed =~ m{ \A local: ( [\+\-]? \d+ \.? \d* ) : ( [\+\-]? \d+ \.? \d* ) }xms ) {
+ my $lat = $1;
+ my $lon = $2;
+ $alert_id = FixMyStreet::Alert::create($email, 'local_problems', $cobrand, $cobrand_data, $lon, $lat);
}
} else {
throw FixMyStreet::Alert::Error('Invalid type');
diff --git a/web/import.cgi b/web/import.cgi
index 2aede1697..e7746f589 100755
--- a/web/import.cgi
+++ b/web/import.cgi
@@ -13,16 +13,16 @@ use Error qw(:try);
use Standard;
use Utils;
use mySociety::AuthToken;
+use mySociety::Config;
use mySociety::EmailUtil;
use mySociety::EvEl;
+use mySociety::GeoUtil;
sub main {
my $q = shift;
my @vars = qw(service subject detail name email phone easting northing lat lon id phone_id);
my %input = map { $_ => $q->param($_) || '' } @vars;
- $input{easting} ||= 0;
- $input{northing} ||= 0;
my @errors;
unless ($ENV{REQUEST_METHOD} eq 'POST') {
@@ -32,6 +32,18 @@ sub main {
return;
}
+ # If we were given easting, northing convert to lat lon now
+ my $latitude = $input{lat} ||= 0;
+ my $longitude = $input{lon} ||= 0;
+ if (
+ !( $latitude || $longitude ) # have not been given lat or lon
+ && ( $input{easting} && $input{northing} ) # but do have e and n
+ )
+ {
+ ( $latitude, $longitude ) =
+ Utils::convert_en_to_latlon( $input{easting}, $input{northing});
+ }
+
my $fh = $q->upload('photo'); # MUST come before $q->header, don't know why!
print $q->header(-charset => 'utf-8', -content_type => 'text/plain');
@@ -50,14 +62,17 @@ sub main {
push @errors, 'Please enter a valid email';
}
- if ($input{lat}) {
+ if ( $latitude && mySociety::Config::get('COUNTRY') eq 'GB' ) {
try {
- ($input{easting}, $input{northing}) = mySociety::GeoUtil::wgs84_to_national_grid($input{lat}, $input{lon}, 'G');
- } catch Error::Simple with {
+ mySociety::GeoUtil::wgs84_to_national_grid( $latitude, $longitude,
+ 'G' );
+ }
+ catch Error::Simple with {
my $e = shift;
push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
};
}
+
# TODO: Get location from photo if present in EXIF data?
my $photo;
@@ -70,7 +85,7 @@ sub main {
};
}
- unless ($photo || ($input{easting} && $input{northing})) {
+ unless ( $photo || ( $latitude || $longitude ) ) {
push @errors, 'Either a location or a photo must be provided.';
}
@@ -92,11 +107,11 @@ sub main {
# Store what we have so far in the database
my $id = dbh()->selectrow_array("select nextval('problem_id_seq')");
Utils::workaround_pg_bytea("insert into problem
- (id, postcode, easting, northing, title, detail, name, service,
+ (id, postcode, latitude, longitude, title, detail, name, service,
email, phone, photo, state, used_map, anonymous, category, areas)
values
(?, '', ?, ?, ?, ?, ?, ?, ?, ?, ?, 'partial', 't', 'f', '', '')", 10,
- $id, $input{easting}, $input{northing}, $input{subject},
+ $id, $latitude, $longitude, $input{subject},
$input{detail}, $input{name}, $input{service}, $input{email}, $input{phone}, $photo);
# Send checking email
diff --git a/web/index.cgi b/web/index.cgi
index e503f50ba..62a882e6b 100755
--- a/web/index.cgi
+++ b/web/index.cgi
@@ -16,6 +16,8 @@ use RABX;
use CGI::Carp;
use URI::Escape;
+# use Carp::Always;
+
use CrossSell;
use FixMyStreet::Geocode;
use mySociety::AuthToken;
@@ -29,6 +31,13 @@ use mySociety::PostcodeUtil;
use mySociety::Random;
use mySociety::VotingArea;
use mySociety::Web qw(ent NewURL);
+use Utils;
+
+sub debug (@) {
+ return;
+ my ( $format, @args ) = @_;
+ warn sprintf $format, map { defined $_ ? $_ : 'undef' } @args;
+}
BEGIN {
if (!dbh()->selectrow_array('select secret from secret for update of secret')) {
@@ -47,13 +56,13 @@ sub main {
my $id = mySociety::AuthToken::retrieve('partial', $partial);
if ($id) {
my @row = dbh()->selectrow_array(
- "select easting, northing, name, email, title, (photo is not null) as has_photo, phone, detail
+ "select latitude, longitude, name, email, title, (photo is not null) as has_photo, phone, detail
from problem where id=? and state='partial'", {}, $id);
if (@row) {
$q->param('anonymous', 1);
$q->param('submit_map', 1);
- $q->param('easting', $row[0]);
- $q->param('northing', $row[1]);
+ $q->param('latitude', $row[0]);
+ $q->param('longitude', $row[1]);
$q->param('name', $row[2]);
$q->param('email', $row[3]);
$q->param('title', $row[4]);
@@ -82,9 +91,11 @@ sub main {
} elsif ($q->param('id')) {
($out, %params) = display_problem($q, [], {});
$params{title} .= ' - ' . _('Viewing a problem');
- } elsif ($q->param('pc') || ($q->param('x') && $q->param('y')) || ($q->param('e') && $q->param('n'))) {
+ } elsif ($q->param('pc') || ($q->param('x') && $q->param('y')) || ($q->param('lat') || $q->param('lon'))) {
($out, %params) = display_location($q);
$params{title} = _('Viewing a location');
+ } elsif ($q->param('e') && $q->param('n')) {
+ ($out, %params) = redirect_from_osgb_to_wgs84($q);
} else {
($out, %params) = front_page($q);
}
@@ -253,7 +264,7 @@ sub submit_update {
sub submit_problem {
my $q = shift;
- my @vars = qw(council title detail name email phone pc easting northing skipped anonymous category partial upload_fileid lat lon);
+ my @vars = qw(council title detail name email phone pc skipped anonymous category partial upload_fileid latitude longitude);
my %input = map { $_ => scalar $q->param($_) } @vars;
for (qw(title detail)) {
$input{$_} = lc $input{$_} if $input{$_} !~ /[a-z]/;
@@ -265,15 +276,17 @@ sub submit_problem {
my @errors;
my %field_errors;
- if ($input{lat}) {
+
+ # If in UK and we have a lat,lon coocdinate check it is in UK
+ if ( $input{latitude} && mySociety::Config::get('COUNTRY') eq 'GB' ) {
try {
- ($input{easting}, $input{northing}) = mySociety::GeoUtil::wgs84_to_national_grid($input{lat}, $input{lon}, 'G');
+ mySociety::GeoUtil::wgs84_to_national_grid($input{latitude}, $input{longitude}, 'G');
} catch Error::Simple with {
my $e = shift;
push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
};
}
-
+
my $fh = $q->upload('photo');
if ($fh) {
my $err = Page::check_photo($q, $fh);
@@ -305,8 +318,9 @@ sub submit_problem {
return display_form($q, \@errors, \%field_errors) if (@errors || scalar keys %field_errors); # Short circuit
my $areas;
- if ($input{easting} && $input{northing}) {
- $areas = mySociety::MaPit::call('point', "27700/$input{easting},$input{northing}");
+ if (defined $input{latitude} && defined $input{longitude}) {
+ my $mapit_query = "4326/$input{longitude},$input{latitude}";
+ $areas = mySociety::MaPit::call( 'point', $mapit_query );
if ($input{council} =~ /^[\d,]+(\|[\d,]+)?$/) {
my $no_details = $1 || '';
my %va = map { $_ => 1 } @$mySociety::VotingArea::council_parent_types;
@@ -345,10 +359,10 @@ sub submit_problem {
$input{council} = join(',', @valid_councils) . $no_details;
}
$areas = ',' . join(',', sort keys %$areas) . ',';
- } elsif ($input{easting} || $input{northing}) {
+ } elsif (defined $input{latitude} || defined $input{longitude}) {
push(@errors, _('Somehow, you only have one co-ordinate. Please try again.'));
} else {
- push(@errors, _('You haven\'t specified any sort of co-ordinates. Please try again.'));
+ push(@errors, _("You haven't specified any sort of co-ordinates. Please try again."));
}
my $image;
@@ -378,10 +392,10 @@ sub submit_problem {
if (my $token = $input{partial}) {
my $id = mySociety::AuthToken::retrieve('partial', $token);
if ($id) {
- dbh()->do("update problem set postcode=?, easting=?, northing=?, title=?, detail=?,
+ dbh()->do("update problem set postcode=?, latitude=?, longitude=?, title=?, detail=?,
name=?, email=?, phone=?, state='confirmed', council=?, used_map='t',
anonymous=?, category=?, areas=?, cobrand=?, cobrand_data=?, confirmed=ms_current_timestamp(),
- lastupdate=ms_current_timestamp() where id=?", {}, $input{pc}, $input{easting}, $input{northing},
+ lastupdate=ms_current_timestamp() where id=?", {}, $input{pc}, $input{latitude}, $input{longitude},
$input{title}, $input{detail}, $input{name}, $input{email},
$input{phone}, $input{council}, $input{anonymous} ? 'f' : 't',
$input{category}, $areas, $cobrand, $cobrand_data, $id);
@@ -400,11 +414,11 @@ Please <a href="/contact">let us know what went on</a> and we\'ll look into it.'
} else {
$id = dbh()->selectrow_array("select nextval('problem_id_seq');");
Utils::workaround_pg_bytea("insert into problem
- (id, postcode, easting, northing, title, detail, name,
+ (id, postcode, latitude, longitude, title, detail, name,
email, phone, photo, state, council, used_map, anonymous, category, areas, lang, cobrand, cobrand_data)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'unconfirmed', ?, ?, ?, ?, ?, ?, ?, ?)", 10,
- $id, $input{pc}, $input{easting}, $input{northing}, $input{title},
+ $id, $input{pc}, $input{latitude}, $input{longitude}, $input{title},
$input{detail}, $input{name}, $input{email}, $input{phone}, $image,
$input{council}, $used_map, $input{anonymous} ? 'f': 't', $input{category},
$areas, $mySociety::Locale::lang, $cobrand, $cobrand_data);
@@ -430,22 +444,29 @@ sub display_form {
push @errors, _('There were problems with your report. Please see below.') if (scalar keys %field_errors);
my ($pin_x, $pin_y, $pin_tile_x, $pin_tile_y) = (0,0,0,0);
- my @vars = qw(title detail name email phone pc easting northing x y skipped council anonymous partial upload_fileid lat lon);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
+ my @vars = qw(title detail name email phone pc latitude longitude x y skipped council anonymous partial upload_fileid);
- # Convert lat/lon to easting/northing if given
- if ($input{lat}) {
- try {
- ($input{easting}, $input{northing}) = mySociety::GeoUtil::wgs84_to_national_grid($input{lat}, $input{lon}, 'G');
- $input_h{easting} = $input{easting};
- $input_h{northing} = $input{northing};
- } catch Error::Simple with {
- my $e = shift;
- push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
- };
+ my %input = ();
+ my %input_h = ();
+
+ foreach my $key (@vars) {
+ my $val = $q->param($key);
+ $input{$key} = defined($val) ? $val : ''; # '0' is valid for longitude
+ $input_h{$key} = ent( $input{$key} );
}
+ # Convert lat/lon to easting/northing if given
+ # if ($input{lat}) {
+ # try {
+ # ($input{easting}, $input{northing}) = mySociety::GeoUtil::wgs84_to_national_grid($input{lat}, $input{lon}, 'G');
+ # $input_h{easting} = $input{easting};
+ # $input_h{northing} = $input{northing};
+ # } catch Error::Simple with {
+ # my $e = shift;
+ # push @errors, "We had a problem with the supplied co-ordinates - outside the UK?";
+ # };
+ # }
+
# Get tile co-ordinates if map clicked
($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0;
($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0;
@@ -458,28 +479,30 @@ sub display_form {
# We need either a map click, an E/N, to be skipping the map, or be filling in a partial form
return display_location($q, @errors)
unless ($pin_x && $pin_y)
- || ($input{easting} && $input{northing})
+ || ($input{latitude} && $input{longitude})
|| ($input{skipped} && $input{pc})
|| ($input{partial} && $input{pc});
# Work out some co-ordinates from whatever we've got
- my ($easting, $northing);
+ my ($latitude, $longitude);
if ($input{skipped}) {
# Map is being skipped
- if ($input{easting} && $input{northing}) {
- $easting = $input{easting};
- $northing = $input{northing};
+ if ( length $input{latitude} && length $input{longitude} ) {
+ $latitude = $input{latitude};
+ $longitude = $input{longitude};
} else {
- my ($e, $n, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
- $easting = $e; $northing = $n;
+ my ( $lat, $lon, $error ) =
+ FixMyStreet::Geocode::lookup( $input{pc}, $q );
+ $latitude = $lat;
+ $longitude = $lon;
}
} elsif ($pin_x && $pin_y) {
# tilma map was clicked on
- ($easting, $northing) = FixMyStreet::Map::click_to_os($pin_tile_x, $pin_x, $pin_tile_y, $pin_y);
- } elsif ($input{partial} && $input{pc} && !$input{easting} && !$input{northing}) {
+ ($latitude, $longitude) = FixMyStreet::Map::click_to_wgs84($pin_tile_x, $pin_x, $pin_tile_y, $pin_y);
+ } elsif ( $input{partial} && $input{pc} && !length $input{latitude} && !length $input{longitude} ) {
my $error;
try {
- ($easting, $northing, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
+ ($latitude, $longitude, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
} catch Error::Simple with {
$error = shift;
};
@@ -487,8 +510,8 @@ sub display_form {
return front_page($q, $error) if $error;
} else {
# Normal form submission
- $easting = $input_h{easting};
- $northing = $input_h{northing};
+ $latitude = $input_h{latitude};
+ $longitude = $input_h{longitude};
}
# Look up councils and do checks for the point we've got
@@ -496,7 +519,7 @@ sub display_form {
$parent_types = [qw(DIS LBO MTD UTA LGD COI)] # No CTY
if $q->{site} eq 'emptyhomes';
# XXX: I think we want in_gb_locale around the next line, needs testing
- my $all_councils = mySociety::MaPit::call('point', "27700/$easting,$northing", type => $parent_types);
+ my $all_councils = mySociety::MaPit::call('point', "4326/$longitude,$latitude", type => $parent_types);
# Let cobrand do a check
my ($success, $error_msg) = Cobrand::council_check($cobrand, { all_councils => $all_councils }, $q, 'submit_problem');
@@ -504,10 +527,10 @@ sub display_form {
return front_page($q, $error_msg);
}
- # Ipswich & St Edmundsbury are responsible for everything in their areas, no Suffolk
+ # Ipswich & St Edmundsbury are responsible for everything in their areas, not Suffolk
delete $all_councils->{2241} if $all_councils->{2446} || $all_councils->{2443};
- # Norwich is responsible for everything in its areas, no Norfolk
+ # Norwich is responsible for everything in its areas, not Norfolk
delete $all_councils->{2233} if $all_councils->{2391};
return display_location($q, _('That spot does not appear to be covered by a council.
@@ -587,9 +610,9 @@ EOF
$type = 1;
}
$vars{form_start} = FixMyStreet::Map::display_map($q,
- easting => $easting, northing => $northing,
+ latitude => $latitude, longitude => $longitude,
type => $type,
- pins => [ [ $easting, $northing, 'purple' ] ],
+ pins => [ [ $latitude, $longitude, 'purple' ] ],
);
my $partial_id;
if (my $token = $input{partial}) {
@@ -683,8 +706,8 @@ photo of the problem if you have one), etc.'));
}
$vars{text_help} .= '
-<input type="hidden" name="easting" value="' . $easting . '">
-<input type="hidden" name="northing" value="' . $northing . '">';
+<input type="hidden" name="latitude" value="' . $latitude . '">
+<input type="hidden" name="longitude" value="' . $longitude . '">';
if (@errors) {
$vars{errors} = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
@@ -763,40 +786,87 @@ EOF
return (Page::template_include('report-form', $q, Page::template_root($q), %vars), robots => 'noindex,nofollow');
}
+# redirect from osgb
+sub redirect_from_osgb_to_wgs84 {
+ my ($q) = @_;
+
+ my $e = $q->param('e');
+ my $n = $q->param('n');
+
+ my ( $lat, $lon ) = Utils::convert_en_to_latlon_truncated( $e, $n );
+
+ my $lat_lon_url = NewURL(
+ $q,
+ -retain => 1,
+ e => undef,
+ n => undef,
+ lat => $lat,
+ lon => $lon
+ );
+
+ print $q->redirect(
+ -location => $lat_lon_url,
+ -status => 301, # permanent
+ );
+
+ return '';
+}
+
sub display_location {
my ($q, @errors) = @_;
my $cobrand = Page::get_cobrand($q);
- my @vars = qw(pc x y e n all_pins no_pins);
- my %input = map { $_ => $q->param($_) || '' } @vars;
- my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
+ my @vars = qw(pc x y lat lon all_pins no_pins);
- (my $easting) = $input{e} =~ /^(\d+)/; $easting ||= 0;
- (my $northing) = $input{n} =~ /^(\d+)/; $northing ||= 0;
+ my %input = ();
+ my %input_h = ();
+
+ foreach my $key (@vars) {
+ my $val = $q->param($key);
+ $input{$key} = defined($val) ? $val : ''; # '0' is valid for longitude
+ $input_h{$key} = ent( $input{$key} );
+ }
+
+ my $latitude = $input{lat};
+ my $longitude = $input{lon};
# X/Y referring to tiles old-school
(my $x) = $input{x} =~ /^(\d+)/; $x ||= 0;
(my $y) = $input{y} =~ /^(\d+)/; $y ||= 0;
- return front_page($q, @errors) unless $x || $y || $input{pc} || $easting || $northing;
- if ($x && $y) {
+ return front_page( $q, @errors )
+ unless ( $x && $y )
+ || $input{pc}
+ || ( defined $latitude && defined $longitude );
+
+ if ( $x && $y ) {
+
# Convert the tile co-ordinates to real ones.
- $easting = FixMyStreet::Map::tile_to_os($x);
- $northing = FixMyStreet::Map::tile_to_os($y);
- } elsif ($easting && $northing) {
+ ( $latitude, $longitude ) =
+ FixMyStreet::Map::tile_xy_to_wgs84( $x, $y );
+ }
+ elsif ( $latitude && $longitude ) {
+
# Don't need to do anything
- } else {
+ }
+ else {
my $error;
try {
- ($easting, $northing, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
- } catch Error::Simple with {
+ ( $latitude, $longitude, $error ) =
+ FixMyStreet::Geocode::lookup( $input{pc}, $q );
+
+ debug 'Looked up postcode "%s": lat: "%s", lon: "%s", error: "%s"',
+ $input{pc}, $latitude, $longitude, $error;
+ }
+ catch Error::Simple with {
$error = shift;
};
- return FixMyStreet::Geocode::list_choices($error, '/', $q) if (ref($error) eq 'ARRAY');
- return front_page($q, $error) if $error;
+ return FixMyStreet::Geocode::list_choices( $error, '/', $q )
+ if ( ref($error) eq 'ARRAY' );
+ return front_page( $q, $error ) if $error;
}
# Check this location is okay to be displayed for the cobrand
- my ($success, $error_msg) = Cobrand::council_check($cobrand, { e => $easting, n => $northing }, $q, 'display_location');
+ my ($success, $error_msg) = Cobrand::council_check($cobrand, { lat => $latitude, lon => $longitude }, $q, 'display_location');
return front_page($q, $error_msg) unless $success;
# Deal with pin hiding/age
@@ -810,10 +880,10 @@ sub display_location {
$interval = '6 months';
}
- my ($on_map_all, $on_map, $around_map, $dist) = FixMyStreet::Map::map_features($q, $easting, $northing, $interval);
+ my ($on_map_all, $on_map, $around_map, $dist) = FixMyStreet::Map::map_features($q, $latitude, $longitude, $interval);
my @pins;
foreach (@$on_map_all) {
- push @pins, [ $_->{easting}, $_->{northing}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
+ push @pins, [ $_->{latitude}, $_->{longitude}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
}
my $on_list = '';
foreach (@$on_map) {
@@ -839,7 +909,7 @@ sub display_location {
$around_list .= $dist . 'km)</small>';
$around_list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
$around_list .= '</li>';
- push @pins, [ $_->{easting}, $_->{northing}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
+ push @pins, [ $_->{latitude}, $_->{longitude}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
}
$around_list = $q->li(_('No problems found.'))
unless $around_list;
@@ -856,21 +926,27 @@ sub display_location {
my $url_skip = NewURL($q, -retain=>1, pc => undef,
x => undef, 'y' => undef,
- easting => $easting, northing => $northing,
+ latitude => $latitude, longitude => $longitude,
'submit_map'=>1, skipped=>1
);
my $pc_h = ent($q->param('pc') || '');
+
+ # truncate the lat,lon for nicer rss urls
+ my ( $short_lat, $short_lon ) =
+ map { Utils::truncate_coordinate($_) } #
+ ( $latitude, $longitude );
+
my %vars = (
'map' => FixMyStreet::Map::display_map($q,
- easting => $easting, northing => $northing,
+ latitude => $latitude, longitude => $longitude,
type => 1,
pins => \@pins,
post => $map_links
),
map_end => FixMyStreet::Map::display_map_end(1),
url_home => Cobrand::url($cobrand, '/', $q),
- url_rss => Cobrand::url($cobrand, NewURL($q, -retain => 1, -url=> "/rss/n/$easting,$northing", pc => undef, x => undef, 'y' => undef), $q),
- url_email => Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, -url=>'/alert', e=>$easting, 'n'=>$northing, feed=>"local:$easting:$northing"), $q),
+ url_rss => Cobrand::url($cobrand, NewURL($q, -retain => 1, -url=> "/rss/l/$short_lat,$short_lon", pc => undef, x => undef, y => undef, lat => undef, lon => undef ), $q),
+ url_email => Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, lat => $short_lat, lon => $short_lon, -url=>'/alert', feed=>"local:$short_lat:$short_lon"), $q),
url_skip => $url_skip,
email_me => _('Email me new local problems'),
rss_alt => _('RSS feed'),
@@ -890,7 +966,7 @@ sub display_location {
);
my %params = (
- rss => [ _('Recent local problems, FixMyStreet'), "/rss/n/$easting,$northing" ],
+ rss => [ _('Recent local problems, FixMyStreet'), "/rss/l/$short_lat,$short_lon" ],
robots => 'noindex,nofollow',
);
@@ -931,9 +1007,12 @@ sub display_problem {
my $extra_data = Cobrand::extra_data($cobrand, $q);
my $google_link = Cobrand::base_url_for_emails($cobrand, $extra_data)
. '/report/' . $problem->{id};
- my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($problem->{easting}, $problem->{northing}, 'G');
- my $map_links = "<p id='sub_map_links'><a href=\"http://maps.google.co.uk/maps?output=embed&amp;z=16&amp;q="
- . URI::Escape::uri_escape_utf8($problem->{title} . ' - ' . $google_link) . "\@$lat,$lon\">View on Google Maps</a></p>";
+
+ my $map_links =
+ "<p id='sub_map_links'>"
+ . "<a href=\"http://maps.google.co.uk/maps?output=embed&amp;z=16&amp;q="
+ . URI::Escape::uri_escape_utf8( $problem->{title} . ' - ' . $google_link )
+ . "\@$problem->{latitude},$problem->{longitude}\">View on Google Maps</a></p>";
my $banner;
if ($q->{site} ne 'emptyhomes' && $problem->{state} eq 'confirmed' && $problem->{duration} > 8*7*24*60*60) {
@@ -945,7 +1024,7 @@ sub display_problem {
my $contact_url = Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, x => undef, 'y' => undef, -url=>'/contact?id=' . $input{id}), $q);
my $back = Cobrand::url($cobrand, NewURL($q, -url => '/',
- 'e' => int($problem->{easting}), 'n' => int($problem->{northing}),
+ lat => $problem->{latitude}, lon => $problem->{longitude},
-retain => 1, pc => undef, x => undef, 'y' => undef, id => undef
), $q);
my $fixed = ($input{fixed}) ? ' checked' : '';
@@ -953,9 +1032,9 @@ sub display_problem {
my %vars = (
banner => $banner,
map_start => FixMyStreet::Map::display_map($q,
- easting => $problem->{easting}, northing => $problem->{northing},
+ latitude => $problem->{latitude}, longitude => $problem->{longitude},
type => 0,
- pins => [ [ $problem->{easting}, $problem->{northing}, 'blue' ] ],
+ pins => [ [ $problem->{latitude}, $problem->{longitude}, 'blue' ] ],
post => $map_links
),
map_end => FixMyStreet::Map::display_map_end(0),
diff --git a/web/questionnaire.cgi b/web/questionnaire.cgi
index 33e823560..cb5e553d3 100755
--- a/web/questionnaire.cgi
+++ b/web/questionnaire.cgi
@@ -220,9 +220,9 @@ sub display_questionnaire {
my %vars = (
input_h => \%input_h,
map_start => FixMyStreet::Map::display_map($q,
- easting => $problem->{easting}, northing => $problem->{northing},
+ latitude => $problem->{latitude}, longitude => $problem->{longitude},
pins => [
- [ $problem->{easting}, $problem->{northing}, $problem->{state} eq 'fixed'?'green':'red' ],
+ [ $problem->{latitude}, $problem->{longitude}, $problem->{state} eq 'fixed'?'green':'red' ],
],
pre => $problem_text, post => $updates
),
diff --git a/web/rss.cgi b/web/rss.cgi
index 8885af249..9a9b4ce18 100755
--- a/web/rss.cgi
+++ b/web/rss.cgi
@@ -17,6 +17,7 @@ use FixMyStreet::Geocode;
use mySociety::MaPit;
use mySociety::GeoUtil;
use mySociety::Gaze;
+use Utils;
sub main {
my $q = shift;
@@ -83,35 +84,39 @@ sub rss_local_problems {
my $cobrand = Page::get_cobrand($q);
my $base = Cobrand::base_url($cobrand);
- if ($lat) { # In the UK, it'll never be 0 :)
- ($e, $n) = mySociety::GeoUtil::wgs84_to_national_grid($lat, $lon, 'G');
- $e = int($e + 0.5);
- $n = int($n + 0.5);
- print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
- return '';
- } elsif ($x && $y) {
+ if ($x && $y) {
# 5000/31 as initial scale factor for these RSS feeds, now variable so redirect.
$e = int( ($x * 5000/31) + 0.5 );
$n = int( ($y * 5000/31) + 0.5 );
- print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
+ ($lat, $lon) = Utils::convert_en_to_latlon($e, $n);
+ print $q->redirect(-location => "$base/rss/l/$lat,$lon$d_str$state_qs");
return '';
} elsif ($e && $n) {
- ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($e, $n, 'G');
+ ($lat, $lon) = Utils::convert_en_to_latlon($e, $n);
+ print $q->redirect(-location => "$base/rss/l/$lat,$lon$d_str$state_qs");
+ return '';
} elsif ($pc) {
my $error;
try {
- ($e, $n, $error) = FixMyStreet::Geocode::lookup($pc, $q);
+ ($lat, $lon, $error) = FixMyStreet::Geocode::lookup($pc, $q);
} catch Error::Simple with {
$error = shift;
};
unless ($error) {
- print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
+ print $q->redirect(-location => "$base/rss/l/$lat,$lon$d_str$state_qs");
}
return '';
+ } elsif ( $lat || $lon ) {
+ # pass through
} else {
die "Missing E/N, x/y, lat/lon, or postcode parameter in RSS feed";
}
- my $qs = '?e=' . int($e) . ';n=' . int($n);
+
+ # truncate the lat,lon for nicer urls
+ ( $lat, $lon ) = map { Utils::truncate_coordinate($_) } ( $lat, $lon );
+
+ my $qs = "?lat=$lat;lon/=$lon";
+
if ($d) {
$qs .= ";d=$d";
$d = 100 if $d > 100;
@@ -122,9 +127,9 @@ sub rss_local_problems {
my $xsl = Cobrand::feed_xsl($cobrand);
if ($state eq 'all') {
- return FixMyStreet::Alert::generate_rss('local_problems', $xsl, $qs, [$e, $n, $d], undef, $cobrand, $q);
+ return FixMyStreet::Alert::generate_rss('local_problems', $xsl, $qs, [$lat, $lon, $d], undef, $cobrand, $q);
} else {
- return FixMyStreet::Alert::generate_rss('local_problems_state', $xsl, $qs, [$e, $n, $d, $state], undef, $cobrand, $q);
+ return FixMyStreet::Alert::generate_rss('local_problems_state', $xsl, $qs, [$lat, $lon, $d, $state], undef, $cobrand, $q);
}
}