aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/gettext-extract2
-rwxr-xr-xbin/send-alerts4
-rwxr-xr-xbin/test-run8
m---------commonlib0
-rw-r--r--conf/general-example7
-rw-r--r--db/alert_types.sql11
-rw-r--r--perllib/Cobrand.pm2
-rw-r--r--perllib/Cobrands/Barnet/Util.pm7
-rw-r--r--perllib/Cobrands/Emptyhomes/Util.pm9
-rw-r--r--perllib/FixMyStreet/Alert.pm339
-rw-r--r--perllib/FixMyStreet/Geocode.pm156
-rw-r--r--perllib/FixMyStreet/Map.pm114
-rw-r--r--perllib/FixMyStreet/Map/Bing.pm71
-rw-r--r--perllib/FixMyStreet/Map/BingOL.pm71
-rw-r--r--perllib/FixMyStreet/Map/Google.pm71
-rw-r--r--perllib/FixMyStreet/Map/OSM.pm71
-rw-r--r--perllib/FixMyStreet/Map/OSM/CycleMap.pm71
-rw-r--r--perllib/FixMyStreet/Map/OSM/StreetView.pm70
-rw-r--r--perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm83
-rw-r--r--perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm81
-rw-r--r--perllib/FixMyStreet/Map/Tilma/Original.pm257
-rw-r--r--perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm28
-rw-r--r--perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm27
-rw-r--r--perllib/Page.pm434
-rw-r--r--perllib/Standard.pm1
-rwxr-xr-xt/Cobrand.t14
-rw-r--r--t/Cobrands/Mysite/Util.pm4
-rwxr-xr-xt/Page.t13
-rw-r--r--templates/website/cobrands/barnet/barnet-header1
-rw-r--r--templates/website/cobrands/emptyhomes/emptyhomes-header1
-rw-r--r--templates/website/header3
-rwxr-xr-xweb/ajax.cgi2
-rwxr-xr-xweb/alert.cgi48
-rwxr-xr-xweb/confirm.cgi10
-rwxr-xr-xweb/contact.cgi2
-rwxr-xr-xweb/import.cgi1
-rwxr-xr-xweb/index.cgi254
-rw-r--r--web/js.js322
-rw-r--r--web/js/OpenLayers.Projection.OrdnanceSurvey.js489
-rw-r--r--web/js/map-OpenStreetMap.js150
-rw-r--r--web/js/map-bing-ol.js96
-rw-r--r--web/js/map-bing.js21
-rw-r--r--web/js/map-google.js22
-rw-r--r--web/js/map-streetview.js88
-rw-r--r--web/js/map-tilma-ol.js42
-rw-r--r--web/js/map-tilma.js320
-rwxr-xr-xweb/questionnaire.cgi15
-rwxr-xr-xweb/reports.cgi4
-rw-r--r--web/robots.txt5
-rwxr-xr-xweb/rss.cgi38
-rwxr-xr-xweb/tms-signup.cgi1
51 files changed, 2978 insertions, 983 deletions
diff --git a/bin/gettext-extract b/bin/gettext-extract
index fb3406984..b354c11f2 100755
--- a/bin/gettext-extract
+++ b/bin/gettext-extract
@@ -40,7 +40,7 @@ PO=locale/FixMyStreet.po
rm -f $PO
# Extract from Perl
-xgettext --add-comments=TRANS --language=Perl --keyword=_ --keyword=nget:1,2 --from-code=utf-8 -o $PO commonlib/perllib/mySociety/*.pm perllib/*.pm web/*.cgi bin/send-* db/alert_types_eha.pl
+xgettext --add-comments=TRANS --language=Perl --keyword=_ --keyword=nget:1,2 --from-code=utf-8 -o $PO perllib/*.pm perllib/FixMyStreet/*.pm web/*.cgi bin/send-* db/alert_types_eha.pl
# Fix headers
TEMP=`tempfile`
diff --git a/bin/send-alerts b/bin/send-alerts
index 57eeb16ae..c52af4059 100755
--- a/bin/send-alerts
+++ b/bin/send-alerts
@@ -21,7 +21,7 @@ use CronFns;
use mySociety::Config;
use mySociety::DBHandle qw(dbh);
-use mySociety::Alert;
+use FixMyStreet::Alert;
BEGIN {
mySociety::Config::set_file("$FindBin::Bin/../conf/general");
@@ -37,5 +37,5 @@ BEGIN {
my $site = CronFns::site(mySociety::Config::get('BASE_URL'));
CronFns::language($site);
my $testing_email = mySociety::Config::get('TESTING_EMAIL');
-mySociety::Alert::email_alerts($testing_email);
+FixMyStreet::Alert::email_alerts($testing_email);
diff --git a/bin/test-run b/bin/test-run
index 3e5f76afa..15f9c3ed2 100755
--- a/bin/test-run
+++ b/bin/test-run
@@ -377,8 +377,8 @@ sub do_alert {
# sign up for alerts in an area
my $postcode = 'EH1 2NG';
- my $x = 2015; my $e = 325000;
- my $y = 4175; my $n = 673548;
+ my $x = 2015; my $e = 325066;
+ my $y = 4175; my $n = 673533;
my $messages = english_fms_messages();
submit_postcode('', $postcode, 'Problems in this area');
$wth->browser_follow_link(text => 'Email me new local problems');
@@ -417,8 +417,8 @@ sub do_alert {
sub do_eha_alert {
# sign up for alerts in an area
my $postcode = 'EH1 2NG';
- my $x = 2015; my $e = 325000;
- my $y = 4175; my $n = 673548;
+ my $x = 2015; my $e = 325066;
+ my $y = 4175; my $n = 673533;
my @texts = ('Eiddo gwag yn yr ardal hon',
'Adrodd am eiddo gwag',
'Nawr, gwiriwch eich e-bost',
diff --git a/commonlib b/commonlib
-Subproject 13e2591319b761789a41a302bd2de54d454776e
+Subproject e0ffea0186a31514b0a05efd30fc76e83f68d3d
diff --git a/conf/general-example b/conf/general-example
index f8c71c846..0a5c9e44d 100644
--- a/conf/general-example
+++ b/conf/general-example
@@ -49,12 +49,7 @@ define('OPTION_GEO_CACHE', '/cache/');
define('OPTION_GOOGLE_MAPS_API_KEY', '');
define('OPTION_MAPIT_URL', 'http://services.mysociety.org/mapit');
-define('OPTION_TILES_TYPE', '10k-full-london');
-define('OPTION_TILES_TIFF_SIZE_METRES', 5000);
-define('OPTION_TILES_TIFF_SIZE_PIXELS', 7874);
-define('OPTION_TILES_WIDTH', 254);
-define('OPTION_TILES_URL', 'http://tilma.mysociety.org/tileserver/' . OPTION_TILES_TYPE . '/');
-
+define('OPTION_MAP_TYPE', 'Tilma::Original::1_10k');
define('OPTION_EVEL_URL', 'http://services.mysociety.org/evel');
define('OPTION_GAZE_URL', 'http://gaze.mysociety.org/gaze');
diff --git a/db/alert_types.sql b/db/alert_types.sql
index d605d8e65..82a8c71f8 100644
--- a/db/alert_types.sql
+++ b/db/alert_types.sql
@@ -42,6 +42,17 @@ values ('local_problems', '', '',
'problem_find_nearby(?, ?, ?) as nearby,problem', 'nearby.problem_id = problem.id and problem.state in (''confirmed'', ''fixed'')', 'created desc',
'{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-nearby');
+-- New problems around a location
+insert into alert_type
+(ref, head_sql_query, head_table,
+ head_title, head_link, head_description,
+ item_table, item_where, item_order,
+ item_title, item_link, item_description, template)
+values ('local_problems_state', '', '',
+ 'New local problems on FixMyStreet', '/', 'The latest local problems reported by users',
+ 'problem_find_nearby(?, ?, ?) as nearby,problem', 'nearby.problem_id = problem.id and problem.state in (?)', 'created desc',
+ '{{title}}, {{confirmed}}', '/report/{{id}}', '{{detail}}', 'alert-problem-nearby');
+
-- New problems sent to a particular council
insert into alert_type
(ref, head_sql_query, head_table,
diff --git a/perllib/Cobrand.pm b/perllib/Cobrand.pm
index 4433858bb..328445fcc 100644
--- a/perllib/Cobrand.pm
+++ b/perllib/Cobrand.pm
@@ -75,8 +75,6 @@ my %fns = (
# Given a QUERY, return a hash of extra params to be included in
# any URLs in links produced on the page returned by that query.
'extra_params' => { default => "''" },
- # Returns a boolean indicating whether the map watermark should be displayed
- 'show_watermark' => { default => '1' },
# Returns any extra text to be displayed with a PROBLEM.
'extra_problem_meta_text' => { default => "''" },
# Returns any extra text to be displayed with an UPDATE.
diff --git a/perllib/Cobrands/Barnet/Util.pm b/perllib/Cobrands/Barnet/Util.pm
index 3cc60117f..32973c10c 100644
--- a/perllib/Cobrands/Barnet/Util.pm
+++ b/perllib/Cobrands/Barnet/Util.pm
@@ -3,18 +3,13 @@
# Util.pm:
# Barnet cobranding for FixMyStreet.
#
-#
# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
# Email: matthew@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: Util.pm,v 1.9 2009-12-22 13:04:05 matthew Exp $
package Cobrands::Barnet::Util;
-use Standard;
use strict;
use Carp;
use URI::Escape;
-use mySociety::Web qw(ent);
use mySociety::VotingArea;
sub new {
@@ -86,7 +81,7 @@ sub council_check {
$url .= 'alert' if $context eq 'alert';
$url .= '?pc=' . URI::Escape::uri_escape_utf8($q->param('pc')) if $q->param('pc');
my $error_msg = "That location is not covered by Barnet.
-Please visit <a href='$url'>the main FixMyStreet site</a>.";
+Please visit <a href=\"$url\">the main FixMyStreet site</a>.";
return (0, $error_msg);
}
diff --git a/perllib/Cobrands/Emptyhomes/Util.pm b/perllib/Cobrands/Emptyhomes/Util.pm
index f562a849b..acb870695 100644
--- a/perllib/Cobrands/Emptyhomes/Util.pm
+++ b/perllib/Cobrands/Emptyhomes/Util.pm
@@ -3,19 +3,14 @@
# Util.pm:
# Emptyhomes Cobranding for FixMyStreet.
#
-#
# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved.
# Email: louise@mysociety.org. WWW: http://www.mysociety.org
-#
-# $Id: Util.pm,v 1.8 2009-12-22 11:17:26 matthew Exp $
package Cobrands::Emptyhomes::Util;
-use Standard;
use strict;
use Carp;
-use mySociety::Web qw(ent);
-sub new{
+sub new {
my $class = shift;
return bless {}, $class;
}
@@ -44,7 +39,7 @@ Set the language and text domain for the site based on the query and host.
=cut
-sub set_lang_and_domain{
+sub set_lang_and_domain {
my ($self, $lang, $unicode) = @_;
mySociety::Locale::negotiate_language('en-gb,English,en_GB|cy,Cymraeg,cy_GB', $lang);
mySociety::Locale::gettext_domain('FixMyStreet-EmptyHomes', $unicode);
diff --git a/perllib/FixMyStreet/Alert.pm b/perllib/FixMyStreet/Alert.pm
new file mode 100644
index 000000000..9996f03c8
--- /dev/null
+++ b/perllib/FixMyStreet/Alert.pm
@@ -0,0 +1,339 @@
+#!/usr/bin/perl -w
+#
+# FixMyStreet::Alert.pm
+# Alerts by email or RSS.
+#
+# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
+# Email: matthew@mysociety.org; WWW: http://www.mysociety.org/
+#
+# $Id: Alert.pm,v 1.71 2010-01-06 16:50:27 louise Exp $
+
+package FixMyStreet::Alert::Error;
+
+use Error qw(:try);
+
+@FixMyStreet::Alert::Error::ISA = qw(Error::Simple);
+
+package FixMyStreet::Alert;
+
+use strict;
+use Error qw(:try);
+use File::Slurp;
+use FindBin;
+use POSIX qw(strftime);
+use XML::RSS;
+
+use Cobrand;
+use mySociety::AuthToken;
+use mySociety::Config;
+use mySociety::DBHandle qw(dbh);
+use mySociety::Email;
+use mySociety::EmailUtil;
+use mySociety::Gaze;
+use mySociety::GeoUtil;
+use mySociety::Locale;
+use mySociety::MaPit;
+use mySociety::Random qw(random_bytes);
+use mySociety::Sundries qw(ordinal);
+use mySociety::Web qw(ent);
+
+# Add a new alert
+sub create ($$$$;@) {
+ my ($email, $alert_type, $cobrand, $cobrand_data, @params) = @_;
+ my $already = 0;
+ if (0==@params) {
+ ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? limit 1',
+ {}, $alert_type, $email);
+ } elsif (1==@params) {
+ ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? limit 1',
+ {}, $alert_type, $email, @params);
+ } elsif (2==@params) {
+ ($already) = dbh()->selectrow_array('select id from alert where alert_type=? and email=? and parameter=? and parameter2=? limit 1',
+ {}, $alert_type, $email, @params);
+ }
+ return $already if $already;
+
+ my $id = dbh()->selectrow_array("select nextval('alert_id_seq');");
+ my $lang = $mySociety::Locale::lang;
+ if (0==@params) {
+ dbh()->do('insert into alert (id, alert_type, email, lang, cobrand, cobrand_data)
+ values (?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, $email, $lang, $cobrand, $cobrand_data);
+ } elsif (1==@params) {
+ dbh()->do('insert into alert (id, alert_type, parameter, email, lang, cobrand, cobrand_data)
+ values (?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
+ } elsif (2==@params) {
+ dbh()->do('insert into alert (id, alert_type, parameter, parameter2, email, lang, cobrand, cobrand_data)
+ values (?, ?, ?, ?, ?, ?, ?, ?)', {}, $id, $alert_type, @params, $email, $lang, $cobrand, $cobrand_data);
+ }
+ dbh()->commit();
+ return $id;
+}
+
+sub confirm ($) {
+ my $id = shift;
+ dbh()->do("update alert set confirmed=1, whendisabled=null where id=?", {}, $id);
+ dbh()->commit();
+}
+
+# Delete an alert
+sub delete ($) {
+ my $id = shift;
+ dbh()->do('update alert set whendisabled = ms_current_timestamp() where id = ?', {}, $id);
+ dbh()->commit();
+}
+
+# This makes load of assumptions, but still should be useful
+#
+# Child must have confirmed, id, email, state(!) columns
+# If parent/child, child table must also have name and text
+# and foreign key to parent must be PARENT_id
+
+sub email_alerts ($) {
+ my ($testing_email) = @_;
+ my $url;
+ my $q = dbh()->prepare("select * from alert_type where ref not like 'local_problems%'");
+ $q->execute();
+ my $testing_email_clause = '';
+ while (my $alert_type = $q->fetchrow_hashref) {
+ my $ref = $alert_type->{ref};
+ my $head_table = $alert_type->{head_table};
+ my $item_table = $alert_type->{item_table};
+ my $testing_email_clause = "and $item_table.email <> '$testing_email'" if $testing_email;
+ my $query = 'select alert.id as alert_id, alert.email as alert_email, alert.lang as alert_lang, alert.cobrand as alert_cobrand,
+ 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,
+ $head_table.*
+ from alert
+ inner join $item_table on alert.parameter::integer = $item_table.${head_table}_id
+ inner join $head_table on alert.parameter::integer = $head_table.id";
+ } else {
+ $query .= " $item_table.*,
+ $item_table.id as item_id
+ from alert, $item_table";
+ }
+ $query .= "
+ where alert_type='$ref' and whendisabled is null and $item_table.confirmed >= whensubscribed
+ and $item_table.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = alert.id and alert_sent.parameter::integer = $item_table.id) is null
+ and $item_table.email <> alert.email
+ $testing_email_clause
+ and $alert_type->{item_where}
+ and alert.confirmed = 1
+ order by alert.id, $item_table.confirmed";
+ # XXX Ugh - needs work
+ $query =~ s/\?/alert.parameter/ if ($query =~ /\?/);
+ $query =~ s/\?/alert.parameter2/ if ($query =~ /\?/);
+ $query = dbh()->prepare($query);
+ $query->execute();
+ my $last_alert_id;
+ my %data = ( template => $alert_type->{template}, data => '' );
+ while (my $row = $query->fetchrow_hashref) {
+ # Cobranded and non-cobranded messages can share a database. In this case, the conf file
+ # should specify a vhost to send the reports for each cobrand, so that they don't get sent
+ # more than once if there are multiple vhosts running off the same database. The email_host
+ # call checks if this is the host that sends mail for this cobrand.
+ next unless (Cobrand::email_host($row->{alert_cobrand}));
+ dbh()->do('insert into alert_sent (alert_id, parameter) values (?,?)', {}, $row->{alert_id}, $row->{item_id});
+ if ($last_alert_id && $last_alert_id != $row->{alert_id}) {
+ _send_aggregated_alert_email(%data);
+ %data = ( template => $alert_type->{template}, data => '' );
+ }
+
+ $url = Cobrand::base_url_for_emails($row->{alert_cobrand}, $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_text} . "\n\n------\n\n";
+ } else {
+ $data{data} .= $url . "/report/" . $row->{id} . " - $row->{title}\n\n";
+ }
+ if (!$data{alert_email}) {
+ %data = (%data, %$row);
+ if ($ref eq 'area_problems' || $ref eq 'council_problems' || $ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::get_voting_area_info($row->{alert_parameter});
+ $data{area_name} = $va_info->{name};
+ }
+ if ($ref eq 'ward_problems') {
+ my $va_info = mySociety::MaPit::get_voting_area_info($row->{alert_parameter2});
+ $data{ward_name} = $va_info->{name};
+ }
+ }
+ $data{cobrand} = $row->{alert_cobrand};
+ $data{cobrand_data} = $row->{alert_cobrand_data};
+ $data{lang} = $row->{alert_lang};
+ $last_alert_id = $row->{alert_id};
+ }
+ if ($last_alert_id) {
+ _send_aggregated_alert_email(%data);
+ }
+ }
+
+ # Nearby done separately as the table contains the parameters
+ my $template = dbh()->selectrow_array("select template from alert_type where ref = 'local_problems'");
+ my $query = "select * from alert where alert_type='local_problems' and whendisabled is null and confirmed=1 order by id";
+ $query = dbh()->prepare($query);
+ $query->execute();
+ while (my $alert = $query->fetchrow_hashref) {
+ next unless (Cobrand::email_host($alert->{cobrand}));
+ my $e = $alert->{parameter};
+ my $n = $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);
+ $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} );
+ my $q = "select * from problem_find_nearby(?, ?, ?) as nearby, problem
+ where nearby.problem_id = problem.id and problem.state in ('confirmed', 'fixed')
+ and problem.confirmed >= ? and problem.confirmed >= ms_current_timestamp() - '7 days'::interval
+ and (select whenqueued from alert_sent where alert_sent.alert_id = ? and alert_sent.parameter::integer = problem.id) is null
+ and problem.email <> ?
+ $testing_email_clause
+ $site_restriction
+ order by confirmed desc";
+ $q = dbh()->prepare($q);
+ $q->execute($e, $n, $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";
+ }
+ _send_aggregated_alert_email(%data) if $data{data};
+ }
+}
+
+sub _send_aggregated_alert_email(%) {
+ my %data = @_;
+ Cobrand::set_lang_and_domain($data{cobrand}, $data{lang}, 1);
+
+ $data{unsubscribe_url} = Cobrand::base_url_for_emails($data{cobrand}, $data{cobrand_data}) . '/A/'
+ . mySociety::AuthToken::store('alert', { id => $data{alert_id}, type => 'unsubscribe', email => $data{alert_email} } );
+ my $template = "$FindBin::Bin/../templates/emails/$data{template}";
+ if ($data{cobrand}) {
+ my $template_cobrand = "$FindBin::Bin/../templates/emails/$data{cobrand}/$data{template}";
+ $template = $template_cobrand if -e $template_cobrand;
+ }
+ $template = File::Slurp::read_file($template);
+ my $sender = Cobrand::contact_email($data{cobrand});
+ my $sender_name = Cobrand::contact_name($data{cobrand});
+ (my $from = $sender) =~ s/team/fms-DO-NOT-REPLY/; # XXX
+ my $email = mySociety::Email::construct_email({
+ _template_ => _($template),
+ _parameters_ => \%data,
+ From => [ $from, _($sender_name) ],
+ To => $data{alert_email},
+ 'Message-ID' => sprintf('<alert-%s-%s@mysociety.org>', time(), unpack('h*', random_bytes(5, 1))),
+ });
+
+ my $result = mySociety::EmailUtil::send_email($email, $sender, $data{alert_email});
+ if ($result == mySociety::EmailUtil::EMAIL_SUCCESS) {
+ dbh()->commit();
+ } else {
+ dbh()->rollback();
+ throw FixMyStreet::Alert::Error("Failed to send alert $data{alert_id}!");
+ }
+}
+
+sub generate_rss ($$$;$$$$) {
+ my ($type, $xsl, $qs, $db_params, $title_params, $cobrand, $http_q) = @_;
+ $db_params ||= [];
+ my $url = Cobrand::base_url($cobrand);
+ my $cobrand_data = Cobrand::extra_data($cobrand, $http_q);
+ my $q = dbh()->prepare('select * from alert_type where ref=?');
+ $q->execute($type);
+ my $alert_type = $q->fetchrow_hashref;
+ my ($site_restriction, $site_id) = Cobrand::site_restriction($cobrand, $cobrand_data);
+ throw FixMyStreet::Alert::Error('Unknown alert type') unless $alert_type;
+
+ # Do our own encoding
+
+ my $rss = new XML::RSS( version => '2.0', encoding => 'UTF-8',
+ stylesheet=> $xsl, encode_output => undef );
+ $rss->add_module(prefix=>'georss', uri=>'http://www.georss.org/georss');
+
+ # XXX: Not generic
+ # Only apply a site restriction if the alert uses the problem table
+ $site_restriction = '' unless $alert_type->{item_table} eq 'problem';
+ my $query = 'select * from ' . $alert_type->{item_table} . ' where '
+ . ($alert_type->{head_table} ? $alert_type->{head_table}.'_id=? and ' : '')
+ . $alert_type->{item_where} . $site_restriction . ' order by '
+ . $alert_type->{item_order};
+ $query .= ' limit 20' unless $type =~ /^all/;
+ $q = dbh()->prepare($query);
+ if ($query =~ /\?/) {
+ throw FixMyStreet::Alert::Error('Missing parameter') unless @$db_params;
+ $q->execute(@$db_params);
+ } else {
+ $q->execute();
+ }
+
+ my @months = ('', 'January','February','March','April','May','June',
+ 'July','August','September','October','November','December');
+ while (my $row = $q->fetchrow_hashref) {
+ # XXX: How to do this properly? name might be null in comment table, hence needing this
+ my $pubDate;
+ $row->{name} ||= 'anonymous';
+ # And we want pretty dates... :-/
+ if ($row->{confirmed}) {
+ $row->{confirmed} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/;
+ $pubDate = mySociety::Locale::in_gb_locale {
+ strftime("%a, %d %b %Y %H:%M:%S %z", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0)
+ };
+ $row->{confirmed} = ordinal($3+0) . ' ' . $months[$2];
+ }
+
+ (my $title = _($alert_type->{item_title})) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->{item_link}) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->{item_description})) =~ s/{{(.*?)}}/$row->{$1}/g;
+ my $cobrand_url = Cobrand::url($cobrand, $url . $link, $http_q);
+ my %item = (
+ title => ent($title),
+ link => $cobrand_url,
+ guid => $cobrand_url,
+ description => ent(ent($desc)) # Yes, double-encoded, really.
+ );
+ $item{pubDate} = $pubDate if $pubDate;
+
+ # XXX: Not-very-generic extensions, at all
+ my $display_photos = Cobrand::allow_photo_display($cobrand);
+ if ($display_photos && $row->{photo}) {
+ $item{description} .= ent("\n<br><img src=\"". Cobrand::url($cobrand, $url, $http_q) . "/photo?id=$row->{id}\">");
+ }
+ $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" };
+ }
+ $rss->add_item( %item );
+ }
+
+ my $row = {};
+ if ($alert_type->{head_sql_query}) {
+ $q = dbh()->prepare($alert_type->{head_sql_query});
+ if ($alert_type->{head_sql_query} =~ /\?/) {
+ $q->execute(@$db_params);
+ } else {
+ $q->execute();
+ }
+ $row = $q->fetchrow_hashref;
+ }
+ foreach (keys %$title_params) {
+ $row->{$_} = $title_params->{$_};
+ }
+ (my $title = _($alert_type->{head_title})) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->{head_link}) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->{head_description})) =~ s/{{(.*?)}}/$row->{$1}/g;
+ $rss->channel(
+ title => ent($title), link => "$url$link$qs", description => ent($desc),
+ language => 'en-gb'
+ );
+
+ my $out = $rss->as_string;
+ my $uri = Cobrand::url($cobrand, $ENV{SCRIPT_URI}, $http_q);
+ $out =~ s{<link>(.*?)</link>}{"<link>" . Cobrand::url($cobrand, $1, $http_q) . "</link><uri>$uri</uri>"}e;
+
+ return $out;
+}
diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm
new file mode 100644
index 000000000..9e89b4f7b
--- /dev/null
+++ b/perllib/FixMyStreet/Geocode.pm
@@ -0,0 +1,156 @@
+#!/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;
+
+use strict;
+use Error qw(:try);
+use File::Slurp;
+use LWP::Simple;
+use Digest::MD5 qw(md5_hex);
+use URI::Escape;
+
+use Cobrand;
+use Page;
+use mySociety::Config;
+use mySociety::GeoUtil;
+use mySociety::MaPit;
+use mySociety::PostcodeUtil;
+use mySociety::Web qw(NewURL);
+
+BEGIN {
+ (my $dir = __FILE__) =~ s{/[^/]*?$}{};
+ mySociety::Config::set_file("$dir/../../conf/general");
+}
+
+# lookup STRING QUERY
+# 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
+# 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, $q) = @_;
+ my ($easting, $northing, $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};
+ }
+ } else {
+ ($easting, $northing, $error) = FixMyStreet::Geocode::string($s, $q);
+ }
+ return ($easting, $northing, $error);
+}
+
+sub geocoded_string_coordinates {
+ my ($js, $q) = @_;
+ my ($easting, $northing, $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);
+}
+
+# string STRING QUERY
+# Canonicalises, looks up on Google Maps API, and caches, a user-inputted location.
+# Returns array of (TILE_X, TILE_Y, EASTING, NORTHING, 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, $q) = @_;
+ $s = lc($s);
+ $s =~ s/[^-&0-9a-z ']/ /g;
+ $s =~ s/\s+/ /g;
+ $s = URI::Escape::uri_escape_utf8($s);
+ $s = Cobrand::disambiguate_location(Page::get_cobrand($q), "q=$s", $q);
+ $s =~ s/%20/+/g;
+ 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);
+ if (-s $cache_file) {
+ $js = File::Slurp::read_file($cache_file);
+ } else {
+ $url .= ',+UK' unless $url =~ /united\++kingdom$/ || $url =~ /uk$/i;
+ $url .= '&sensor=false&gl=uk&key=' . mySociety::Config::get('GOOGLE_MAPS_API_KEY');
+ $js = LWP::Simple::get($url);
+ File::Slurp::write_file($cache_file, $js) if $js && $js !~ /"code":6[12]0/;
+ }
+ if (!$js) {
+ $error = _('Sorry, we could not parse that location. Please try again.');
+ } elsif ($js !~ /"code" *: *200/) {
+ $error = _('Sorry, we could not find that location.');
+ } elsif ($js =~ /}, *{/) { # Multiple
+ my @js = split /}, *{/, $js;
+ my @valid_locations;
+ foreach (@js) {
+ next unless /"address" *: *"(.*?)"/s;
+ my $address = $1;
+ next unless Cobrand::geocoded_string_check(Page::get_cobrand($q), $address, $q);
+ next if $address =~ /BT\d/;
+ push (@valid_locations, $_);
+ push (@$error, $address);
+ }
+ if (scalar @valid_locations == 1) {
+ return geocoded_string_coordinates($valid_locations[0], $q);
+ }
+ $error = _('Sorry, we could not find that location.') unless $error;
+ } elsif ($js =~ /BT\d/) {
+ # 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);
+ }
+ return ($easting, $northing, $error);
+}
+
+# list_choices
+# Prints response if there's more than one possible result
+sub list_choices {
+ my ($choices, $page, $q) = @_;
+ my $url;
+ my $cobrand = Page::get_cobrand($q);
+ my $message = _('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.');
+ my $out = '<p>' . $message . '</p>';
+ my $choice_list = '<ul>';
+ foreach my $choice (@$choices) {
+ $choice =~ s/, United Kingdom//;
+ $choice =~ s/, UK//;
+ $url = Cobrand::url($cobrand, NewURL($q, -retain => 1, -url => $page, 'pc' => $choice), $q);
+ $url =~ s/%20/+/g;
+ $choice_list .= '<li><a href="' . $url . '">' . $choice . "</a></li>\n";
+ }
+ $choice_list .= '</ul>';
+ $out .= $choice_list;
+ my %vars = (message => $message,
+ choice_list => $choice_list,
+ header => _('More than one match'),
+ url_home => Cobrand::url($cobrand, '/', $q));
+ my $cobrand_choice = Page::template_include('geocode-choice', $q, Page::template_root($q), %vars);
+ return $cobrand_choice if $cobrand_choice;
+ return $out;
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm
new file mode 100644
index 000000000..12ecf78fe
--- /dev/null
+++ b/perllib/FixMyStreet/Map.pm
@@ -0,0 +1,114 @@
+#!/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;
+
+use strict;
+
+use Problems;
+use Cobrand;
+use mySociety::Config;
+use mySociety::Gaze;
+use mySociety::GeoUtil;
+use mySociety::Locale;
+use mySociety::Web qw(ent NewURL);
+
+# 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 $class = "FixMyStreet::Map::$type";
+ eval "use $class";
+}
+
+sub header {
+ 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 $encoding = '';
+ $encoding = ' enctype="multipart/form-data"' if $type==2;
+ my $pc = $q->param('pc') || '';
+ my $pc_enc = ent($pc);
+ return <<EOF;
+<form action="$form_action" method="post" name="mapForm" id="mapForm"$encoding>
+<input type="hidden" name="submit_map" value="1">
+<input type="hidden" name="pc" value="$pc_enc">
+$cobrand_form_elements
+EOF
+}
+
+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 $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 = 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);
+
+ return ($around_map, $around_map_list, $nearby, $dist);
+}
+
+sub compass ($$$) {
+ 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);
+ }
+ }
+ my $recentre = NewURL($q);
+ my $host = Page::base_url_with_lang($q, undef);
+ return <<EOF;
+<table cellpadding="0" cellspacing="0" border="0" id="compass">
+<tr valign="bottom">
+<td align="right"><a rel="nofollow" href="${compass[$x-1][$y+1]}"><img src="$host/i/arrow-northwest.gif" alt="NW" width=11 height=11></a></td>
+<td align="center"><a rel="nofollow" href="${compass[$x][$y+1]}"><img src="$host/i/arrow-north.gif" vspace="3" alt="N" width=13 height=11></a></td>
+<td><a rel="nofollow" href="${compass[$x+1][$y+1]}"><img src="$host/i/arrow-northeast.gif" alt="NE" width=11 height=11></a></td>
+</tr>
+<tr>
+<td><a rel="nofollow" href="${compass[$x-1][$y]}"><img src="$host/i/arrow-west.gif" hspace="3" alt="W" width=11 height=13></a></td>
+<td align="center"><a rel="nofollow" href="$recentre"><img src="$host/i/rose.gif" alt="Recentre" width=35 height=34></a></td>
+<td><a rel="nofollow" href="${compass[$x+1][$y]}"><img src="$host/i/arrow-east.gif" hspace="3" alt="E" width=11 height=13></a></td>
+</tr>
+<tr valign="top">
+<td align="right"><a rel="nofollow" href="${compass[$x-1][$y-1]}"><img src="$host/i/arrow-southwest.gif" alt="SW" width=11 height=11></a></td>
+<td align="center"><a rel="nofollow" href="${compass[$x][$y-1]}"><img src="$host/i/arrow-south.gif" vspace="3" alt="S" width=13 height=11></a></td>
+<td><a rel="nofollow" href="${compass[$x+1][$y-1]}"><img src="$host/i/arrow-southeast.gif" alt="SE" width=11 height=11></a></td>
+</tr>
+</table>
+EOF
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Bing.pm b/perllib/FixMyStreet/Map/Bing.pm
new file mode 100644
index 000000000..8446a10fd
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Bing.pm
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::Bing
+# Bing 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;
+
+use strict;
+use mySociety::GeoUtil;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script>
+<script type="text/javascript" src="/js/map-bing.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($params{easting}, $params{northing}, 'G');
+ my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'lat': $lat,
+ 'lon': $lon
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/BingOL.pm b/perllib/FixMyStreet/Map/BingOL.pm
new file mode 100644
index 000000000..3939a710f
--- /dev/null
+++ b/perllib/FixMyStreet/Map/BingOL.pm
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::Bing
+# Bing 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;
+
+use strict;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<!-- <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-GB"></script> -->
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-bing-ol.js"></script>
+<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010. Microsoft');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'easting': $params{easting},
+ 'northing': $params{northing}
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Google.pm b/perllib/FixMyStreet/Map/Google.pm
new file mode 100644
index 000000000..7a314efad
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Google.pm
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::Google
+# Google 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;
+
+use strict;
+use mySociety::GeoUtil;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
+<script type="text/javascript" src="/js/map-google.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my ($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($params{easting}, $params{northing}, 'G');
+ my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'lat': $lat,
+ 'lon': $lon
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm
new file mode 100644
index 000000000..ccbb3ca53
--- /dev/null
+++ b/perllib/FixMyStreet/Map/OSM.pm
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::OSM
+# OSM 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;
+
+use strict;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenStreetMap.js"></script>
+<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $copyright = _('Map &copy; <a href="http://www.openstreetmap.org/">OpenStreetMap</a> and contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'easting': $params{easting},
+ 'northing': $params{northing},
+ 'map_type': OpenLayers.Layer.OSM.Mapnik
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/OSM/CycleMap.pm b/perllib/FixMyStreet/Map/OSM/CycleMap.pm
new file mode 100644
index 000000000..01c51acf4
--- /dev/null
+++ b/perllib/FixMyStreet/Map/OSM/CycleMap.pm
@@ -0,0 +1,71 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::OSM::CycleMap
+# OSM CycleMap 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;
+
+use strict;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-OpenStreetMap.js"></script>
+<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $copyright = _('Map &copy; <a href="http://www.openstreetmap.org/">OpenStreetMap</a> and contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'easting': $params{easting},
+ 'northing': $params{northing},
+ 'map_type': OpenLayers.Layer.OSM.CycleMap
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/OSM/StreetView.pm b/perllib/FixMyStreet/Map/OSM/StreetView.pm
new file mode 100644
index 000000000..08f677d25
--- /dev/null
+++ b/perllib/FixMyStreet/Map/OSM/StreetView.pm
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+#
+# FixMyStreet:Map::OSM::StreetView
+# OSM StreetView 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;
+
+use strict;
+use mySociety::Web qw(ent);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-streetview.js"></script>
+<script type="text/javascript" src="/js/OpenLayers.Projection.OrdnanceSurvey.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'easting': $params{easting},
+ 'northing': $params{northing}
+}
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm b/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm
new file mode 100644
index 000000000..b1fe0126d
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/OL/1_10k.pm
@@ -0,0 +1,83 @@
+#!/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;
+
+use strict;
+
+use constant TILE_WIDTH => 254;
+use constant TIF_SIZE_M => 5000;
+use constant TIF_SIZE_PX => 7874;
+use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
+use constant TILE_TYPE => '10k-full';
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-tilma-ol.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $tile_width = TILE_WIDTH;
+ my $tile_type = TILE_TYPE;
+ my $sf = SCALE_FACTOR / TILE_WIDTH;
+ my $copyright = _('&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008.');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'tilewidth': $tile_width,
+ 'tileheight': $tile_width,
+ 'easting': $params{easting},
+ 'northing': $params{northing},
+ 'tile_type': '$tile_type',
+ 'maxResolution': $sf
+};
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map">
+ <div id="watermark"></div>
+ </div>
+ <p id="copyright">$copyright</p>
+$params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm b/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm
new file mode 100644
index 000000000..7ef372351
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/OL/StreetView.pm
@@ -0,0 +1,81 @@
+#!/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;
+
+use strict;
+
+use constant TILE_WIDTH => 250;
+use constant TIF_SIZE_M => 5000;
+use constant TIF_SIZE_PX => 5000;
+use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
+use constant TILE_TYPE => 'streetview';
+
+sub header_js {
+ return '
+<script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script>
+<script type="text/javascript" src="/js/map-tilma-ol.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+
+ foreach my $pin (@{$params{pins}}) {
+ }
+
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $tile_width = TILE_WIDTH;
+ my $tile_type = TILE_TYPE;
+ my $sf = SCALE_FACTOR / TILE_WIDTH;
+ my $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
+ $out .= <<EOF;
+<script type="text/javascript">
+var fixmystreet = {
+ 'tilewidth': $tile_width,
+ 'tileheight': $tile_width,
+ 'easting': $params{easting},
+ 'northing': $params{northing},
+ 'tile_type': '$tile_type',
+ 'maxResolution': $sf
+};
+</script>
+<div id="map_box">
+ $params{pre}
+ <div id="map"></div>
+ <p id="copyright">$copyright</p>
+ $params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+}
+
+sub map_pins {
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Tilma/Original.pm b/perllib/FixMyStreet/Map/Tilma/Original.pm
new file mode 100644
index 000000000..5772f6ccd
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/Original.pm
@@ -0,0 +1,257 @@
+#!/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;
+
+use strict;
+use LWP::Simple;
+
+use Cobrand;
+use mySociety::Web qw(ent NewURL);
+
+sub header_js {
+ return '
+<script type="text/javascript" src="/js/map-tilma.js"></script>
+';
+}
+
+# display_map Q PARAMS
+# PARAMS include:
+# EASTING, NORTHING 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
+# PRE/POST are HTML to show above/below map
+sub _display_map {
+ my ($q, %params) = @_;
+ $params{pre} ||= '';
+ $params{post} ||= '';
+ my $mid_point = TILE_WIDTH; # Map is 2 TILE_WIDTHs in size, square.
+ if ($q->{site} eq 'barnet') { # Map is c. 380px wide
+ $mid_point = 189;
+ }
+
+ # X/Y tile co-ords may be overridden in the query string
+ my @vars = qw(x y);
+ my %input = map { $_ => $q->param($_) || '' } @vars;
+ ($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0;
+ ($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0;
+
+ my ($x, $y, $px, $py) = FixMyStreet::Map::os_to_px_with_adjust($q, $params{easting}, $params{northing}, $input{x}, $input{y});
+
+ my $pins = '';
+ foreach my $pin (@{$params{pins}}) {
+ my $pin_x = FixMyStreet::Map::os_to_px($pin->[0], $x);
+ my $pin_y = FixMyStreet::Map::os_to_px($pin->[1], $y, 1);
+ $pins .= FixMyStreet::Map::display_pin($q, $pin_x, $pin_y, $pin->[2]);
+ }
+
+ $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);
+ return '<div id="map_box"> <div id="map"><div id="drag">' . _("Unable to fetch the map tiles from the tile server.") . '</div></div></div><div id="side">' if !$tiles;
+ my $tileids = RABX::unserialise($tiles);
+ my $tl = ($x-1) . '.' . $y;
+ my $tr = $x . '.' . $y;
+ my $bl = ($x-1) . '.' . ($y-1);
+ my $br = $x . '.' . ($y-1);
+ return '<div id="side">' if (!$tileids->[0][0] || !$tileids->[0][1] || !$tileids->[1][0] || !$tileids->[1][1]);
+ my $tl_src = $url . $tileids->[0][0];
+ my $tr_src = $url . $tileids->[0][1];
+ my $bl_src = $url . $tileids->[1][0];
+ my $br_src = $url . $tileids->[1][1];
+
+ my $cobrand = Page::get_cobrand($q);
+ my $root_path_js = Cobrand::root_path_js($cobrand, $q);
+ my $out = FixMyStreet::Map::header($q, $params{type});
+ my $img_type;
+ if ($params{type}) {
+ $out .= <<EOF;
+<input type="hidden" name="x" id="formX" value="$x">
+<input type="hidden" name="y" id="formY" value="$y">
+EOF
+ $img_type = '<input type="image"';
+ } else {
+ $img_type = '<img';
+ }
+ my $imgw = TILE_WIDTH . 'px';
+ my $tile_width = TILE_WIDTH;
+ my $tile_type = TILE_TYPE;
+ $out .= <<EOF;
+<script type="text/javascript">
+$root_path_js
+var fixmystreet = {
+ 'x': $x - 3,
+ 'y': $y - 3,
+ 'start_x': $px,
+ 'start_y': $py,
+ 'tile_type': '$tile_type',
+ 'tilewidth': $tile_width,
+ 'tileheight': $tile_width
+};
+</script>
+<div id="map_box">
+$params{pre}
+ <div id="map"><div id="drag">
+ $img_type alt="NW map tile" id="t2.2" name="tile_$tl" src="$tl_src" style="top:0px; left:0;">$img_type alt="NE map tile" id="t2.3" name="tile_$tr" src="$tr_src" style="top:0px; left:$imgw;"><br>$img_type alt="SW map tile" id="t3.2" name="tile_$bl" src="$bl_src" style="top:$imgw; left:0;">$img_type alt="SE map tile" id="t3.3" name="tile_$br" src="$br_src" style="top:$imgw; left:$imgw;">
+ <div id="pins">$pins</div>
+ </div>
+EOF
+ $out .= '<div id="watermark"></div>' if $params{watermark};
+ $out .= compass($q, $x, $y);
+ my $copyright = $params{copyright};
+ $out .= <<EOF;
+ </div>
+ <p id="copyright">$copyright</p>
+$params{post}
+</div>
+<div id="side">
+EOF
+ return $out;
+}
+
+sub display_map_end {
+ my ($type) = @_;
+ my $out = '</div>';
+ $out .= '</form>' if ($type);
+ return $out;
+}
+
+sub display_pin {
+ my ($q, $px, $py, $col, $num) = @_;
+ $num = '' if !$num || $num > 9;
+ my $host = Page::base_url_with_lang($q, undef);
+ 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} && $col ne 'blue';
+ my $cobrand = Page::get_cobrand($q);
+ my $url = Cobrand::url($cobrand, NewURL($q, -retain => 1,
+ -url => '/report/' . $_->{id},
+ pc => undef,
+ x => undef,
+ y => undef,
+ sx => undef,
+ sy => undef,
+ all_pins => undef,
+ no_pins => undef), $q);
+ $out = '<a title="' . ent($_->{title}) . '" href="' . $url . '">' . $out . '</a>';
+ return $out;
+}
+
+sub map_pins {
+ my ($q, $x, $y, $sx, $sy, $interval) = @_;
+
+ 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 $pins = '';
+ foreach (@$around_map) {
+ 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';
+ $pins .= FixMyStreet::Map::display_pin($q, $px, $py, $col);
+ }
+
+ foreach (@$nearby) {
+ 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';
+ $pins .= FixMyStreet::Map::display_pin($q, $px, $py, $col);
+ }
+
+ 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);
+}
+
+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 = FixMyStreet::Map::click_to_tile($pin_tile_x, $pin_x);
+ my $tile_y = FixMyStreet::Map::click_to_tile($pin_tile_y, $pin_y, 1);
+ my $easting = FixMyStreet::Map::tile_to_os($tile_x);
+ my $northing = FixMyStreet::Map::tile_to_os($tile_y);
+ return ($easting, $northing);
+}
+
+# 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 ($q, $easting, $northing, $in_x, $in_y) = @_;
+
+ my $x = FixMyStreet::Map::os_to_tile($easting);
+ my $y = FixMyStreet::Map::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 = FixMyStreet::Map::os_to_px($easting, $x_tile);
+ my $py = FixMyStreet::Map::os_to_px($northing, $y_tile, 1);
+ if ($q->{site} eq 'barnet') { # Map is 380px, so might need to adjust
+ if (!$in_x && $px > 380) {
+ $x_tile++;
+ $px = FixMyStreet::Map::os_to_px($easting, $x_tile);
+ }
+ if (!$in_y && $py > 380) {
+ $y_tile--;
+ $py = FixMyStreet::Map::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
new file mode 100644
index 000000000..f97163c68
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/Original/1_10k.pm
@@ -0,0 +1,28 @@
+#!/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;
+
+use strict;
+
+use constant TILE_WIDTH => 254;
+use constant TIF_SIZE_M => 5000;
+use constant TIF_SIZE_PX => 7874;
+use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
+use constant TILE_TYPE => '10k-full';
+
+use FixMyStreet::Map::Tilma::Original;
+
+sub display_map {
+ my ($q, %params) = @_;
+ $params{copyright} = _('&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008.');
+ $params{watermark} = 1;
+ return _display_map($q, %params);
+}
+
+1;
diff --git a/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm b/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm
new file mode 100644
index 000000000..103f4c15c
--- /dev/null
+++ b/perllib/FixMyStreet/Map/Tilma/Original/StreetView.pm
@@ -0,0 +1,27 @@
+#!/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;
+
+use strict;
+
+use constant TILE_WIDTH => 250;
+use constant TIF_SIZE_M => 5000;
+use constant TIF_SIZE_PX => 5000;
+use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
+use constant TILE_TYPE => 'streetview';
+
+use FixMyStreet::Map::Tilma::Original;
+
+sub display_map {
+ my ($q, %params) = @_;
+ $params{copyright} = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
+ return _display_map($q, %params);
+}
+
+1;
diff --git a/perllib/Page.pm b/perllib/Page.pm
index 127fad11b..1b94f5c2b 100644
--- a/perllib/Page.pm
+++ b/perllib/Page.pm
@@ -16,41 +16,34 @@ use Carp;
use mySociety::CGIFast qw(-no_xhtml);
use Error qw(:try);
use File::Slurp;
-use HTTP::Date;
+use HTTP::Date; # time2str
use Image::Magick;
use Image::Size;
-use LWP::Simple;
-use Digest::MD5 qw(md5_hex);
use POSIX qw(strftime);
use URI::Escape;
use Text::Template;
use Memcached;
use Problems;
-use Utils;
use Cobrand;
+
use mySociety::Config;
use mySociety::DBHandle qw/dbh select_all/;
use mySociety::EvEl;
-use mySociety::Gaze;
-use mySociety::GeoUtil;
use mySociety::Locale;
use mySociety::MaPit;
-use mySociety::PostcodeUtil;
use mySociety::TempFiles;
use mySociety::Tracking;
use mySociety::WatchUpdate;
-use mySociety::Web qw(ent NewURL);
+use mySociety::Web qw(ent);
BEGIN {
(my $dir = __FILE__) =~ s{/[^/]*?$}{};
mySociety::Config::set_file("$dir/../conf/general");
}
-use constant TILE_WIDTH => mySociety::Config::get('TILES_WIDTH');
-use constant TIF_SIZE_M => mySociety::Config::get('TILES_TIFF_SIZE_METRES');
-use constant TIF_SIZE_PX => mySociety::Config::get('TILES_TIFF_SIZE_PIXELS');
-use constant SCALE_FACTOR => TIF_SIZE_M / (TIF_SIZE_PX / TILE_WIDTH);
+# Under the BEGIN so that the config has been set.
+use FixMyStreet::Map;
my $lastmodified;
@@ -205,12 +198,17 @@ sub template_vars ($%) {
'lang_url' => $lang_url,
'title' => $params{title},
'rss' => '',
+ map_js => FixMyStreet::Map::header_js(),
);
if ($params{rss}) {
$vars{rss} = '<link rel="alternate" type="application/rss+xml" title="' . $params{rss}[0] . '" href="' . $params{rss}[1] . '">';
}
+ if ($params{robots}) {
+ $vars{robots} = '<meta name="robots" content="' . $params{robots} . '">';
+ }
+
if ($q->{site} eq 'fixmystreet') {
my $home = !$params{title} && $ENV{SCRIPT_NAME} eq '/index.cgi' && !$ENV{QUERY_STRING};
$vars{heading_element_start} = $home ? '<h1 id="header">' : '<div id="header"><a href="/">';
@@ -285,7 +283,7 @@ sub header ($%) {
my $default_params = Cobrand::header_params(get_cobrand($q), $q, %params);
my %default_params = %{$default_params};
%params = (%default_params, %params);
- my %permitted_params = map { $_ => 1 } qw(title rss js expires lastmodified template cachecontrol context status_code);
+ my %permitted_params = map { $_ => 1 } qw(title rss expires lastmodified template cachecontrol context status_code robots);
foreach (keys %params) {
croak "bad parameter '$_'" if (!exists($permitted_params{$_}));
}
@@ -318,8 +316,6 @@ sub header ($%) {
sub footer {
my ($q, %params) = @_;
my $extra = $params{extra};
- my $js = $params{js} || '';
- $js = ''; # Don't use fileupload JS at the moment
if ($q->{site} ne 'fixmystreet') {
my $template = template($q, %params) . '-footer';
@@ -381,8 +377,6 @@ $orglogo
$track
-$js
-
$piwik
</body>
@@ -401,275 +395,6 @@ sub error_page ($$) {
print $q->header(-content_length => length($html)), $html;
}
-# display_map Q PARAMS
-# PARAMS include:
-# X,Y is bottom left tile of 2x2 grid
-# TYPE is 1 if the map is clickable, 2 if clickable and has a form upload,
-# 0 if not clickable
-# PINS is HTML of pins to show
-# PX,PY are coordinates of pin
-# PRE/POST are HTML to show above/below map
-sub display_map {
- my ($q, %params) = @_;
- $params{pins} ||= '';
- $params{pre} ||= '';
- $params{post} ||= '';
- my $mid_point = TILE_WIDTH; # Map is 2 TILE_WIDTHs in size, square.
- if ($q->{site} eq 'barnet') { # Map is c. 380px wide
- $mid_point = 189;
- }
- my $px = defined($params{px}) ? $mid_point - $params{px} : 0;
- my $py = defined($params{py}) ? $mid_point - $params{py} : 0;
- my $x = int($params{x})<=0 ? 0 : $params{x};
- my $y = int($params{y})<=0 ? 0 : $params{y};
- my $url = mySociety::Config::get('TILES_URL');
- my $tiles_url = $url . ($x-1) . '-' . $x . ',' . ($y-1) . '-' . $y . '/RABX';
- my $tiles = LWP::Simple::get($tiles_url);
- return '<div id="map_box"> <div id="map"><div id="drag">' . _("Unable to fetch the map tiles from the tile server.") . '</div></div></div><div id="side">' if !$tiles;
- my $tileids = RABX::unserialise($tiles);
- my $tl = ($x-1) . '.' . $y;
- my $tr = $x . '.' . $y;
- my $bl = ($x-1) . '.' . ($y-1);
- my $br = $x . '.' . ($y-1);
- return '<div id="side">' if (!$tileids->[0][0] || !$tileids->[0][1] || !$tileids->[1][0] || !$tileids->[1][1]);
- my $tl_src = $url . $tileids->[0][0];
- my $tr_src = $url . $tileids->[0][1];
- my $bl_src = $url . $tileids->[1][0];
- my $br_src = $url . $tileids->[1][1];
-
- my $out = '';
- my $cobrand = Page::get_cobrand($q);
- my $root_path_js = Cobrand::root_path_js($cobrand, $q);
- my $cobrand_form_elements = Cobrand::form_elements($cobrand, 'mapForm', $q);
- my $img_type;
- my $form_action = Cobrand::url($cobrand, '', $q);
- if ($params{type}) {
- my $encoding = '';
- $encoding = ' enctype="multipart/form-data"' if ($params{type}==2);
- my $pc = $q->param('pc') || '';
- my $pc_enc = ent($pc);
- $out .= <<EOF;
-<form action="$form_action" method="post" name="mapForm" id="mapForm"$encoding>
-<input type="hidden" name="submit_map" value="1">
-<input type="hidden" name="x" id="formX" value="$x">
-<input type="hidden" name="y" id="formY" value="$y">
-<input type="hidden" name="pc" value="$pc_enc">
-$cobrand_form_elements
-EOF
- $img_type = '<input type="image"';
- } else {
- $img_type = '<img';
- }
- my $imgw = TILE_WIDTH . 'px';
- my $tile_width = TILE_WIDTH;
- my $tile_type = mySociety::Config::get('TILES_TYPE');
- $out .= <<EOF;
-<script type="text/javascript">
-$root_path_js
-var fixmystreet = {
- 'x': $x - 3,
- 'y': $y - 3,
- 'start_x': $px,
- 'start_y': $py,
- 'tile_type': '$tile_type',
- 'tilewidth': $tile_width,
- 'tileheight': $tile_width
-};
-</script>
-<div id="map_box">
-$params{pre}
- <div id="map"><div id="drag">
- $img_type alt="NW map tile" id="t2.2" name="tile_$tl" src="$tl_src" style="top:0px; left:0;">$img_type alt="NE map tile" id="t2.3" name="tile_$tr" src="$tr_src" style="top:0px; left:$imgw;"><br>$img_type alt="SW map tile" id="t3.2" name="tile_$bl" src="$bl_src" style="top:$imgw; left:0;">$img_type alt="SE map tile" id="t3.3" name="tile_$br" src="$br_src" style="top:$imgw; left:$imgw;">
- <div id="pins">$params{pins}</div>
- </div>
-EOF
- if (Cobrand::show_watermark($cobrand) && mySociety::Config::get('TILES_TYPE') ne 'streetview') {
- $out .= '<div id="watermark"></div>';
- }
- $out .= compass($q, $x, $y);
- my $copyright;
- if (mySociety::Config::get('TILES_TYPE') eq 'streetview') {
- $copyright = _('Map contains Ordnance Survey data &copy; Crown copyright and database right 2010.');
- } else {
- $copyright = _('&copy; Crown copyright. All rights reserved. Ministry of Justice 100037819&nbsp;2008.');
- }
- $out .= <<EOF;
- </div>
- <p id="copyright">$copyright</p>
-$params{post}
-EOF
- $out .= '</div>';
- $out .= '<div id="side">';
- return $out;
-}
-
-sub display_map_end {
- my ($type) = @_;
- my $out = '</div>';
- $out .= '</form>' if ($type);
- return $out;
-}
-
-sub display_pin {
- my ($q, $px, $py, $col, $num) = @_;
- $num = '' if !$num || $num > 9;
- my $host = base_url_with_lang($q, undef);
- 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} && $col ne 'blue';
- my $cobrand = Page::get_cobrand($q);
- my $url = Cobrand::url($cobrand, NewURL($q, -retain => 1,
- -url => '/report/' . $_->{id},
- pc => undef,
- x => undef,
- y => undef,
- sx => undef,
- sy => undef,
- all_pins => undef,
- no_pins => undef), $q);
- $out = '<a title="' . ent($_->{title}) . '" href="' . $url . '">' . $out . '</a>';
- return $out;
-}
-
-sub map_pins {
- my ($q, $x, $y, $sx, $sy, $interval) = @_;
-
- my $pins = '';
- my $min_e = Page::tile_to_os($x-3); # Extra space to left/below due to rounding, I think
- my $min_n = Page::tile_to_os($y-3);
- my $mid_e = Page::tile_to_os($x);
- my $mid_n = Page::tile_to_os($y);
- my $max_e = Page::tile_to_os($x+2);
- my $max_n = Page::tile_to_os($y+2);
- my $cobrand = Page::get_cobrand($q);
- # list of problems aoround map can be limited, but should show all pins
- my $around_limit = Cobrand::on_map_list_limit($cobrand);
- my $around_map;
- my $around_map_list = Problems::around_map($min_e, $max_e, $min_n, $max_n, $interval, $around_limit);
- if ($around_limit) {
- $around_map = Problems::around_map($min_e, $max_e, $min_n, $max_n, $interval, undef);
- } else {
- $around_map = $around_map_list;
- }
- my @ids = ();
- foreach (@$around_map_list) {
- push(@ids, $_->{id});
- }
- foreach (@$around_map) {
- my $px = Page::os_to_px($_->{easting}, $sx);
- my $py = Page::os_to_px($_->{northing}, $sy, 1);
- my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
- $pins .= Page::display_pin($q, $px, $py, $col);
- }
-
- 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 = int($dist*10+0.5)/10;
-
- my $limit = 20; # - @$current_map;
- my $nearby = Problems::nearby($dist, join(',', @ids), $limit, $mid_e, $mid_n, $interval);
- foreach (@$nearby) {
- my $px = Page::os_to_px($_->{easting}, $sx);
- my $py = Page::os_to_px($_->{northing}, $sy, 1);
- my $col = $_->{state} eq 'fixed' ? 'green' : 'red';
- $pins .= Page::display_pin($q, $px, $py, $col);
- }
-
- return ($pins, $around_map_list, $nearby, $dist);
-}
-
-sub compass ($$$) {
- 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);
- }
- }
- my $recentre = NewURL($q);
- my $host = base_url_with_lang($q, undef);
- return <<EOF;
-<table cellpadding="0" cellspacing="0" border="0" id="compass">
-<tr valign="bottom">
-<td align="right"><a rel="nofollow" href="${compass[$x-1][$y+1]}"><img src="$host/i/arrow-northwest.gif" alt="NW" width=11 height=11></a></td>
-<td align="center"><a rel="nofollow" href="${compass[$x][$y+1]}"><img src="$host/i/arrow-north.gif" vspace="3" alt="N" width=13 height=11></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y+1]}"><img src="$host/i/arrow-northeast.gif" alt="NE" width=11 height=11></a></td>
-</tr>
-<tr>
-<td><a rel="nofollow" href="${compass[$x-1][$y]}"><img src="$host/i/arrow-west.gif" hspace="3" alt="W" width=11 height=13></a></td>
-<td align="center"><a rel="nofollow" href="$recentre"><img src="$host/i/rose.gif" alt="Recentre" width=35 height=34></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y]}"><img src="$host/i/arrow-east.gif" hspace="3" alt="E" width=11 height=13></a></td>
-</tr>
-<tr valign="top">
-<td align="right"><a rel="nofollow" href="${compass[$x-1][$y-1]}"><img src="$host/i/arrow-southwest.gif" alt="SW" width=11 height=11></a></td>
-<td align="center"><a rel="nofollow" href="${compass[$x][$y-1]}"><img src="$host/i/arrow-south.gif" vspace="3" alt="S" width=13 height=11></a></td>
-<td><a rel="nofollow" href="${compass[$x+1][$y-1]}"><img src="$host/i/arrow-southeast.gif" alt="SE" width=11 height=11></a></td>
-</tr>
-</table>
-EOF
-}
-
-# 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);
-}
-
-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;
-}
-
-sub os_to_px_with_adjust {
- my ($q, $easting, $northing, $in_x, $in_y) = @_;
-
- my $x = Page::os_to_tile($easting);
- my $y = Page::os_to_tile($northing);
- my $x_tile = $in_x || int($x);
- my $y_tile = $in_y || int($y);
- my $px = Page::os_to_px($easting, $x_tile);
- my $py = Page::os_to_px($northing, $y_tile, 1);
- if ($q->{site} eq 'barnet') { # Map is 380px
- if ($py > 380) {
- $y_tile--;
- $py = Page::os_to_px($northing, $y_tile, 1);
- }
- if ($px > 380) {
- $x_tile++;
- $px = Page::os_to_px($easting, $x_tile);
- }
- }
- return ($x, $y, $x_tile, $y_tile, $px, $py);
-}
-
# send_email TO (NAME) TEMPLATE-NAME PARAMETERS
# TEMPLATE-NAME is currently one of problem, update, alert, tms
sub send_email {
@@ -927,143 +652,6 @@ sub mapit_check_error {
return 0;
}
-# geocode STRING QUERY
-# 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
-# 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 geocode {
- my ($s, $q) = @_;
- my ($x, $y, $easting, $northing, $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 = mapit_check_error($location)) {
- $easting = $location->{easting};
- $northing = $location->{northing};
- my $xx = Page::os_to_tile($easting);
- my $yy = Page::os_to_tile($northing);
- $x = int($xx);
- $y = int($yy);
- $x += 1 if ($xx - $x > 0.5);
- $y += 1 if ($yy - $y > 0.5);
- }
- } else {
- ($x, $y, $easting, $northing, $error) = geocode_string($s, $q);
- }
- return ($x, $y, $easting, $northing, $error);
-}
-
-sub geocoded_string_coordinates {
- my ($js, $q) = @_;
- my ($x, $y, $easting, $northing, $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');
- my $xx = Page::os_to_tile($easting);
- my $yy = Page::os_to_tile($northing);
- $x = int($xx);
- $y = int($yy);
- $x += 1 if ($xx - $x > 0.5);
- $y += 1 if ($yy - $y > 0.5);
- } 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 ($x, $y, $easting, $northing, $error);
-}
-
-# geocode_string STRING QUERY
-# Canonicalises, looks up on Google Maps API, and caches, a user-inputted location.
-# Returns array of (TILE_X, TILE_Y, EASTING, NORTHING, 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 geocode_string {
- my ($s, $q) = @_;
- $s = lc($s);
- $s =~ s/[^-&0-9a-z ']/ /g;
- $s =~ s/\s+/ /g;
- $s = URI::Escape::uri_escape_utf8($s);
- $s = Cobrand::disambiguate_location(get_cobrand($q), "q=$s", $q);
- $s =~ s/%20/+/g;
- 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, $x, $y, $easting, $northing);
- if (-s $cache_file) {
- $js = File::Slurp::read_file($cache_file);
- } else {
- $url .= ',+UK' unless $url =~ /united\++kingdom$/ || $url =~ /uk$/i;
- $url .= '&sensor=false&gl=uk&key=' . mySociety::Config::get('GOOGLE_MAPS_API_KEY');
- $js = LWP::Simple::get($url);
- File::Slurp::write_file($cache_file, $js) if $js && $js !~ /"code":6[12]0/;
- }
- if (!$js) {
- $error = _('Sorry, we could not parse that location. Please try again.');
- } elsif ($js !~ /"code" *: *200/) {
- $error = _('Sorry, we could not find that location.');
- } elsif ($js =~ /}, *{/) { # Multiple
- my @js = split /}, *{/, $js;
- my @valid_locations;
- foreach (@js) {
- next unless /"address" *: *"(.*?)"/s;
- my $address = $1;
- next unless Cobrand::geocoded_string_check(get_cobrand($q), $address, $q);
- next if $address =~ /BT\d/;
- push (@valid_locations, $_);
- push (@$error, $address);
- }
- if (scalar @valid_locations == 1) {
- return geocoded_string_coordinates($valid_locations[0], $q);
- }
- $error = _('Sorry, we could not find that location.') unless $error;
- } elsif ($js =~ /BT\d/) {
- # 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 {
- ($x, $y, $easting, $northing, $error) = geocoded_string_coordinates($js, $q);
- }
- return ($x, $y, $easting, $northing, $error);
-}
-
-# geocode_choice
-# Prints response if there's more than one possible result
-sub geocode_choice {
- my ($choices, $page, $q) = @_;
- my $url;
- my $cobrand = Page::get_cobrand($q);
- my $message = _('We found more than one match for that location. We show up to ten matches, please try a different search if yours is not here.');
- my $out = '<p>' . $message . '</p>';
- my $choice_list = '<ul>';
- foreach my $choice (@$choices) {
- $choice =~ s/, United Kingdom//;
- $choice =~ s/, UK//;
- $url = Cobrand::url($cobrand, NewURL($q, -retain => 1, -url => $page, 'pc' => $choice), $q);
- $url =~ s/%20/+/g;
- $choice_list .= '<li><a href="' . $url . '">' . $choice . "</a></li>\n";
- }
- $choice_list .= '</ul>';
- $out .= $choice_list;
- my %vars = (message => $message,
- choice_list => $choice_list,
- header => _('More than one match'),
- url_home => Cobrand::url($cobrand, '/', $q));
- my $cobrand_choice = Page::template_include('geocode-choice', $q, Page::template_root($q), %vars);
- return $cobrand_choice if $cobrand_choice;
- return $out;
-}
-
sub short_name {
my $name = shift;
# Special case Durham as it's the only place with two councils of the same name
diff --git a/perllib/Standard.pm b/perllib/Standard.pm
index eda2d89fb..571065c14 100644
--- a/perllib/Standard.pm
+++ b/perllib/Standard.pm
@@ -20,7 +20,6 @@ use lib "$FindBin::Bin/../commonlib/perllib";
use Page;
-
package Standard;
sub import {
diff --git a/t/Cobrand.t b/t/Cobrand.t
index 1f3321b0d..f6c2e9c87 100755
--- a/t/Cobrand.t
+++ b/t/Cobrand.t
@@ -11,7 +11,7 @@
use strict;
use warnings;
-use Test::More tests => 62;
+use Test::More tests => 59;
use Test::Exception;
use Error qw(:try);
@@ -213,17 +213,6 @@ sub test_url {
is($url, '/xyz', 'url returns passed url if there is no url function defined by the cobrand');
}
-sub test_show_watermark {
- my $cobrand = 'mysite';
- my $watermark = Cobrand::show_watermark($cobrand);
- is($watermark, 0, 'show_watermark returns output from cobrand module');
-
- $cobrand = 'nosite';
- $watermark = Cobrand::show_watermark($cobrand);
- is($watermark, 1, 'watermark returns 1 if there is no show_watermark function defined by the cobrand');
-
-}
-
sub test_allow_photo_upload {
my $cobrand = 'mysite';
my $photo_upload = Cobrand::allow_photo_upload($cobrand);
@@ -287,7 +276,6 @@ ok(test_root_path_js() == 1, 'Ran all tests for root_js');
ok(test_site_title() == 1, 'Ran all tests for site_title');
ok(test_on_map_list_limit() == 1, 'Ran all tests for on_map_list_limit');
ok(test_url() == 1, 'Ran all tests for url');
-ok(test_show_watermark() == 1, 'Ran all tests for show_watermark');
ok(test_allow_photo_upload() == 1, 'Ran all tests for allow_photo_upload');
ok(test_allow_photo_display() == 1, 'Ran all tests for allow_photo_display');
ok(test_council_check() == 1, 'Ran all tests for council_check');
diff --git a/t/Cobrands/Mysite/Util.pm b/t/Cobrands/Mysite/Util.pm
index 8aedd2c8c..c1a8023c1 100644
--- a/t/Cobrands/Mysite/Util.pm
+++ b/t/Cobrands/Mysite/Util.pm
@@ -87,10 +87,6 @@ sub url {
return '/transformed_url';
}
-sub show_watermark {
- return 0;
-}
-
sub allow_photo_upload {
return 0;
}
diff --git a/t/Page.t b/t/Page.t
index 26962c743..d4ccc9f3d 100755
--- a/t/Page.t
+++ b/t/Page.t
@@ -11,7 +11,7 @@
use strict;
use warnings;
-use Test::More tests => 15;
+use Test::More tests => 13;
use Test::Exception;
use FindBin;
@@ -20,6 +20,7 @@ use lib "$FindBin::Bin/../perllib";
use lib "$FindBin::Bin/../commonlib/perllib";
use Page;
+use FixMyStreet::Geocode;
use mySociety::MockQuery;
use mySociety::Locale;
@@ -40,14 +41,12 @@ sub test_geocode_string() {
my $q = new MockQuery('nosite', \%params);
# geocode a straightforward string, expect success
- my ($x, $y, $easting, $northing, $error) = Page::geocode_string('Buckingham Palace', $q);
- ok($x == 3280, 'example x coordinate generated') or diag("Got $x");
- ok($y == 1114, 'example y coordinate generated') or diag("Got $y");;
- ok($easting == 529044, 'example easting generated') or diag("Got $easting");
- ok($northing == 179619, 'example northing generated') or diag("Got $northing");
+ 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
- ($x, $y, $easting, $northing, $error) = Page::geocode_string('Falls Road, Belfast', $q);
+ ($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");
}
diff --git a/templates/website/cobrands/barnet/barnet-header b/templates/website/cobrands/barnet/barnet-header
index 3474091e6..67619b417 100644
--- a/templates/website/cobrands/barnet/barnet-header
+++ b/templates/website/cobrands/barnet/barnet-header
@@ -51,6 +51,7 @@
<script type="text/javascript" src="/cobrands/barnet/javascript/2008-portsurf.js"></script>
<script type="text/javascript" src="/yui/utilities.js"></script>
<script type="text/javascript" src="/js.js"></script>
+{{ $map_js }}
{{ $rss }}
diff --git a/templates/website/cobrands/emptyhomes/emptyhomes-header b/templates/website/cobrands/emptyhomes/emptyhomes-header
index 09a96b581..dfdefa8cc 100644
--- a/templates/website/cobrands/emptyhomes/emptyhomes-header
+++ b/templates/website/cobrands/emptyhomes/emptyhomes-header
@@ -5,6 +5,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<script type="text/javascript" src="/yui/utilities.js"></script>
<script type="text/javascript" src="/js.js"></script>
+{{ $map_js }}
<style type="text/css">
@import "/css/core.css";
@import "/css/cobrands/emptyhomes/emptyhomes.css";
diff --git a/templates/website/header b/templates/website/header
index 44f51ca55..028d05506 100644
--- a/templates/website/header
+++ b/templates/website/header
@@ -4,6 +4,9 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="/yui/utilities.js"></script>
<script type="text/javascript" src="/js.js"></script>
+ {{ $map_js }}
+ {{ $robots }}
+
<title>{{ $title }}{{ $site_title }}</title>
<style type="text/css">@import url("/css/core.css"); @import url("/css/main.css");</style>
<!--[if LT IE 7]>
diff --git a/web/ajax.cgi b/web/ajax.cgi
index fff437846..fa722f02e 100755
--- a/web/ajax.cgi
+++ b/web/ajax.cgi
@@ -35,7 +35,7 @@ sub main {
unless ($input{all_pins}) {
$interval = '6 months';
}
- my ($pins, $on_map, $around_map, $dist) = Page::map_pins($q, $x, $y, $sx, $sy, $interval);
+ my ($pins, $on_map, $around_map, $dist) = FixMyStreet::Map::map_pins($q, $x, $y, $sx, $sy, $interval);
my $cobrand = Page::get_cobrand($q);
my $list = '';
my $link = '';
diff --git a/web/alert.cgi b/web/alert.cgi
index 108a02683..71249759e 100755
--- a/web/alert.cgi
+++ b/web/alert.cgi
@@ -13,7 +13,8 @@ use Standard;
use Digest::SHA1 qw(sha1_hex);
use Error qw(:try);
use CrossSell;
-use mySociety::Alert;
+use FixMyStreet::Alert;
+use FixMyStreet::Geocode;
use mySociety::AuthToken;
use mySociety::Config;
use mySociety::DBHandle qw(select_all);
@@ -55,7 +56,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('x') && $q->param('y'))) {
+ } elsif ($q->param('pc') || ($q->param('e') && $q->param('n'))) {
$title = _('Local RSS feeds and email alerts');
$out = alert_list($q);
} else {
@@ -63,7 +64,7 @@ EOF
$out = alert_front_page($q);
}
- print Page::header($q, title => $title);
+ print Page::header($q, title => $title, robots => 'noindex,nofollow');
print $out;
print Page::footer($q);
}
@@ -71,25 +72,22 @@ Page::do_fastcgi(\&main);
sub alert_list {
my ($q, @errors) = @_;
- my @vars = qw(pc rznvy x y);
+ my @vars = qw(pc rznvy e n);
my %input = map { $_ => scalar $q->param($_) } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
my($error, $e, $n);
- my $x = $input{x}; my $y = $input{y};
- $x ||= 0; $x += 0;
- $y ||= 0; $y += 0;
- if ($x || $y) {
- $e = Page::tile_to_os($input{x});
- $n = Page::tile_to_os($input{y});
+ if ($input{e} || $input{n}) {
+ $e = $input{e};
+ $n = $input{n};
} else {
try {
- ($x, $y, $e, $n, $error) = Page::geocode($input{pc}, $q);
+ ($e, $n, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
} catch Error::Simple with {
$error = shift;
};
}
- return Page::geocode_choice($error, '/alert', $q) if ref($error) eq 'ARRAY';
+ return FixMyStreet::Geocode::list_choices($error, '/alert', $q) if ref($error) eq 'ARRAY';
return alert_front_page($q, $error) if $error;
my $pretty_pc = $input_h{pc};
@@ -273,8 +271,8 @@ EOF
rss_feed_5k => $rss_feed_5k,
rss_feed_10k => $rss_feed_10k,
rss_feed_20k => $rss_feed_20k,
- x => $x,
- y => $y,
+ e => $e,
+ n => $n,
options => $options );
my $cobrand_page = Page::template_include('alert-options', $q, Page::template_root($q), %vars);
$out = $cobrand_page if ($cobrand_page);
@@ -446,8 +444,8 @@ sub alert_signed_input {
my $out;
my $cobrand = Page::get_cobrand($q);
if ($signed_email eq sha1_hex("$id-$email-$salt-$secret")) {
- my $alert_id = mySociety::Alert::create($email, 'new_updates', $cobrand, '', $id);
- mySociety::Alert::confirm($alert_id);
+ my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, '', $id);
+ FixMyStreet::Alert::confirm($alert_id);
$out = $q->p(_('You have successfully subscribed to that alert.'));
my $cobrand = Page::get_cobrand($q);
my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
@@ -476,14 +474,14 @@ sub alert_token {
my $message;
my $display_advert = Cobrand::allow_crosssell_adverts($cobrand);
if ($type eq 'subscribe') {
- mySociety::Alert::confirm($id);
+ FixMyStreet::Alert::confirm($id);
$message = _('You have successfully confirmed your alert.');
$out = $q->p($message);
if ($display_advert) {
$out .= CrossSell::display_advert($q, $email);
}
} elsif ($type eq 'unsubscribe') {
- mySociety::Alert::delete($id);
+ FixMyStreet::Alert::delete($id);
$message = _('You have successfully deleted your alert.');
$out = $q->p($message);
if ($display_advert) {
@@ -517,22 +515,22 @@ sub alert_do_subscribe {
my $cobrand_data = Cobrand::extra_alert_data($cobrand, $q);
if ($type eq 'updates') {
my $id = $q->param('id');
- $alert_id = mySociety::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
+ $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
} elsif ($type eq 'problems') {
- $alert_id = mySociety::Alert::create($email, 'new_problems', $cobrand, $cobrand_data);
+ $alert_id = FixMyStreet::Alert::create($email, 'new_problems', $cobrand, $cobrand_data);
} elsif ($type eq 'local') {
my $feed = $q->param('feed');
if ($feed =~ /^area:(?:\d+:)?(\d+)/) {
- $alert_id = mySociety::Alert::create($email, 'area_problems', $cobrand, $cobrand_data, $1);
+ $alert_id = FixMyStreet::Alert::create($email, 'area_problems', $cobrand, $cobrand_data, $1);
} elsif ($feed =~ /^council:(\d+)/) {
- $alert_id = mySociety::Alert::create($email, 'council_problems', $cobrand, $cobrand_data, $1, $1);
+ $alert_id = FixMyStreet::Alert::create($email, 'council_problems', $cobrand, $cobrand_data, $1, $1);
} elsif ($feed =~ /^ward:(\d+):(\d+)/) {
- $alert_id = mySociety::Alert::create($email, 'ward_problems', $cobrand, $cobrand_data, $1, $2);
+ $alert_id = FixMyStreet::Alert::create($email, 'ward_problems', $cobrand, $cobrand_data, $1, $2);
} elsif ($feed =~ /^local:(\d+):(\d+)/) {
- $alert_id = mySociety::Alert::create($email, 'local_problems', $cobrand, $cobrand_data, $1, $2);
+ $alert_id = FixMyStreet::Alert::create($email, 'local_problems', $cobrand, $cobrand_data, $1, $2);
}
} else {
- throw mySociety::Alert::Error('Invalid type');
+ throw FixMyStreet::Alert::Error('Invalid type');
}
my %h = ();
diff --git a/web/confirm.cgi b/web/confirm.cgi
index cd874e9f0..daa260d65 100755
--- a/web/confirm.cgi
+++ b/web/confirm.cgi
@@ -12,7 +12,7 @@ use strict;
use Standard;
use Digest::SHA1 qw(sha1_hex);
use CrossSell;
-use mySociety::Alert;
+use FixMyStreet::Alert;
use mySociety::AuthToken;
use mySociety::Random qw(random_bytes);
@@ -115,8 +115,8 @@ sub confirm_update {
# Subscribe updater to email updates if requested
if ($add_alert) {
- my $alert_id = mySociety::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $problem_id);
- mySociety::Alert::confirm($alert_id);
+ my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $problem_id);
+ FixMyStreet::Alert::confirm($alert_id);
}
return $out;
@@ -182,8 +182,8 @@ $q->p('<a href="/report/' . $id . '">' . _('View your report') . '</a>.');
}
# Subscribe problem reporter to email updates
- my $alert_id = mySociety::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
- mySociety::Alert::confirm($alert_id);
+ my $alert_id = FixMyStreet::Alert::create($email, 'new_updates', $cobrand, $cobrand_data, $id);
+ FixMyStreet::Alert::confirm($alert_id);
return $out;
}
diff --git a/web/contact.cgi b/web/contact.cgi
index 0b1a27c9f..fd0043d94 100755
--- a/web/contact.cgi
+++ b/web/contact.cgi
@@ -19,7 +19,7 @@ use mySociety::Random qw(random_bytes);
# Main code for index.cgi
sub main {
my $q = shift;
- print Page::header($q, title=>_('Contact Us'), context=>'contact');
+ print Page::header($q, title=>_('Contact Us'), context=>'contact', robots => 'noindex,nofollow');
my $out = '';
if ($q->param('submit_form')) {
$out = contact_submit($q);
diff --git a/web/import.cgi b/web/import.cgi
index f839bd5f5..2aede1697 100755
--- a/web/import.cgi
+++ b/web/import.cgi
@@ -11,6 +11,7 @@
use strict;
use Error qw(:try);
use Standard;
+use Utils;
use mySociety::AuthToken;
use mySociety::EmailUtil;
use mySociety::EvEl;
diff --git a/web/index.cgi b/web/index.cgi
index dcbec5a1c..83b3b421c 100755
--- a/web/index.cgi
+++ b/web/index.cgi
@@ -8,6 +8,7 @@
use strict;
use Standard;
+use Utils;
use Error qw(:try);
use File::Slurp;
use LWP::Simple;
@@ -16,6 +17,7 @@ use CGI::Carp;
use URI::Escape;
use CrossSell;
+use FixMyStreet::Geocode;
use mySociety::AuthToken;
use mySociety::Config;
use mySociety::DBHandle qw(select_all);
@@ -80,7 +82,7 @@ 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'))) {
+ } elsif ($q->param('pc') || ($q->param('x') && $q->param('y')) || ($q->param('e') && $q->param('n'))) {
($out, %params) = display_location($q);
$params{title} = _('Viewing a location');
} else {
@@ -457,50 +459,34 @@ sub display_form {
return display_location($q, @errors)
unless ($pin_x && $pin_y)
|| ($input{easting} && $input{northing})
- || ($input{skipped} && $input{x} && $input{y})
|| ($input{skipped} && $input{pc})
|| ($input{partial} && $input{pc});
# Work out some co-ordinates from whatever we've got
- my ($px, $py, $easting, $northing);
+ my ($easting, $northing);
if ($input{skipped}) {
# Map is being skipped
- if ($input{x} && $input{y}) {
- $easting = Page::tile_to_os($input{x});
- $northing = Page::tile_to_os($input{y});
+ if ($input{easting} && $input{northing}) {
+ $easting = $input{easting};
+ $northing = $input{northing};
} else {
- my ($x, $y, $e, $n, $error) = Page::geocode($input{pc}, $q);
+ my ($e, $n, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
$easting = $e; $northing = $n;
}
} elsif ($pin_x && $pin_y) {
- # Map was clicked on
- $pin_x = Page::click_to_tile($pin_tile_x, $pin_x);
- $pin_y = Page::click_to_tile($pin_tile_y, $pin_y, 1);
- $input{x} ||= int($pin_x);
- $input{y} ||= int($pin_y);
- $px = Page::tile_to_px($pin_x, $input{x});
- $py = Page::tile_to_px($pin_y, $input{y}, 1);
- $easting = Page::tile_to_os($pin_x);
- $northing = Page::tile_to_os($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}) {
- my ($x, $y, $error);
+ my $error;
try {
- ($x, $y, $easting, $northing, $error) = Page::geocode($input{pc}, $q);
+ ($easting, $northing, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
} catch Error::Simple with {
$error = shift;
};
- return Page::geocode_choice($error, '/', $q) if ref($error) eq 'ARRAY';
+ return FixMyStreet::Geocode::list_choices($error, '/', $q) if ref($error) eq 'ARRAY';
return front_page($q, $error) if $error;
- $input{x} = int(Page::os_to_tile($easting));
- $input{y} = int(Page::os_to_tile($northing));
- $px = Page::os_to_px($easting, $input{x});
- $py = Page::os_to_px($northing, $input{y}, 1);
} else {
# Normal form submission
- my ($x, $y, $tile_x, $tile_y);
- ($x, $y, $tile_x, $tile_y, $px, $py) = Page::os_to_px_with_adjust($q, $input{easting}, $input{northing}, undef, undef);
- $input{x} = $tile_x;
- $input{y} = $tile_y;
$easting = $input_h{easting};
$northing = $input_h{northing};
}
@@ -589,22 +575,22 @@ please specify the closest point on land.')) unless %$all_councils;
$vars{form_start} = <<EOF;
<form action="$form_action" method="post" name="mapSkippedForm"$enctype>
<input type="hidden" name="pc" value="$input_h{pc}">
-<input type="hidden" name="x" value="$input_h{x}">
-<input type="hidden" name="y" value="$input_h{y}">
<input type="hidden" name="skipped" value="1">
$cobrand_form_elements
<div id="skipped-map">
EOF
} else {
- my $pins = Page::display_pin($q, $px, $py, 'purple');
my $type;
if ($allow_photo_upload) {
$type = 2;
} else {
$type = 1;
}
- $vars{form_start} = Page::display_map($q, x => $input{x}, 'y' => $input{y}, type => $type,
- pins => $pins, px => $px, py => $py );
+ $vars{form_start} = FixMyStreet::Map::display_map($q,
+ easting => $easting, northing => $northing,
+ type => $type,
+ pins => [ [ $easting, $northing, 'purple' ] ],
+ );
my $partial_id;
if (my $token = $input{partial}) {
$partial_id = mySociety::AuthToken::retrieve('partial', $token);
@@ -770,41 +756,43 @@ EOF
%vars = (%vars,
category => $category,
- map_end => Page::display_map_end(1),
+ map_end => FixMyStreet::Map::display_map_end(1),
url_home => Cobrand::url($cobrand, '/', $q),
submit_button => _('Submit')
);
- return (Page::template_include('report-form', $q, Page::template_root($q), %vars));
+ return (Page::template_include('report-form', $q, Page::template_root($q), %vars), robots => 'noindex,nofollow');
}
sub display_location {
my ($q, @errors) = @_;
my $cobrand = Page::get_cobrand($q);
- my @vars = qw(pc x y all_pins no_pins);
+ 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;
- if ($input{y} =~ /favicon/) {
- my $base = mySociety::Config::get('BASE_URL');
- print $q->redirect(-location => $base . '/favicon.ico', -status => 301);
- return '';
- }
- my($error, $easting, $northing);
+
+ (my $easting) = $input{e} =~ /^(\d+)/; $easting ||= 0;
+ (my $northing) = $input{n} =~ /^(\d+)/; $northing ||= 0;
+
+ # 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};
- if (!$x && !$y) {
+ return front_page($q, @errors) unless $x || $y || $input{pc} || $easting || $northing;
+
+ 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) {
+ # Don't need to do anything
+ } else {
+ my $error;
try {
- ($x, $y, $easting, $northing, $error) = Page::geocode($input{pc}, $q);
+ ($easting, $northing, $error) = FixMyStreet::Geocode::lookup($input{pc}, $q);
} catch Error::Simple with {
$error = shift;
};
- }
- return Page::geocode_choice($error, '/', $q) if (ref($error) eq 'ARRAY');
- return front_page($q, $error) if ($error);
-
- if (!$easting || !$northing) {
- $easting = Page::tile_to_os($x);
- $northing = Page::tile_to_os($y);
+ 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
@@ -821,16 +809,12 @@ sub display_location {
$all_text = _('Include stale reports');
$interval = '6 months';
}
- my ($pins, $on_map, $around_map, $dist) = Page::map_pins($q, $x, $y, $x, $y, $interval);
- if ($input{no_pins}) {
- $hide_link = NewURL($q, -retain=>1, no_pins=>undef);
- $hide_text = _('Show pins');
- $pins = '';
- } else {
- $hide_link = NewURL($q, -retain=>1, no_pins=>1);
- $hide_text = _('Hide pins');
+
+ my ($on_map_all, $on_map, $around_map, $dist) = FixMyStreet::Map::map_features($q, $easting, $northing, $interval);
+ my @pins;
+ foreach (@$on_map_all) {
+ push @pins, [ $_->{easting}, $_->{northing}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
}
- my $map_links = "<p id='sub_map_links'><a id='hide_pins_link' rel='nofollow' href='$hide_link'>$hide_text</a> | <a id='all_pins_link' rel='nofollow' href='$all_link'>$all_text</a></p> <input type='hidden' id='all_pins' name='all_pins' value='$input_h{all_pins}'>";
my $on_list = '';
foreach (@$on_map) {
my $report_url = NewURL($q, -retain => 1, -url => '/report/' . $_->{id}, pc => undef, x => undef, 'y' => undef);
@@ -854,18 +838,38 @@ sub display_location {
$around_list .= '</a>';
$around_list .= ' <small>' . _('(fixed)') . '</small>' if $_->{state} eq 'fixed';
$around_list .= '</li>';
+ push @pins, [ $_->{easting}, $_->{northing}, $_->{state} eq 'fixed' ? 'green' : 'red' ];
}
$around_list = $q->li(_('No problems found.'))
unless $around_list;
- my $url_skip = NewURL($q, -retain=>1, 'submit_map'=>1, skipped=>1);
+ if ($input{no_pins}) {
+ $hide_link = NewURL($q, -retain=>1, no_pins=>undef);
+ $hide_text = _('Show pins');
+ @pins = ();
+ } else {
+ $hide_link = NewURL($q, -retain=>1, no_pins=>1);
+ $hide_text = _('Hide pins');
+ }
+ my $map_links = "<p id='sub_map_links'><a id='hide_pins_link' rel='nofollow' href='$hide_link'>$hide_text</a> | <a id='all_pins_link' rel='nofollow' href='$all_link'>$all_text</a></p> <input type='hidden' id='all_pins' name='all_pins' value='$input_h{all_pins}'>";
+
+ my $url_skip = NewURL($q, -retain=>1, pc => undef,
+ x => undef, 'y' => undef,
+ easting => $easting, northing => $northing,
+ 'submit_map'=>1, skipped=>1
+ );
my $pc_h = ent($q->param('pc') || '');
my %vars = (
- 'map' => Page::display_map($q, x => $x, 'y' => $y, type => 1, pins => $pins, post => $map_links ),
- map_end => Page::display_map_end(1),
+ 'map' => FixMyStreet::Map::display_map($q,
+ easting => $easting, northing => $northing,
+ 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', x=>$x, 'y'=>$y, feed=>"local:$easting:$northing"), $q),
+ url_email => Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, -url=>'/alert', e=>$easting, 'n'=>$northing, feed=>"local:$easting:$northing"), $q),
url_skip => $url_skip,
email_me => _('Email me new local problems'),
rss_alt => _('RSS feed'),
@@ -885,7 +889,8 @@ sub display_location {
);
my %params = (
- rss => [ _('Recent local problems, FixMyStreet'), "/rss/n/$easting,$northing" ]
+ rss => [ _('Recent local problems, FixMyStreet'), "/rss/n/$easting,$northing" ],
+ robots => 'noindex,nofollow',
);
return (Page::template_include('map', $q, Page::template_root($q), %vars), %params);
@@ -898,11 +903,9 @@ sub display_problem {
my $cobrand = Page::get_cobrand($q);
push @errors, _('There were problems with your update. Please see below.') if (scalar keys %field_errors);
- my @vars = qw(id name rznvy update fixed add_alert upload_fileid x y submit_update);
+ my @vars = qw(id name rznvy update fixed add_alert upload_fileid submit_update);
my %input = map { $_ => $q->param($_) || '' } @vars;
my %input_h = map { $_ => $q->param($_) ? ent($q->param($_)) : '' } @vars;
- ($input{x}) = $input{x} =~ /^(\d+)/; $input{x} ||= 0;
- ($input{y}) = $input{y} =~ /^(\d+)/; $input{y} ||= 0;
my $base = Cobrand::base_url($cobrand);
# Some council with bad email software
@@ -923,62 +926,70 @@ sub display_problem {
my $problem = Problems::fetch_problem($input{id});
return display_location($q, _('Unknown problem ID')) unless $problem;
return front_page($q, _('That report has been removed from FixMyStreet.'), '410 Gone') if $problem->{state} eq 'hidden';
- my ($x, $y, $x_tile, $y_tile, $px, $py) = Page::os_to_px_with_adjust($q, $problem->{easting}, $problem->{northing}, $input{x}, $input{y});
-
- # Try and have pin near centre of map
- if (!$input{x} && $x - $x_tile > 0.5) {
- $x_tile += 1;
- $px = Page::os_to_px($problem->{easting}, $x_tile);
- }
- if (!$input{y} && $y - $y_tile > 0.5) {
- $y_tile += 1;
- $py = Page::os_to_px($problem->{northing}, $y_tile, 1);
- }
- my %vars;
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 $pins = Page::display_pin($q, $px, $py, 'blue');
- $vars{map_start} = Page::display_map($q, x => $x_tile, 'y' => $y_tile, type => 0,
- pins => $pins, px => $px, py => $py, post => $map_links );
+ 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 $banner;
if ($q->{site} ne 'emptyhomes' && $problem->{state} eq 'confirmed' && $problem->{duration} > 8*7*24*60*60) {
- $vars{banner} = $q->p({id => 'unknown'}, _('This problem is old and of unknown status.'))
+ $banner = $q->p({id => 'unknown'}, _('This problem is old and of unknown status.'));
}
if ($problem->{state} eq 'fixed') {
- $vars{banner} = $q->p({id => 'fixed'}, _('This problem has been fixed') . '.')
+ $banner = $q->p({id => 'fixed'}, _('This problem has been fixed') . '.');
}
- $vars{problem_title} = ent($problem->{title});
- $vars{problem_meta} = Page::display_problem_meta_line($q, $problem);
- $vars{problem_detail} = Page::display_problem_detail($problem);
- $vars{problem_photo} = Page::display_problem_photo($q, $problem);
-
- my $contact_url = Cobrand::url($cobrand, NewURL($q, -retain => 1, pc => undef, -url=>'/contact?id=' . $input{id}), $q);
- $vars{unsuitable} = $q->a({rel => 'nofollow', href => $contact_url}, _('Offensive? Unsuitable? Tell us'));
-
- my $back = Cobrand::url($cobrand, NewURL($q, -url => '/', 'x' => $x_tile, 'y' => $y_tile, -retain => 1, pc => undef, id => undef ), $q);
- $vars{more_problems} = '<a href="' . $back . '">' . _('More problems nearby') . '</a>';
+ 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}),
+ -retain => 1, pc => undef, x => undef, 'y' => undef, id => undef
+ ), $q);
+ my $fixed = ($input{fixed}) ? ' checked' : '';
- $vars{url_home} = Cobrand::url($cobrand, '/', $q),
+ my %vars = (
+ banner => $banner,
+ map_start => FixMyStreet::Map::display_map($q,
+ easting => $problem->{easting}, northing => $problem->{northing},
+ type => 0,
+ pins => [ [ $problem->{easting}, $problem->{northing}, 'blue' ] ],
+ post => $map_links
+ ),
+ map_end => FixMyStreet::Map::display_map_end(0),
+ problem_title => ent($problem->{title}),
+ problem_meta => Page::display_problem_meta_line($q, $problem),
+ problem_detail => Page::display_problem_detail($problem),
+ problem_photo => Page::display_problem_photo($q, $problem),
+ problem_updates => Page::display_problem_updates($input{id}, $q),
+ unsuitable => $q->a({rel => 'nofollow', href => $contact_url}, _('Offensive? Unsuitable? Tell us')),
+ more_problems => '<a href="' . $back . '">' . _('More problems nearby') . '</a>',
+ url_home => Cobrand::url($cobrand, '/', $q),
+ alert_link => Cobrand::url($cobrand, NewURL($q, -url => '/alert?type=updates;id='.$input_h{id}, -retain => 1, pc => undef, x => undef, 'y' => undef ), $q),
+ alert_text => _('Email me updates'),
+ email_label => _('Email:'),
+ subscribe => _('Subscribe'),
+ blurb => _('Receive email when updates are left on this problem'),
+ cobrand_form_elements1 => Cobrand::form_elements($cobrand, 'alerts', $q),
+ form_alert_action => Cobrand::url($cobrand, '/alert', $q),
+ rss_url => Cobrand::url($cobrand, NewURL($q, -retain=>1, -url => '/rss/'.$input_h{id}, pc => undef, x => undef, 'y' => undef, id => undef), $q),
+ rss_title => _('RSS feed'),
+ rss_alt => _('RSS feed of updates to this problem'),
+ update_heading => $q->h2(_('Provide an update')),
+ field_errors => \%field_errors,
+ add_alert_checked => ($input{add_alert} || !$input{submit_update}) ? ' checked' : '',
+ fixedline_box => $problem->{state} eq 'fixed' ? '' : qq{<input type="checkbox" name="fixed" id="form_fixed" value="1"$fixed>},
+ fixedline_label => $problem->{state} eq 'fixed' ? '' : qq{<label for="form_fixed">} . _('This problem has been fixed') . qq{</label>},
+ name_label => _('Name:'),
+ update_label => _('Update:'),
+ alert_label => _('Alert me to future updates'),
+ post_label => _('Post'),
+ cobrand_form_elements => Cobrand::form_elements($cobrand, 'updateForm', $q),
+ form_action => Cobrand::url($cobrand, '/', $q),
+ input_h => \%input_h,
+ );
- $vars{alert_link} = Cobrand::url($cobrand, NewURL($q, -url => '/alert?type=updates;id='.$input_h{id}, -retain => 1, pc => undef ), $q);
- $vars{alert_text} = _('Email me updates');
- $vars{email_label} = _('Email:');
- $vars{subscribe} = _('Subscribe');
- $vars{blurb} = _('Receive email when updates are left on this problem');
- $vars{cobrand_form_elements1} = Cobrand::form_elements($cobrand, 'alerts', $q);
- $vars{form_alert_action} = Cobrand::url($cobrand, '/alert', $q);
- $vars{rss_url} = Cobrand::url($cobrand, NewURL($q, -retain=>1, -url => '/rss/'.$input_h{id}, pc => undef, id => undef), $q);
- $vars{rss_title} = _('RSS feed');
- $vars{rss_alt} = _('RSS feed of updates to this problem');
-
- $vars{problem_updates} = Page::display_problem_updates($input{id}, $q);
- $vars{update_heading} = $q->h2(_('Provide an update'));
$vars{update_blurb} = $q->p($q->small(_('Please note that updates are not sent to the council. If you leave your name it will be public. Your information will only be used in accordance with our <a href="/faq#privacy">privacy policy</a>')))
unless $q->{site} eq 'emptyhomes'; # No council blurb
@@ -986,22 +997,10 @@ sub display_problem {
$vars{errors} = '<ul class="error"><li>' . join('</li><li>', @errors) . '</li></ul>';
}
- $vars{field_errors} = \%field_errors;
-
- my $fixed = ($input{fixed}) ? ' checked' : '';
- $vars{add_alert_checked} = ($input{add_alert} || !$input{submit_update}) ? ' checked' : '';
- $vars{fixedline_box} = $problem->{state} eq 'fixed' ? ''
- : qq{<input type="checkbox" name="fixed" id="form_fixed" value="1"$fixed>};
- $vars{fixedline_label} = $problem->{state} eq 'fixed' ? ''
- : qq{<label for="form_fixed">} . _('This problem has been fixed') . qq{</label>};
- $vars{name_label} = _('Name:');
- $vars{update_label} = _('Update:');
- $vars{alert_label} = _('Alert me to future updates');
- $vars{post_label} = _('Post');
- $vars{cobrand_form_elements} = Cobrand::form_elements($cobrand, 'updateForm', $q);
my $allow_photo_upload = Cobrand::allow_photo_upload($cobrand);
if ($allow_photo_upload) {
my $photo_label = _('Photo:');
+ $vars{enctype} = ' enctype="multipart/form-data"';
$vars{photo_element} = <<EOF;
<div id="fileupload_normalUI">
<label for="form_photo">$photo_label</label>
@@ -1010,17 +1009,12 @@ sub display_problem {
EOF
}
- $vars{form_action} = Cobrand::url($cobrand, '/', $q);
- if ($allow_photo_upload) {
- $vars{enctype} = ' enctype="multipart/form-data"';
- }
- $vars{map_end} = Page::display_map_end(0);
my %params = (
rss => [ _('Updates to this problem, FixMyStreet'), "/rss/$input_h{id}" ],
+ robots => 'index, nofollow',
title => $problem->{title}
);
- $vars{input_h} = \%input_h;
my $page = Page::template_include('problem', $q, Page::template_root($q), %vars);
return ($page, %params);
}
diff --git a/web/js.js b/web/js.js
index ffe6b0850..b2012fc84 100644
--- a/web/js.js
+++ b/web/js.js
@@ -1,12 +1,6 @@
/*
* js.js
* FixMyStreet JavaScript
- *
- * TODO
- * Investigate jQuery
- * Tidy it all up
- * Selection of pin doesn't really need a server request, but I don't really care
- *
*/
@@ -16,41 +10,6 @@ YAHOO.util.Event.onContentReady('pc', function() {
}
});
-function compass_pan(e, a) {
- YAHOO.util.Event.preventDefault(e);
- if (a.home) {
- a.x = a.orig_x-drag_x;
- a.y = a.orig_y-drag_y;
- }
- pan(a.x, a.y);
-}
-
-YAHOO.util.Event.onContentReady('compass', function() {
- var ua=navigator.userAgent.toLowerCase();
- // if (document.getElementById('mapForm') && (/safari/.test(ua) || /Konqueror/.test(ua))) return;
- if (document.getElementById('map').offsetWidth > 510) return;
-
- var points = this.getElementsByTagName('a');
- YAHOO.util.Event.addListener(points[1], 'click', compass_pan, { x:0, y:fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[3], 'click', compass_pan, { x:fixmystreet.tilewidth, y:0 });
- YAHOO.util.Event.addListener(points[5], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:0 });
- YAHOO.util.Event.addListener(points[7], 'click', compass_pan, { x:0, y:-fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[0], 'click', compass_pan, { x:fixmystreet.tilewidth, y:fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[2], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[6], 'click', compass_pan, { x:fixmystreet.tilewidth, y:-fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[8], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:-fixmystreet.tileheight });
- YAHOO.util.Event.addListener(points[4], 'click', compass_pan, { home:1, orig_x:drag_x, orig_y:drag_y });
-});
-
-YAHOO.util.Event.onContentReady('map', function() {
- var ua=navigator.userAgent.toLowerCase();
- // if (document.getElementById('mapForm') && (/safari/.test(ua) || /Konqueror/.test(ua))) return;
- if (document.getElementById('map').offsetWidth > 510) return;
- new YAHOO.util.DDMap('map');
- update_tiles(fixmystreet.start_x, fixmystreet.start_y, true);
-});
-
-
YAHOO.util.Event.onContentReady('mapForm', function() {
this.onsubmit = function() {
if (this.submit_problem) {
@@ -119,52 +78,6 @@ YAHOO.util.Event.onContentReady('email_alert_box', function() {
});
});
-YAHOO.util.Event.addListener('hide_pins_link', 'click', function(e) {
- YAHOO.util.Event.preventDefault(e);
- if (this.innerHTML == 'Show pins') {
- YAHOO.util.Dom.setStyle('pins', 'display', 'block');
- this.innerHTML = 'Hide pins';
- } else if (this.innerHTML == 'Dangos pinnau') {
- YAHOO.util.Dom.setStyle('pins', 'display', 'block');
- this.innerHTML = 'Cuddio pinnau';
- } else if (this.innerHTML == 'Cuddio pinnau') {
- YAHOO.util.Dom.setStyle('pins', 'display', 'none');
- this.innerHTML = 'Dangos pinnau';
- } else if (this.innerHTML == 'Hide pins') {
- YAHOO.util.Dom.setStyle('pins', 'display', 'none');
- this.innerHTML = 'Show pins';
- }
-});
-YAHOO.util.Event.addListener('all_pins_link', 'click', function(e) {
- YAHOO.util.Event.preventDefault(e);
- YAHOO.util.Dom.setStyle('pins', 'display', 'block');
- var welsh = 0;
- if (this.innerHTML == 'Include stale reports') {
- this.innerHTML = 'Hide stale reports';
- document.getElementById('all_pins').value = '1';
- load_pins(fixmystreet.x, fixmystreet.y);
- } else if (this.innerHTML == 'Cynnwys hen adroddiadau') {
- this.innerHTML = 'Cuddio hen adroddiadau';
- document.getElementById('all_pins').value = '1';
- welsh = 1;
- load_pins(fixmystreet.x, fixmystreet.y);
- } else if (this.innerHTML == 'Cuddio hen adroddiadau') {
- this.innerHTML = 'Cynnwys hen adroddiadau';
- welsh = 1;
- document.getElementById('all_pins').value = '';
- load_pins(fixmystreet.x, fixmystreet.y);
- } else if (this.innerHTML == 'Hide stale reports') {
- this.innerHTML = 'Include stale reports';
- document.getElementById('all_pins').value = '';
- load_pins(fixmystreet.x, fixmystreet.y);
- }
- if (welsh) {
- document.getElementById('hide_pins_link').innerHTML = 'Cuddio pinnau';
- } else {
- document.getElementById('hide_pins_link').innerHTML = 'Hide pins';
- }
-});
-
/* File upload */
/*
function doSubmit(e) {
@@ -250,238 +163,3 @@ var swfu_settings = {
}
};
*/
-
-// I love the global
-var tile_x = 0;
-var tile_y = 0;
-
-var myAnim;
-function pan(x, y) {
- if (!myAnim || !myAnim.isAnimated()) {
- myAnim = new YAHOO.util.Motion('drag', { points:{by:[x,y]} }, 10, YAHOO.util.Easing.easeOut);
- myAnim.useSeconds = false;
- //myAnim.onTween.subscribe(function(){ update_tiles(x/10, y/10, false); });
- myAnim.onComplete.subscribe(function(){
- update_tiles(x, y, false);
- cleanCache();
- });
- myAnim.animate();
- }
-}
-
-var drag_x = 0;
-var drag_y = 0;
-function update_tiles(dx, dy, force) {
- dx = getInt(dx); dy = getInt(dy);
- if (!dx && !dy && !force) return;
- var old_drag_x = drag_x;
- var old_drag_y = drag_y;
- drag_x += dx;
- drag_y += dy;
-
- var drag = document.getElementById('drag');
- drag.style.left = drag_x + 'px';
- drag.style.top = drag_y + 'px';
-
- var horizontal = Math.floor(old_drag_x/fixmystreet.tilewidth) - Math.floor(drag_x/fixmystreet.tilewidth);
- var vertical = Math.floor(old_drag_y/fixmystreet.tileheight) - Math.floor(drag_y/fixmystreet.tileheight);
- if (!horizontal && !vertical && !force) return;
- fixmystreet.x += horizontal;
-
- tile_x += horizontal;
- fixmystreet.y -= vertical;
- tile_y += vertical;
- var url = [ root_path + '/tilma/tileserver/' + fixmystreet.tile_type + '/', fixmystreet.x, '-', (fixmystreet.x+5), ',', fixmystreet.y, '-', (fixmystreet.y+5), '/JSON' ].join('');
- YAHOO.util.Connect.asyncRequest('GET', url, {
- success: urls_loaded, failure: urls_not_loaded,
- argument: [tile_x, tile_y]
- });
-
- if (force) return;
- load_pins(fixmystreet.x, fixmystreet.y);
-}
-
-function load_pins(x, y) {
- if (document.getElementById('formX')) {
- all_pins = '';
- if (document.getElementById('all_pins')) {
- all_pins = document.getElementById('all_pins').value;
- }
- var ajax_params = [ 'sx=' + document.getElementById('formX').value,
- 'sy=' + document.getElementById('formY').value,
- 'x=' + (x+3),
- 'y=' + (y+3),
- 'all_pins=' + all_pins ];
-
- if (document.getElementById('extra_param')) {
- ajax_params.push(document.getElementById('extra_param').name + '=' + document.getElementById('extra_param').value);
- }
-
- var url = [ root_path , '/ajax?', ajax_params.join(';')].join('');
- YAHOO.util.Connect.asyncRequest('GET', url, {
- success: pins_loaded
- });
- }
-}
-
-function pins_loaded(o) {
- var data = eval(o.responseText);
- document.getElementById('pins').innerHTML = data.pins;
- if (typeof(data.current) != 'undefined')
- document.getElementById('current').innerHTML = data.current;
- if (typeof(data.current_near) != 'undefined')
- document.getElementById('current_near').innerHTML = data.current_near;
- if (typeof(data.fixed_near) != 'undefined')
- document.getElementById('fixed_near').innerHTML = data.fixed_near;
-}
-
-function urls_not_loaded(o) { /* Nothing yet */ }
-
-// Load 6x6 grid of tiles around current 2x2
-function urls_loaded(o) {
- var tiles = eval(o.responseText);
- var drag = document.getElementById('drag');
- for (var i=0; i<6; i++) {
- var ii = (i + o.argument[1]);
- for (var j=0; j<6; j++) {
- if (tiles[i][j] == null) continue;
- var jj = (j + o.argument[0]);
- var id = [ 't', ii, '.', jj ].join('');
- var xx = fixmystreet.x+j;
- var yy = fixmystreet.y+5-i;
- var img = document.getElementById(id);
- if (img) {
- if (!img.galleryimg) { img.galleryimg = false; }
- img.onclick = drag_check;
- tileCache[id] = { x: xx, y: yy, t: img };
- continue;
- }
- img = cloneNode();
- img.style.top = ((ii-2)*fixmystreet.tileheight) + 'px';
- img.style.left = ((jj-2)*fixmystreet.tilewidth) + 'px';
- img.name = [ 'tile_', xx, '.', yy ].join('')
- img.id = id;
- if (browser) {
- img.style.visibility = 'hidden';
- img.onload=function() { this.style.visibility = 'visible'; }
- }
- img.src = 'http://tilma.mysociety.org/tileserver/' + fixmystreet.tile_type + '/' + tiles[i][j];
- tileCache[id] = { x: xx, y: yy, t: img };
- drag.appendChild(img);
- }
- }
-}
-
-var imgElCache;
-function cloneNode() {
- var img = null;
- if (!imgElCache) {
- var form = document.getElementById('mapForm');
- if (form) {
- img = imgElCache = document.createElement('input');
- img.type = 'image';
- } else {
- img = imgElCache = document.createElement('img');
- }
- img.onclick = drag_check;
- img.style.position = 'absolute';
- img.style.width = fixmystreet.tilewidth + 'px';
- img.style.height = fixmystreet.tileheight + 'px';
- img.galleryimg = false;
- img.alt = 'Loading...';
- } else {
- img = imgElCache.cloneNode(true);
- }
- return img;
-}
-
-var tileCache=[];
-function cleanCache() {
- for (var i in tileCache) {
- if (tileCache[i].x < fixmystreet.x || tileCache[i].x > fixmystreet.x+5 || tileCache[i].y < fixmystreet.y || tileCache[i].y > fixmystreet.y+5) {
- var t = tileCache[i].t;
- t.parentNode.removeChild(t); // de-leak?
- delete tileCache[i];
- }
- }
-}
-
-/* Called every mousemove, so on first call, overwrite itself with quicker version */
-function get_posn(ev) {
- var posx, posy;
- if (ev.pageX || ev.pageY) {
- get_posn = function(e) {
- return { x: e.pageX, y: e.pageY };
- };
- } else if (ev.clientX || ev.clientY) {
- get_posn = function(e) {
- return {
- x: e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
- y: e.clientY + document.body.scrollTop + document.documentElement.scrollTop
- };
- };
- } else {
- get_posn = function(e) {
- return { x: undef, y: undef };
- };
- }
- return get_posn(ev);
-}
-
-function setCursor(s) {
- var drag = document.getElementById('drag');
- var inputs = drag.getElementsByTagName('input');
- for (var i=0; i<inputs.length; i++) {
- inputs[i].style.cursor = s;
- }
-}
-
-var in_drag = false;
-function drag_check(e) {
- if (in_drag) {
- in_drag = false;
- return false;
- }
- return true;
-}
-
-/* Simpler version of DDProxy */
-var mouse_pos = {};
-YAHOO.util.DDMap = function(id, sGroup, config) {
- this.init(id, sGroup, config);
-};
-YAHOO.extend(YAHOO.util.DDMap, YAHOO.util.DD, {
- scroll: false,
- b4MouseDown: function(e) { },
- startDrag: function(x, y) {
- mouse_pos = { x: x, y: y };
- setCursor('move');
- in_drag = true;
- },
- b4Drag: function(e) { },
- onDrag: function(e) {
- var point = get_posn(e);
- if (point == mouse_pos) return false;
- var dx = point.x-mouse_pos.x;
- var dy = point.y-mouse_pos.y;
- mouse_pos = point;
- update_tiles(dx, dy, false);
- },
- endDrag: function(e) {
- setCursor('crosshair');
- cleanCache();
- },
- toString: function() {
- return ("DDMap " + this.id);
- }
-});
-
-var browser = 1;
-var ua=navigator.userAgent.toLowerCase();
-if (!/opera|safari|gecko/.test(ua) && typeof document.all!='undefined')
- browser=0;
-
-function getInt(n) {
- n = parseInt(n); return (isNaN(n) ? 0 : n);
-}
-
diff --git a/web/js/OpenLayers.Projection.OrdnanceSurvey.js b/web/js/OpenLayers.Projection.OrdnanceSurvey.js
new file mode 100644
index 000000000..bb596d3bf
--- /dev/null
+++ b/web/js/OpenLayers.Projection.OrdnanceSurvey.js
@@ -0,0 +1,489 @@
+/**
+ * OpenLayers OSGB Grid Projection Transformations
+ *
+ * Conversion to OpenLayers by Thomas Wood (grand.edgemaster@gmail.com)
+ *
+ * this program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * PLEASE DO NOT HOTLINK THIS, save this onto your own server
+ * - I cannot guarantee this file will remain here forever.
+ *
+ * ---------------------------------------------------------------------------
+ *
+ * Credits:
+ * Based from the geotools js library by Paul Dixon
+ * GeoTools javascript coordinate transformations
+ * http://files.dixo.net/geotools.html
+ *
+ * Portions of this file copyright (c)2005 Paul Dixon (paul@elphin.com)
+ *
+ * The algorithm used by the script for WGS84-OSGB36 conversions is derived
+ * from an OSGB spreadsheet (www.gps.gov.uk) with permission. This has been
+ * adapted into Perl by Ian Harris, and into PHP by Barry Hunter. Conversion
+ * accuracy is in the order of 7m for 90% of Great Britain, and should be
+ * be similar to the conversion made by a typical GPSr
+ *
+ */
+
+OpenLayers.Projection.OS = {
+
+ /**
+ * Method: projectForwardBritish
+ * Given an object with x and y properties in EPSG:4326, modify the x,y
+ * properties on the object to be the OSGB36 (transverse mercator)
+ * projected coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed to spherical
+ * mercator.
+ */
+ projectForwardBritish: function(point) {
+ var x1 = OpenLayers.Projection.OS.Lat_Long_H_to_X(point.y,point.x,0,6378137.00,6356752.313);
+ var y1 = OpenLayers.Projection.OS.Lat_Long_H_to_Y(point.y,point.x,0,6378137.00,6356752.313);
+ var z1 = OpenLayers.Projection.OS.Lat_H_to_Z (point.y, 0,6378137.00,6356752.313);
+
+ var x2 = OpenLayers.Projection.OS.Helmert_X(x1,y1,z1,-446.448,-0.2470,-0.8421,20.4894);
+ var y2 = OpenLayers.Projection.OS.Helmert_Y(x1,y1,z1, 125.157,-0.1502,-0.8421,20.4894);
+ var z2 = OpenLayers.Projection.OS.Helmert_Z(x1,y1,z1,-542.060,-0.1502,-0.2470,20.4894);
+
+ var lat2 = OpenLayers.Projection.OS.XYZ_to_Lat (x2,y2,z2,6377563.396,6356256.910);
+ var lon2 = OpenLayers.Projection.OS.XYZ_to_Long(x2,y2);
+
+ point.x = OpenLayers.Projection.OS.Lat_Long_to_East (lat2,lon2,6377563.396,6356256.910,400000,0.999601272,49.00000,-2.00000);
+ point.y = OpenLayers.Projection.OS.Lat_Long_to_North(lat2,lon2,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);
+
+ return point;
+ },
+
+ /**
+ * Method: projectInverseBritish
+ * Given an object with x and y properties in OSGB36 (transverse mercator),
+ * modify the x,y properties on the object to be the unprojected coordinates.
+ *
+ * Parameters:
+ * point - {Object} An object with x and y properties.
+ *
+ * Returns:
+ * {Object} The point, with the x and y properties transformed from
+ * OSGB36 to unprojected coordinates..
+ */
+ projectInverseBritish: function(point) {
+ var lat1 = OpenLayers.Projection.OS.E_N_to_Lat (point.x,point.y,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);
+ var lon1 = OpenLayers.Projection.OS.E_N_to_Long(point.x,point.y,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);
+
+ var x1 = OpenLayers.Projection.OS.Lat_Long_H_to_X(lat1,lon1,0,6377563.396,6356256.910);
+ var y1 = OpenLayers.Projection.OS.Lat_Long_H_to_Y(lat1,lon1,0,6377563.396,6356256.910);
+ var z1 = OpenLayers.Projection.OS.Lat_H_to_Z (lat1, 0,6377563.396,6356256.910);
+
+ var x2 = OpenLayers.Projection.OS.Helmert_X(x1,y1,z1,446.448 ,0.2470,0.8421,-20.4894);
+ var y2 = OpenLayers.Projection.OS.Helmert_Y(x1,y1,z1,-125.157,0.1502,0.8421,-20.4894);
+ var z2 = OpenLayers.Projection.OS.Helmert_Z(x1,y1,z1,542.060 ,0.1502,0.2470,-20.4894);
+
+ var lat = OpenLayers.Projection.OS.XYZ_to_Lat(x2,y2,z2,6378137.000,6356752.313);
+ var lon = OpenLayers.Projection.OS.XYZ_to_Long(x2,y2);
+
+ point.x = lon;
+ point.y = lat;
+ return point;
+ },
+
+ goog2osgb: function(point) {
+ return OpenLayers.Projection.OS.projectForwardBritish(OpenLayers.Layer.SphericalMercator.projectInverse(point));
+ },
+
+ osgb2goog: function(point) {
+ return OpenLayers.Layer.SphericalMercator.projectForward(OpenLayers.Projection.OS.projectInverseBritish(point));
+ },
+
+ /*****
+ * Mathematical functions
+ *****/
+ E_N_to_Lat: function(East, North, a, b, e0, n0, f0, PHI0, LAM0) {
+ //Un-project Transverse Mercator eastings and northings back to latitude.
+ //eastings (East) and northings (North) in meters; _
+ //ellipsoid axis dimensions (a & b) in meters; _
+ //eastings (e0) and northings (n0) of false origin in meters; _
+ //central meridian scale factor (f0) and _
+ //latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.
+
+ //Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI0 = PHI0 * (Pi / 180);
+ var RadLAM0 = LAM0 * (Pi / 180);
+
+ //Compute af0, bf0, e squared (e2), n and Et
+ var af0 = a * f0;
+ var bf0 = b * f0;
+ var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
+ var n = (af0 - bf0) / (af0 + bf0);
+ var Et = East - e0;
+
+ //Compute initial value for latitude (PHI) in radians
+ var PHId = OpenLayers.Projection.OS.InitialLat(North, n0, af0, RadPHI0, n, bf0);
+
+ //Compute nu, rho and eta2 using value for PHId
+ var nu = af0 / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(PHId),2)))));
+ var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(PHId),2)));
+ var eta2 = (nu / rho) - 1;
+
+ //Compute Latitude
+ var VII = (Math.tan(PHId)) / (2 * rho * nu);
+ var VIII = ((Math.tan(PHId)) / (24 * rho * Math.pow(nu,3))) * (5 + (3 * (Math.pow(Math.tan(PHId),2))) + eta2 - (9 * eta2 * (Math.pow(Math.tan(PHId),2))));
+ var IX = ((Math.tan(PHId)) / (720 * rho * Math.pow(nu,5))) * (61 + (90 * ((Math.tan(PHId)) ^ 2)) + (45 * (Math.pow(Math.tan(PHId),4))));
+
+ var E_N_to_Lat = (180 / Pi) * (PHId - (Math.pow(Et,2) * VII) + (Math.pow(Et,4) * VIII) - ((Et ^ 6) * IX));
+
+ return (E_N_to_Lat);
+ },
+
+ E_N_to_Long: function(East, North, a, b, e0, n0, f0, PHI0, LAM0) {
+ //Un-project Transverse Mercator eastings and northings back to longitude.
+ //eastings (East) and northings (North) in meters; _
+ //ellipsoid axis dimensions (a & b) in meters; _
+ //eastings (e0) and northings (n0) of false origin in meters; _
+ //central meridian scale factor (f0) and _
+ //latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.
+
+ //Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI0 = PHI0 * (Pi / 180);
+ var RadLAM0 = LAM0 * (Pi / 180);
+
+ //Compute af0, bf0, e squared (e2), n and Et
+ var af0 = a * f0;
+ var bf0 = b * f0;
+ var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
+ var n = (af0 - bf0) / (af0 + bf0);
+ var Et = East - e0;
+
+ //Compute initial value for latitude (PHI) in radians
+ var PHId = OpenLayers.Projection.OS.InitialLat(North, n0, af0, RadPHI0, n, bf0);
+
+ //Compute nu, rho and eta2 using value for PHId
+ var nu = af0 / (Math.sqrt(1 - (e2 * (Math.pow(Math.sin(PHId),2)))));
+ var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(PHId),2)));
+ var eta2 = (nu / rho) - 1;
+
+ //Compute Longitude
+ var X = (Math.pow(Math.cos(PHId),-1)) / nu;
+ var XI = ((Math.pow(Math.cos(PHId),-1)) / (6 * Math.pow(nu,3))) * ((nu / rho) + (2 * (Math.pow(Math.tan(PHId),2))));
+ var XII = ((Math.pow(Math.cos(PHId),-1)) / (120 * Math.pow(nu,5))) * (5 + (28 * (Math.pow(Math.tan(PHId),2))) + (24 * (Math.pow(Math.tan(PHId),4))));
+ var XIIA = ((Math.pow(Math.cos(PHId),-1)) / (5040 * Math.pow(nu,7))) * (61 + (662 * (Math.pow(Math.tan(PHId),2))) + (1320 * (Math.pow(Math.tan(PHId),4))) + (720 * (Math.pow(Math.tan(PHId),6))));
+
+ var E_N_to_Long = (180 / Pi) * (RadLAM0 + (Et * X) - (Math.pow(Et,3) * XI) + (Math.pow(Et,5) * XII) - (Math.pow(Et,7) * XIIA));
+
+ return E_N_to_Long;
+ },
+
+ InitialLat: function(North, n0, afo, PHI0, n, bfo) {
+ //Compute initial value for Latitude (PHI) IN RADIANS.
+ //northing of point (North) and northing of false origin (n0) in meters; _
+ //semi major axis multiplied by central meridian scale factor (af0) in meters; _
+ //latitude of false origin (PHI0) IN RADIANS; _
+ //n (computed from a, b and f0) and _
+ //ellipsoid semi major axis multiplied by central meridian scale factor (bf0) in meters.
+
+ //First PHI value (PHI1)
+ var PHI1 = ((North - n0) / afo) + PHI0;
+
+ //Calculate M
+ var M = OpenLayers.Projection.OS.Marc(bfo, n, PHI0, PHI1);
+
+ //Calculate new PHI value (PHI2)
+ var PHI2 = ((North - n0 - M) / afo) + PHI1;
+
+ //Iterate to get final value for InitialLat
+ while (Math.abs(North - n0 - M) > 0.00001)
+ {
+ PHI2 = ((North - n0 - M) / afo) + PHI1;
+ M = OpenLayers.Projection.OS.Marc(bfo, n, PHI0, PHI2);
+ PHI1 = PHI2;
+ }
+ return PHI2;
+ },
+
+ Lat_Long_H_to_X: function(PHI, LAM, H, a, b) {
+ // Convert geodetic coords lat (PHI), long (LAM) and height (H) to cartesian X coordinate.
+ // Input: - _
+ // Latitude (PHI)& Longitude (LAM) both in decimal degrees; _
+ // Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.
+
+ // Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI = PHI * (Pi / 180);
+ var RadLAM = LAM * (Pi / 180);
+
+ // Compute eccentricity squared and nu
+ var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
+ var V = a / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(RadPHI),2)))));
+
+ // Compute X
+ return (V + H) * (Math.cos(RadPHI)) * (Math.cos(RadLAM));
+ },
+
+
+ Lat_Long_H_to_Y: function(PHI, LAM, H, a, b) {
+ // Convert geodetic coords lat (PHI), long (LAM) and height (H) to cartesian Y coordinate.
+ // Input: - _
+ // Latitude (PHI)& Longitude (LAM) both in decimal degrees; _
+ // Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.
+
+ // Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI = PHI * (Pi / 180);
+ var RadLAM = LAM * (Pi / 180);
+
+ // Compute eccentricity squared and nu
+ var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
+ var V = a / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(RadPHI),2))) ));
+
+ // Compute Y
+ return (V + H) * (Math.cos(RadPHI)) * (Math.sin(RadLAM));
+ },
+
+
+ Lat_H_to_Z: function(PHI, H, a, b) {
+ // Convert geodetic coord components latitude (PHI) and height (H) to cartesian Z coordinate.
+ // Input: - _
+ // Latitude (PHI) decimal degrees; _
+ // Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.
+
+ // Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI = PHI * (Pi / 180);
+
+ // Compute eccentricity squared and nu
+ var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
+ var V = a / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(RadPHI),2)) )));
+
+ // Compute X
+ return ((V * (1 - e2)) + H) * (Math.sin(RadPHI));
+ },
+
+
+ Helmert_X: function(X,Y,Z,DX,Y_Rot,Z_Rot,s) {
+
+ // (X, Y, Z, DX, Y_Rot, Z_Rot, s)
+ // Computed Helmert transformed X coordinate.
+ // Input: - _
+ // cartesian XYZ coords (X,Y,Z), X translation (DX) all in meters ; _
+ // Y and Z rotations in seconds of arc (Y_Rot, Z_Rot) and scale in ppm (s).
+
+ // Convert rotations to radians and ppm scale to a factor
+ var Pi = 3.14159265358979;
+ var sfactor = s * 0.000001;
+
+ var RadY_Rot = (Y_Rot / 3600) * (Pi / 180);
+
+ var RadZ_Rot = (Z_Rot / 3600) * (Pi / 180);
+
+ //Compute transformed X coord
+ return (X + (X * sfactor) - (Y * RadZ_Rot) + (Z * RadY_Rot) + DX);
+ },
+
+
+ Helmert_Y: function(X,Y,Z,DY,X_Rot,Z_Rot,s) {
+ // Computed Helmert transformed Y coordinate.
+ // Input: - _
+ // cartesian XYZ coords (X,Y,Z), Y translation (DY) all in meters ; _
+ // X and Z rotations in seconds of arc (X_Rot, Z_Rot) and scale in ppm (s).
+
+ // Convert rotations to radians and ppm scale to a factor
+ var Pi = 3.14159265358979;
+ var sfactor = s * 0.000001;
+ var RadX_Rot = (X_Rot / 3600) * (Pi / 180);
+ var RadZ_Rot = (Z_Rot / 3600) * (Pi / 180);
+
+ // Compute transformed Y coord
+ return (X * RadZ_Rot) + Y + (Y * sfactor) - (Z * RadX_Rot) + DY;
+ },
+
+
+
+ Helmert_Z: function(X, Y, Z, DZ, X_Rot, Y_Rot, s) {
+ // Computed Helmert transformed Z coordinate.
+ // Input: - _
+ // cartesian XYZ coords (X,Y,Z), Z translation (DZ) all in meters ; _
+ // X and Y rotations in seconds of arc (X_Rot, Y_Rot) and scale in ppm (s).
+ //
+ // Convert rotations to radians and ppm scale to a factor
+ var Pi = 3.14159265358979;
+ var sfactor = s * 0.000001;
+ var RadX_Rot = (X_Rot / 3600) * (Pi / 180);
+ var RadY_Rot = (Y_Rot / 3600) * (Pi / 180);
+
+ // Compute transformed Z coord
+ return (-1 * X * RadY_Rot) + (Y * RadX_Rot) + Z + (Z * sfactor) + DZ;
+ } ,
+
+ XYZ_to_Lat: function(X, Y, Z, a, b) {
+ // Convert XYZ to Latitude (PHI) in Dec Degrees.
+ // Input: - _
+ // XYZ cartesian coords (X,Y,Z) and ellipsoid axis dimensions (a & b), all in meters.
+
+ // this FUNCTION REQUIRES THE "Iterate_XYZ_to_Lat" FUNCTION
+ // this FUNCTION IS CALLED BY THE "XYZ_to_H" FUNCTION
+
+ var RootXYSqr = Math.sqrt(Math.pow(X,2) + Math.pow(Y,2));
+ var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
+ var PHI1 = Math.atan2(Z , (RootXYSqr * (1 - e2)) );
+
+ var PHI = OpenLayers.Projection.OS.Iterate_XYZ_to_Lat(a, e2, PHI1, Z, RootXYSqr);
+
+ var Pi = 3.14159265358979;
+
+ return PHI * (180 / Pi);
+ },
+
+
+ Iterate_XYZ_to_Lat: function(a, e2, PHI1, Z, RootXYSqr) {
+ // Iteratively computes Latitude (PHI).
+ // Input: - _
+ // ellipsoid semi major axis (a) in meters; _
+ // eta squared (e2); _
+ // estimated value for latitude (PHI1) in radians; _
+ // cartesian Z coordinate (Z) in meters; _
+ // RootXYSqr computed from X & Y in meters.
+
+ // this FUNCTION IS CALLED BY THE "XYZ_to_PHI" FUNCTION
+ // this FUNCTION IS ALSO USED ON IT'S OWN IN THE _
+ // "Projection and Transformation Calculations.xls" SPREADSHEET
+
+
+ var V = a / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(PHI1),2))));
+ var PHI2 = Math.atan2((Z + (e2 * V * (Math.sin(PHI1)))) , RootXYSqr);
+
+ while (Math.abs(PHI1 - PHI2) > 0.000000001) {
+ PHI1 = PHI2;
+ V = a / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(PHI1),2))));
+ PHI2 = Math.atan2((Z + (e2 * V * (Math.sin(PHI1)))) , RootXYSqr);
+ }
+
+ return PHI2;
+ },
+
+
+ XYZ_to_Long: function (X, Y) {
+ // Convert XYZ to Longitude (LAM) in Dec Degrees.
+ // Input: - _
+ // X and Y cartesian coords in meters.
+
+ var Pi = 3.14159265358979;
+ return Math.atan2(Y , X) * (180 / Pi);
+ },
+
+ Marc: function (bf0, n, PHI0, PHI) {
+ //Compute meridional arc.
+ //Input: - _
+ // ellipsoid semi major axis multiplied by central meridian scale factor (bf0) in meters; _
+ // n (computed from a, b and f0); _
+ // lat of false origin (PHI0) and initial or final latitude of point (PHI) IN RADIANS.
+
+ //this FUNCTION IS CALLED BY THE - _
+ // "Lat_Long_to_North" and "InitialLat" FUNCTIONS
+ // this FUNCTION IS ALSO USED ON IT'S OWN IN THE "Projection and Transformation Calculations.xls" SPREADSHEET
+
+ return bf0 * (((1 + n + ((5 / 4) * Math.pow(n,2)) + ((5 / 4) * Math.pow(n,3))) * (PHI - PHI0)) - (((3 * n) + (3 * Math.pow(n,2)) + ((21 / 8) * Math.pow(n,3))) * (Math.sin(PHI - PHI0)) * (Math.cos(PHI + PHI0))) + ((((15 / 8
+ ) * Math.pow(n,2)) + ((15 / 8) * Math.pow(n,3))) * (Math.sin(2 * (PHI - PHI0))) * (Math.cos(2 * (PHI + PHI0)))) - (((35 / 24) * Math.pow(n,3)) * (Math.sin(3 * (PHI - PHI0))) * (Math.cos(3 * (PHI + PHI0)))));
+ },
+
+ Lat_Long_to_East: function (PHI, LAM, a, b, e0, f0, PHI0, LAM0) {
+ //Project Latitude and longitude to Transverse Mercator eastings.
+ //Input: - _
+ // Latitude (PHI) and Longitude (LAM) in decimal degrees; _
+ // ellipsoid axis dimensions (a & b) in meters; _
+ // eastings of false origin (e0) in meters; _
+ // central meridian scale factor (f0); _
+ // latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.
+
+ // Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI = PHI * (Pi / 180);
+ var RadLAM = LAM * (Pi / 180);
+ var RadPHI0 = PHI0 * (Pi / 180);
+ var RadLAM0 = LAM0 * (Pi / 180);
+
+ var af0 = a * f0;
+ var bf0 = b * f0;
+ var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
+ var n = (af0 - bf0) / (af0 + bf0);
+ var nu = af0 / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(RadPHI),2) )));
+ var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(RadPHI),2) ));
+ var eta2 = (nu / rho) - 1;
+ var p = RadLAM - RadLAM0;
+
+ var IV = nu * (Math.cos(RadPHI));
+ var V = (nu / 6) * ( Math.pow(Math.cos(RadPHI),3)) * ((nu / rho) - (Math.pow(Math.tan(RadPHI),2)));
+ var VI = (nu / 120) * (Math.pow(Math.cos(RadPHI),5)) * (5 - (18 * (Math.pow(Math.tan(RadPHI),2))) + (Math.pow(Math.tan(RadPHI),4)) + (14 * eta2) - (58 * (Math.pow(Math.tan(RadPHI),2)) * eta2));
+
+ return e0 + (p * IV) + (Math.pow(p,3) * V) + (Math.pow(p,5) * VI);
+ },
+
+ Lat_Long_to_North: function (PHI, LAM, a, b, e0, n0, f0, PHI0, LAM0) {
+ // Project Latitude and longitude to Transverse Mercator northings
+ // Input: - _
+ // Latitude (PHI) and Longitude (LAM) in decimal degrees; _
+ // ellipsoid axis dimensions (a & b) in meters; _
+ // eastings (e0) and northings (n0) of false origin in meters; _
+ // central meridian scale factor (f0); _
+ // latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.
+
+ // REQUIRES THE "Marc" FUNCTION
+
+ // Convert angle measures to radians
+ var Pi = 3.14159265358979;
+ var RadPHI = PHI * (Pi / 180);
+ var RadLAM = LAM * (Pi / 180);
+ var RadPHI0 = PHI0 * (Pi / 180);
+ var RadLAM0 = LAM0 * (Pi / 180);
+
+ var af0 = a * f0;
+ var bf0 = b * f0;
+ var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
+ var n = (af0 - bf0) / (af0 + bf0);
+ var nu = af0 / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(RadPHI),2))));
+ var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(RadPHI),2)));
+ var eta2 = (nu / rho) - 1;
+ var p = RadLAM - RadLAM0;
+ var M = OpenLayers.Projection.OS.Marc(bf0, n, RadPHI0, RadPHI);
+
+ var I = M + n0;
+ var II = (nu / 2) * (Math.sin(RadPHI)) * (Math.cos(RadPHI));
+ var III = ((nu / 24) * (Math.sin(RadPHI)) * (Math.pow(Math.cos(RadPHI),3))) * (5 - (Math.pow(Math.tan(RadPHI),2)) + (9 * eta2));
+ var IIIA = ((nu / 720) * (Math.sin(RadPHI)) * (Math.pow(Math.cos(RadPHI),5))) * (61 - (58 * (Math.pow(Math.tan(RadPHI),2))) + (Math.pow(Math.tan(RadPHI),4)));
+
+ return I + (Math.pow(p,2) * II) + (Math.pow(p,4) * III) + (Math.pow(p,6) * IIIA);
+ }
+
+};
+
+/**
+ * Note: Two transforms declared
+ * Transforms from EPSG:4326 to EPSG:27700 and from EPSG:27700 to EPSG:4326
+ * are set by this class.
+ */
+OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:27700",
+ OpenLayers.Projection.OS.projectForwardBritish);
+OpenLayers.Projection.addTransform("EPSG:27700", "EPSG:4326",
+ OpenLayers.Projection.OS.projectInverseBritish);
+OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:27700",
+ OpenLayers.Projection.OS.goog2osgb);
+OpenLayers.Projection.addTransform("EPSG:27700", "EPSG:900913",
+ OpenLayers.Projection.OS.osgb2goog);
diff --git a/web/js/map-OpenStreetMap.js b/web/js/map-OpenStreetMap.js
new file mode 100644
index 000000000..04237e075
--- /dev/null
+++ b/web/js/map-OpenStreetMap.js
@@ -0,0 +1,150 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var map = new OpenLayers.Map("map", {
+ controls: [
+ new OpenLayers.Control.ArgParser(),
+ //new OpenLayers.Control.LayerSwitcher(),
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanZoom()
+ ],
+ displayProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var streetview = new fixmystreet.map_type("", {
+ zoomOffset: 14,
+ numZoomLevels: 4
+ });
+ map.addLayer(streetview);
+
+ var centre = new OpenLayers.LonLat( fixmystreet.easting, fixmystreet.northing );
+ centre.transform(
+ new OpenLayers.Projection("EPSG:27700"),
+ map.getProjectionObject()
+ );
+ map.setCenter(centre, 2);
+});
+
+
+// http://www.openstreetmap.org/openlayers/OpenStreetMap.js (added maxResolution)
+
+/**
+ * Namespace: Util.OSM
+ */
+OpenLayers.Util.OSM = {};
+
+/**
+ * Constant: MISSING_TILE_URL
+ * {String} URL of image to display for missing tiles
+ */
+OpenLayers.Util.OSM.MISSING_TILE_URL = "http://www.openstreetmap.org/openlayers/img/404.png";
+
+/**
+ * Property: originalOnImageLoadError
+ * {Function} Original onImageLoadError function.
+ */
+OpenLayers.Util.OSM.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;
+
+/**
+ * Function: onImageLoadError
+ */
+OpenLayers.Util.onImageLoadError = function() {
+ if (this.src.match(/^http:\/\/[abc]\.[a-z]+\.openstreetmap\.org\//)) {
+ this.src = OpenLayers.Util.OSM.MISSING_TILE_URL;
+ } else if (this.src.match(/^http:\/\/[def]\.tah\.openstreetmap\.org\//)) {
+ // do nothing - this layer is transparent
+ } else {
+ OpenLayers.Util.OSM.originalOnImageLoadError;
+ }
+};
+
+/**
+ * Class: OpenLayers.Layer.OSM.Mapnik
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.OSM>
+ */
+OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, {
+ /**
+ * Constructor: OpenLayers.Layer.OSM.Mapnik
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ var url = [
+ "http://a.tile.openstreetmap.org/${z}/${x}/${y}.png",
+ "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png",
+ "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"
+ ];
+ options = OpenLayers.Util.extend({
+ /* Below line added to OSM's file in order to allow minimum zoom level */
+ maxResolution: 156543.0339/Math.pow(2, options.zoomOffset || 0),
+ numZoomLevels: 19,
+ buffer: 0
+ }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.OSM.Mapnik"
+});
+
+/**
+ * Class: OpenLayers.Layer.OSM.Osmarender
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.OSM>
+ */
+OpenLayers.Layer.OSM.Osmarender = OpenLayers.Class(OpenLayers.Layer.OSM, {
+ /**
+ * Constructor: OpenLayers.Layer.OSM.Osmarender
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ var url = [
+ "http://a.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png",
+ "http://b.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png",
+ "http://c.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png"
+ ];
+ options = OpenLayers.Util.extend({ numZoomLevels: 18, buffer: 0 }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.OSM.Osmarender"
+});
+
+/**
+ * Class: OpenLayers.Layer.OSM.CycleMap
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.OSM>
+ */
+OpenLayers.Layer.OSM.CycleMap = OpenLayers.Class(OpenLayers.Layer.OSM, {
+ /**
+ * Constructor: OpenLayers.Layer.OSM.CycleMap
+ *
+ * Parameters:
+ * name - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ var url = [
+ "http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
+ "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
+ "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"
+ ];
+ options = OpenLayers.Util.extend({
+ /* Below line added to OSM's file in order to allow minimum zoom level */
+ maxResolution: 156543.0339/Math.pow(2, options.zoomOffset || 0),
+ numZoomLevels: 19,
+ buffer: 0
+ }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.OSM.CycleMap"
+});
diff --git a/web/js/map-bing-ol.js b/web/js/map-bing-ol.js
new file mode 100644
index 000000000..ba9445326
--- /dev/null
+++ b/web/js/map-bing-ol.js
@@ -0,0 +1,96 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var map = new OpenLayers.Map("map", {
+ controls: [
+ new OpenLayers.Control.ArgParser(),
+ //new OpenLayers.Control.LayerSwitcher(),
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanZoom()
+ ],
+ displayProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var bing = new OpenLayers.Layer.Bing("", {
+ zoomOffset: 13,
+ numZoomLevels: 4
+ });
+ map.addLayer(bing);
+
+ var centre = new OpenLayers.LonLat( fixmystreet.easting, fixmystreet.northing );
+ centre.transform(
+ new OpenLayers.Projection("EPSG:27700"),
+ map.getProjectionObject()
+ );
+ map.setCenter(centre, 3);
+});
+
+OpenLayers.Util.OS = {};
+OpenLayers.Util.OS.MISSING_TILE_URL = "http://openstreetmap.org/openlayers/img/404.png";
+OpenLayers.Util.OS.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;
+OpenLayers.Util.onImageLoadError = function() {
+ OpenLayers.Util.OS.originalOnImageLoadError;
+};
+
+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ initialize: function(name, options) {
+ var url = [];
+ options = OpenLayers.Util.extend({
+ /* Below line added to OSM's file in order to allow minimum zoom level */
+ maxResolution: 156543.0339/Math.pow(2, options.zoomOffset || 0),
+ numZoomLevels: 18,
+ transitionEffect: "resize",
+ sphericalMercator: true,
+ attribution: "© Microsoft / OS 2010"
+ }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArguments);
+ },
+
+ get_quadkey: function(x, y, level) {
+ var key = '';
+ for (var i = level; i > 0; i--) {
+ var digit = 0;
+ var mask = 1 << (i - 1);
+ if ((x & mask) != 0) {
+ digit++;
+ }
+ if ((y & mask) != 0) {
+ digit += 2;
+ }
+ key += digit;
+ }
+ return key;
+ },
+
+ getURL: function (bounds) {
+ var res = this.map.getResolution();
+ var x = Math.round((bounds.left - this.maxExtent.left)
+ / (res * this.tileSize.w));
+ var y = Math.round((this.maxExtent.top - bounds.top)
+ / (res * this.tileSize.h));
+ var z = this.serverResolutions != null ?
+ OpenLayers.Util.indexOf(this.serverResolutions, res) :
+ this.map.getZoom() + this.zoomOffset;
+
+ if (z == 16) {
+ var url = [
+ "http://a.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
+ "http://b.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
+ "http://c.os.openstreetmap.org/sv/${z}/${x}/${y}.png"
+ ];
+ } else {
+ var url = [
+ "http://ecn.t0.tiles.virtualearth.net/tiles/r${id}.png?g=587&productSet=mmOS",
+ "http://ecn.t1.tiles.virtualearth.net/tiles/r${id}.png?g=587&productSet=mmOS",
+ "http://ecn.t2.tiles.virtualearth.net/tiles/r${id}.png?g=587&productSet=mmOS",
+ "http://ecn.t3.tiles.virtualearth.net/tiles/r${id}.png?g=587&productSet=mmOS"
+ ];
+ }
+ var s = '' + x + y + z;
+ url = this.selectUrl(s, url);
+
+ var id = this.get_quadkey(x, y, z);
+ var path = OpenLayers.String.format(url, {'id': id, 'x': x, 'y': y, 'z': z});
+ return path;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Bing"
+});
diff --git a/web/js/map-bing.js b/web/js/map-bing.js
new file mode 100644
index 000000000..fbbbcc676
--- /dev/null
+++ b/web/js/map-bing.js
@@ -0,0 +1,21 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var centre = new Microsoft.Maps.Location( fixmystreet.lat, fixmystreet.lon );
+ var map = new Microsoft.Maps.Map(document.getElementById("map"), {
+ mapTypeId: Microsoft.Maps.MapTypeId.ordnanceSurvey,
+ center: centre,
+ zoom: 15,
+ enableClickableLogo: false,
+ enableSearchLogo: false,
+ showCopyright: false,
+ showDashboard: true,
+ showLogo: false,
+ showScalebar: false
+ });
+ //minZoomLevel: 14,
+ //numZoomLevels: 4
+
+ Microsoft.Maps.Events.addHandler(map, "viewchangestart", function(e) {
+ /* Doesn't work */
+ if (map.getTargetZoom() < 12) return false;
+ });
+});
diff --git a/web/js/map-google.js b/web/js/map-google.js
new file mode 100644
index 000000000..77e54ab7c
--- /dev/null
+++ b/web/js/map-google.js
@@ -0,0 +1,22 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var centre = new google.maps.LatLng( fixmystreet.lat, fixmystreet.lon );
+ var map = new google.maps.Map(document.getElementById("map"), {
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
+ center: centre,
+ zoom: 16,
+ disableDefaultUI: true,
+ navigationControl: true,
+ navigationControlOptions: {
+ style: google.maps.NavigationControlStyle.SMALL
+ },
+ mapTypeControl: true,
+ mapTypeControlOptions: {
+ style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
+ }
+ });
+
+ google.maps.event.addListener(map, "zoom_changed", function() {
+ if (map.getZoom() < 13) map.setZoom(13);
+ if (map.getZoom() > 17) map.setZoom(17);
+ });
+});
diff --git a/web/js/map-streetview.js b/web/js/map-streetview.js
new file mode 100644
index 000000000..088e5b6a2
--- /dev/null
+++ b/web/js/map-streetview.js
@@ -0,0 +1,88 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var map = new OpenLayers.Map("map", {
+ controls: [
+ new OpenLayers.Control.ArgParser(),
+ //new OpenLayers.Control.LayerSwitcher(),
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanZoom()
+ ],
+ displayProjection: new OpenLayers.Projection("EPSG:4326")
+ });
+ var streetview = new OpenLayers.Layer.StreetView("OS StreetView (1:10000)", {
+ zoomOffset: 14,
+ numZoomLevels: 4
+ });
+ map.addLayer(streetview);
+
+ var centre = new OpenLayers.LonLat( fixmystreet.easting, fixmystreet.northing );
+ centre.transform(
+ new OpenLayers.Projection("EPSG:27700"),
+ map.getProjectionObject()
+ );
+ map.setCenter(centre, 2);
+});
+
+
+// http://os.openstreetmap.org/openlayers/OS.js (added one line)
+
+/**
+ * Namespace: Util.OS
+ */
+OpenLayers.Util.OS = {};
+
+/**
+ * Constant: MISSING_TILE_URL
+ * {String} URL of image to display for missing tiles
+ */
+OpenLayers.Util.OS.MISSING_TILE_URL = "http://openstreetmap.org/openlayers/img/404.png";
+
+/**
+ * Property: originalOnImageLoadError
+ * {Function} Original onImageLoadError function.
+ */
+OpenLayers.Util.OS.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;
+
+/**
+ * Function: onImageLoadError
+ */
+OpenLayers.Util.onImageLoadError = function() {
+ OpenLayers.Util.OS.originalOnImageLoadError;
+};
+
+/**
+ * @requires OpenLayers/Layer/XYZ.js
+ *
+ * Class: OpenLayers.Layer.StreetView
+ *
+ * Inherits from:
+ * - <OpenLayers.Layer.XYZ>
+ */
+OpenLayers.Layer.StreetView = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ /**
+ * Constructor: OpenLayers.Layer.StreetView
+ *
+ * Parameters:
+ * name - {String}
+ * url - {String}
+ * options - {Object} Hashtable of extra options to tag onto the layer
+ */
+ initialize: function(name, options) {
+ var url = [
+ "http://a.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
+ "http://b.os.openstreetmap.org/sv/${z}/${x}/${y}.png",
+ "http://c.os.openstreetmap.org/sv/${z}/${x}/${y}.png"
+ ];
+ options = OpenLayers.Util.extend({
+ /* Below line added to OSM's file in order to allow minimum zoom level */
+ maxResolution: 156543.0339/Math.pow(2, options.zoomOffset || 0),
+ numZoomLevels: 18,
+ transitionEffect: "resize",
+ sphericalMercator: true,
+ attribution: "Contains Ordnance Survey data © Crown copyright and database right 2010"
+ }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.StreetView"
+});
diff --git a/web/js/map-tilma-ol.js b/web/js/map-tilma-ol.js
new file mode 100644
index 000000000..cfd196c1d
--- /dev/null
+++ b/web/js/map-tilma-ol.js
@@ -0,0 +1,42 @@
+YAHOO.util.Event.onContentReady('map', function() {
+ var map = new OpenLayers.Map("map", {
+ controls: [
+ new OpenLayers.Control.ArgParser(),
+ new OpenLayers.Control.Navigation(),
+ new OpenLayers.Control.PanPanel()
+ ]
+ });
+ var tilma = new OpenLayers.Layer.Tilma("Tilma", {
+ maxResolution: fixmystreet.maxResolution,
+ tileSize: new OpenLayers.Size(fixmystreet.tilewidth, fixmystreet.tileheight),
+ map_type: fixmystreet.tile_type
+ });
+ map.addLayer(tilma);
+
+ var centre = new OpenLayers.LonLat( fixmystreet.easting, fixmystreet.northing );
+ map.setCenter(centre);
+});
+
+OpenLayers.Layer.Tilma = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+ initialize: function(name, options) {
+ var url = "http://tilma.mysociety.org/tileserver/${type}/${x},${y}/png";
+ options = OpenLayers.Util.extend({
+ transitionEffect: "resize",
+ numZoomLevels: 1,
+ units: "m",
+ maxExtent: new OpenLayers.Bounds(0, 0, 700000, 1300000),
+ }, options);
+ var newArguments = [name, url, options];
+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArguments);
+ },
+
+ getURL: function (bounds) {
+ var res = this.map.getResolution();
+ var x = Math.round(bounds.left / (res * this.tileSize.w));
+ var y = Math.round(bounds.bottom / (res * this.tileSize.h));
+ var path = OpenLayers.String.format(this.url, {'x': x, 'y': y, 'type': this.map_type});
+ return path;
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.Tilma"
+});
diff --git a/web/js/map-tilma.js b/web/js/map-tilma.js
new file mode 100644
index 000000000..1ae38cff1
--- /dev/null
+++ b/web/js/map-tilma.js
@@ -0,0 +1,320 @@
+/*
+ * map-tilma.js
+ * JavaScript specifically for the tilma based maps
+ */
+
+function compass_pan(e, a) {
+ YAHOO.util.Event.preventDefault(e);
+ if (a.home) {
+ a.x = a.orig_x-drag_x;
+ a.y = a.orig_y-drag_y;
+ }
+ pan(a.x, a.y);
+}
+
+YAHOO.util.Event.onContentReady('compass', function() {
+ var ua=navigator.userAgent.toLowerCase();
+ // if (document.getElementById('mapForm') && (/safari/.test(ua) || /Konqueror/.test(ua))) return;
+ if (document.getElementById('map').offsetWidth > 510) return;
+
+ var points = this.getElementsByTagName('a');
+ YAHOO.util.Event.addListener(points[1], 'click', compass_pan, { x:0, y:fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[3], 'click', compass_pan, { x:fixmystreet.tilewidth, y:0 });
+ YAHOO.util.Event.addListener(points[5], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:0 });
+ YAHOO.util.Event.addListener(points[7], 'click', compass_pan, { x:0, y:-fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[0], 'click', compass_pan, { x:fixmystreet.tilewidth, y:fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[2], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[6], 'click', compass_pan, { x:fixmystreet.tilewidth, y:-fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[8], 'click', compass_pan, { x:-fixmystreet.tilewidth, y:-fixmystreet.tileheight });
+ YAHOO.util.Event.addListener(points[4], 'click', compass_pan, { home:1, orig_x:drag_x, orig_y:drag_y });
+});
+
+YAHOO.util.Event.onContentReady('map', function() {
+ var ua=navigator.userAgent.toLowerCase();
+ // if (document.getElementById('mapForm') && (/safari/.test(ua) || /Konqueror/.test(ua))) return;
+ if (document.getElementById('map').offsetWidth > 510) return;
+ new YAHOO.util.DDMap('map');
+ update_tiles(fixmystreet.start_x, fixmystreet.start_y, true);
+});
+
+
+YAHOO.util.Event.addListener('hide_pins_link', 'click', function(e) {
+ YAHOO.util.Event.preventDefault(e);
+ if (this.innerHTML == 'Show pins') {
+ YAHOO.util.Dom.setStyle('pins', 'display', 'block');
+ this.innerHTML = 'Hide pins';
+ } else if (this.innerHTML == 'Dangos pinnau') {
+ YAHOO.util.Dom.setStyle('pins', 'display', 'block');
+ this.innerHTML = 'Cuddio pinnau';
+ } else if (this.innerHTML == 'Cuddio pinnau') {
+ YAHOO.util.Dom.setStyle('pins', 'display', 'none');
+ this.innerHTML = 'Dangos pinnau';
+ } else if (this.innerHTML == 'Hide pins') {
+ YAHOO.util.Dom.setStyle('pins', 'display', 'none');
+ this.innerHTML = 'Show pins';
+ }
+});
+YAHOO.util.Event.addListener('all_pins_link', 'click', function(e) {
+ YAHOO.util.Event.preventDefault(e);
+ YAHOO.util.Dom.setStyle('pins', 'display', 'block');
+ var welsh = 0;
+ if (this.innerHTML == 'Include stale reports') {
+ this.innerHTML = 'Hide stale reports';
+ document.getElementById('all_pins').value = '1';
+ load_pins(fixmystreet.x, fixmystreet.y);
+ } else if (this.innerHTML == 'Cynnwys hen adroddiadau') {
+ this.innerHTML = 'Cuddio hen adroddiadau';
+ document.getElementById('all_pins').value = '1';
+ welsh = 1;
+ load_pins(fixmystreet.x, fixmystreet.y);
+ } else if (this.innerHTML == 'Cuddio hen adroddiadau') {
+ this.innerHTML = 'Cynnwys hen adroddiadau';
+ welsh = 1;
+ document.getElementById('all_pins').value = '';
+ load_pins(fixmystreet.x, fixmystreet.y);
+ } else if (this.innerHTML == 'Hide stale reports') {
+ this.innerHTML = 'Include stale reports';
+ document.getElementById('all_pins').value = '';
+ load_pins(fixmystreet.x, fixmystreet.y);
+ }
+ if (welsh) {
+ document.getElementById('hide_pins_link').innerHTML = 'Cuddio pinnau';
+ } else {
+ document.getElementById('hide_pins_link').innerHTML = 'Hide pins';
+ }
+});
+
+// I love the global
+var tile_x = 0;
+var tile_y = 0;
+
+var myAnim;
+function pan(x, y) {
+ if (!myAnim || !myAnim.isAnimated()) {
+ myAnim = new YAHOO.util.Motion('drag', { points:{by:[x,y]} }, 10, YAHOO.util.Easing.easeOut);
+ myAnim.useSeconds = false;
+ //myAnim.onTween.subscribe(function(){ update_tiles(x/10, y/10, false); });
+ myAnim.onComplete.subscribe(function(){
+ update_tiles(x, y, false);
+ cleanCache();
+ });
+ myAnim.animate();
+ }
+}
+
+var drag_x = 0;
+var drag_y = 0;
+function update_tiles(dx, dy, force) {
+ dx = getInt(dx); dy = getInt(dy);
+ if (!dx && !dy && !force) return;
+ var old_drag_x = drag_x;
+ var old_drag_y = drag_y;
+ drag_x += dx;
+ drag_y += dy;
+
+ var drag = document.getElementById('drag');
+ drag.style.left = drag_x + 'px';
+ drag.style.top = drag_y + 'px';
+
+ var horizontal = Math.floor(old_drag_x/fixmystreet.tilewidth) - Math.floor(drag_x/fixmystreet.tilewidth);
+ var vertical = Math.floor(old_drag_y/fixmystreet.tileheight) - Math.floor(drag_y/fixmystreet.tileheight);
+ if (!horizontal && !vertical && !force) return;
+ fixmystreet.x += horizontal;
+
+ tile_x += horizontal;
+ fixmystreet.y -= vertical;
+ tile_y += vertical;
+ var url = [ root_path + '/tilma/tileserver/' + fixmystreet.tile_type + '/', fixmystreet.x, '-', (fixmystreet.x+5), ',', fixmystreet.y, '-', (fixmystreet.y+5), '/JSON' ].join('');
+ YAHOO.util.Connect.asyncRequest('GET', url, {
+ success: urls_loaded, failure: urls_not_loaded,
+ argument: [tile_x, tile_y]
+ });
+
+ if (force) return;
+ load_pins(fixmystreet.x, fixmystreet.y);
+}
+
+function load_pins(x, y) {
+ if (document.getElementById('formX')) {
+ all_pins = '';
+ if (document.getElementById('all_pins')) {
+ all_pins = document.getElementById('all_pins').value;
+ }
+ var ajax_params = [ 'sx=' + document.getElementById('formX').value,
+ 'sy=' + document.getElementById('formY').value,
+ 'x=' + (x+3),
+ 'y=' + (y+3),
+ 'all_pins=' + all_pins ];
+
+ if (document.getElementById('extra_param')) {
+ ajax_params.push(document.getElementById('extra_param').name + '=' + document.getElementById('extra_param').value);
+ }
+
+ var url = [ root_path , '/ajax?', ajax_params.join(';')].join('');
+ YAHOO.util.Connect.asyncRequest('GET', url, {
+ success: pins_loaded
+ });
+ }
+}
+
+function pins_loaded(o) {
+ var data = eval(o.responseText);
+ document.getElementById('pins').innerHTML = data.pins;
+ if (typeof(data.current) != 'undefined')
+ document.getElementById('current').innerHTML = data.current;
+ if (typeof(data.current_near) != 'undefined')
+ document.getElementById('current_near').innerHTML = data.current_near;
+ if (typeof(data.fixed_near) != 'undefined')
+ document.getElementById('fixed_near').innerHTML = data.fixed_near;
+}
+
+function urls_not_loaded(o) { /* Nothing yet */ }
+
+// Load 6x6 grid of tiles around current 2x2
+function urls_loaded(o) {
+ var tiles = eval(o.responseText);
+ var drag = document.getElementById('drag');
+ for (var i=0; i<6; i++) {
+ var ii = (i + o.argument[1]);
+ for (var j=0; j<6; j++) {
+ if (tiles[i][j] == null) continue;
+ var jj = (j + o.argument[0]);
+ var id = [ 't', ii, '.', jj ].join('');
+ var xx = fixmystreet.x+j;
+ var yy = fixmystreet.y+5-i;
+ var img = document.getElementById(id);
+ if (img) {
+ if (!img.galleryimg) { img.galleryimg = false; }
+ img.onclick = drag_check;
+ tileCache[id] = { x: xx, y: yy, t: img };
+ continue;
+ }
+ img = cloneNode();
+ img.style.top = ((ii-2)*fixmystreet.tileheight) + 'px';
+ img.style.left = ((jj-2)*fixmystreet.tilewidth) + 'px';
+ img.name = [ 'tile_', xx, '.', yy ].join('')
+ img.id = id;
+ if (browser) {
+ img.style.visibility = 'hidden';
+ img.onload=function() { this.style.visibility = 'visible'; }
+ }
+ img.src = 'http://tilma.mysociety.org/tileserver/' + fixmystreet.tile_type + '/' + tiles[i][j];
+ tileCache[id] = { x: xx, y: yy, t: img };
+ drag.appendChild(img);
+ }
+ }
+}
+
+var imgElCache;
+function cloneNode() {
+ var img = null;
+ if (!imgElCache) {
+ var form = document.getElementById('mapForm');
+ if (form) {
+ img = imgElCache = document.createElement('input');
+ img.type = 'image';
+ } else {
+ img = imgElCache = document.createElement('img');
+ }
+ img.onclick = drag_check;
+ img.style.position = 'absolute';
+ img.style.width = fixmystreet.tilewidth + 'px';
+ img.style.height = fixmystreet.tileheight + 'px';
+ img.galleryimg = false;
+ img.alt = 'Loading...';
+ } else {
+ img = imgElCache.cloneNode(true);
+ }
+ return img;
+}
+
+var tileCache=[];
+function cleanCache() {
+ for (var i in tileCache) {
+ if (tileCache[i].x < fixmystreet.x || tileCache[i].x > fixmystreet.x+5 || tileCache[i].y < fixmystreet.y || tileCache[i].y > fixmystreet.y+5) {
+ var t = tileCache[i].t;
+ t.parentNode.removeChild(t); // de-leak?
+ delete tileCache[i];
+ }
+ }
+}
+
+/* Called every mousemove, so on first call, overwrite itself with quicker version */
+function get_posn(ev) {
+ var posx, posy;
+ if (ev.pageX || ev.pageY) {
+ get_posn = function(e) {
+ return { x: e.pageX, y: e.pageY };
+ };
+ } else if (ev.clientX || ev.clientY) {
+ get_posn = function(e) {
+ return {
+ x: e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
+ y: e.clientY + document.body.scrollTop + document.documentElement.scrollTop
+ };
+ };
+ } else {
+ get_posn = function(e) {
+ return { x: undef, y: undef };
+ };
+ }
+ return get_posn(ev);
+}
+
+function setCursor(s) {
+ var drag = document.getElementById('drag');
+ var inputs = drag.getElementsByTagName('input');
+ for (var i=0; i<inputs.length; i++) {
+ inputs[i].style.cursor = s;
+ }
+}
+
+var in_drag = false;
+function drag_check(e) {
+ if (in_drag) {
+ in_drag = false;
+ return false;
+ }
+ return true;
+}
+
+/* Simpler version of DDProxy */
+var mouse_pos = {};
+YAHOO.util.DDMap = function(id, sGroup, config) {
+ this.init(id, sGroup, config);
+};
+YAHOO.extend(YAHOO.util.DDMap, YAHOO.util.DD, {
+ scroll: false,
+ b4MouseDown: function(e) { },
+ startDrag: function(x, y) {
+ mouse_pos = { x: x, y: y };
+ setCursor('move');
+ in_drag = true;
+ },
+ b4Drag: function(e) { },
+ onDrag: function(e) {
+ var point = get_posn(e);
+ if (point == mouse_pos) return false;
+ var dx = point.x-mouse_pos.x;
+ var dy = point.y-mouse_pos.y;
+ mouse_pos = point;
+ update_tiles(dx, dy, false);
+ },
+ endDrag: function(e) {
+ setCursor('crosshair');
+ cleanCache();
+ },
+ toString: function() {
+ return ("DDMap " + this.id);
+ }
+});
+
+var browser = 1;
+var ua=navigator.userAgent.toLowerCase();
+if (!/opera|safari|gecko/.test(ua) && typeof document.all!='undefined')
+ browser=0;
+
+function getInt(n) {
+ n = parseInt(n); return (isNaN(n) ? 0 : n);
+}
+
diff --git a/web/questionnaire.cgi b/web/questionnaire.cgi
index 1da410b80..33e823560 100755
--- a/web/questionnaire.cgi
+++ b/web/questionnaire.cgi
@@ -10,6 +10,7 @@
use strict;
use Standard;
+use Utils;
use Error qw(:try);
use CrossSell;
use mySociety::AuthToken;
@@ -213,17 +214,19 @@ sub display_questionnaire {
return $error;
}
my $reported_date_time = Page::prettify_epoch($q, $problem->{time});
- my ($x, $y, $x_tile, $y_tile, $px, $py) = Page::os_to_px_with_adjust($q, $problem->{easting}, $problem->{northing}, undef, undef);
-
- my $pins = Page::display_pin($q, $px, $py, $problem->{state} eq 'fixed'?'green':'red');
my $problem_text = Page::display_problem_text($q, $problem);
my $updates = Page::display_problem_updates($problem->{id}, $q);
my %vars = (
input_h => \%input_h,
- map_start => Page::display_map($q, x => $x_tile, y => $y_tile, pins => $pins,
- px => $px, py => $py, pre => $problem_text, post => $updates ),
- map_end => Page::display_map_end(0),
+ map_start => FixMyStreet::Map::display_map($q,
+ easting => $problem->{easting}, northing => $problem->{northing},
+ pins => [
+ [ $problem->{easting}, $problem->{northing}, $problem->{state} eq 'fixed'?'green':'red' ],
+ ],
+ pre => $problem_text, post => $updates
+ ),
+ map_end => FixMyStreet::Map::display_map_end(0),
heading => _('Questionnaire'),
yes => _('Yes'),
no => _('No'),
diff --git a/web/reports.cgi b/web/reports.cgi
index 4faee63f9..fe4213be4 100755
--- a/web/reports.cgi
+++ b/web/reports.cgi
@@ -12,7 +12,7 @@
use strict;
use Standard;
use URI::Escape;
-use mySociety::Alert;
+use FixMyStreet::Alert;
use mySociety::MaPit;
use mySociety::Web qw(ent NewURL);
use mySociety::VotingArea;
@@ -117,7 +117,7 @@ sub main {
}
print $q->header( -type => 'application/xml; charset=utf-8' );
my $xsl = Cobrand::feed_xsl($cobrand);
- my $out = mySociety::Alert::generate_rss($type, $xsl, "/$url", \@params, \%title_params, $cobrand, $q);
+ my $out = FixMyStreet::Alert::generate_rss($type, $xsl, "/$url", \@params, \%title_params, $cobrand, $q);
$out =~ s/matthew.fixmystreet/emptyhomes.matthew.fixmystreet/g if $q->{site} eq 'emptyhomes';
print $out;
return;
diff --git a/web/robots.txt b/web/robots.txt
index e69de29bb..53675186c 100644
--- a/web/robots.txt
+++ b/web/robots.txt
@@ -0,0 +1,5 @@
+User-agent: msnbot
+Crawl-delay: 3
+
+User-agent: bingbot
+Crawl-delay: 3
diff --git a/web/rss.cgi b/web/rss.cgi
index a2d9bcb4f..a716d93e9 100755
--- a/web/rss.cgi
+++ b/web/rss.cgi
@@ -12,7 +12,8 @@ use strict;
use Error qw(:try);
use Standard;
use URI::Escape;
-use mySociety::Alert;
+use FixMyStreet::Alert;
+use FixMyStreet::Geocode;
use mySociety::MaPit;
use mySociety::GeoUtil;
use mySociety::Gaze;
@@ -34,20 +35,20 @@ sub main {
return;
}
my $qs = 'report/' . $id;
- $out = mySociety::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand, $q);
+ $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand, $q);
} elsif ($type eq 'new_problems' || $type eq 'new_fixed_problems') {
- $out = mySociety::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
+ $out = FixMyStreet::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
} elsif ($type eq 'council_problems') {
my $id = $q->param('id');
my $qs = '/'.$id;
- $out = mySociety::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand. $q);
+ $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], undef, $cobrand. $q);
} elsif ($type eq 'area_problems') {
my $id = $q->param('id');
my $va_info = mySociety::MaPit::call('area', $id);
my $qs = '/'.$id;
- $out = mySociety::Alert::generate_rss($type, $xsl, $qs, [$id], { NAME => $va_info->{name} }, $cobrand, $q);
+ $out = FixMyStreet::Alert::generate_rss($type, $xsl, $qs, [$id], { NAME => $va_info->{name} }, $cobrand, $q);
} elsif ($type eq 'all_problems') {
- $out = mySociety::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
+ $out = FixMyStreet::Alert::generate_rss($type, $xsl, '', undef, undef, $cobrand, $q);
} else {
my $base = mySociety::Config::get('BASE_URL');
print $q->redirect($base . '/alert');
@@ -71,6 +72,13 @@ sub rss_local_problems {
$d = '' unless $d =~ /^\d+$/;
my $d_str = '';
$d_str = "/$d" if $d;
+ my $state = $q->param('state') || 'all';
+ $state = 'all' unless $state =~ /^(all|open|fixed)$/;
+
+ # state is getting lost in the redirects. Add it on to the end as a query
+ my $state_qs = "?state=$state" unless $state eq 'all';
+
+ $state = 'confirmed' if $state eq 'open';
my $cobrand = Page::get_cobrand($q);
my $base = Cobrand::base_url($cobrand);
@@ -78,33 +86,31 @@ sub rss_local_problems {
($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");
+ print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
return '';
} elsif ($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");
+ print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
return '';
} elsif ($e && $n) {
- $x = int(Page::os_to_tile($e));
- $y = int(Page::os_to_tile($n));
($lat, $lon) = mySociety::GeoUtil::national_grid_to_wgs84($e, $n, 'G');
} elsif ($pc) {
my $error;
try {
- ($x, $y, $e, $n, $error) = Page::geocode($pc, $q);
+ ($e, $n, $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");
+ print $q->redirect(-location => "$base/rss/n/$e,$n$d_str$state_qs");
}
return '';
} else {
die "Missing E/N, x/y, lat/lon, or postcode parameter in RSS feed";
}
- my $qs = "?x=$x;y=$y";
+ my $qs = '?e=' . int($e) . ';n=' . int($n);
if ($d) {
$qs .= ";d=$d";
$d = 100 if $d > 100;
@@ -114,6 +120,10 @@ sub rss_local_problems {
}
my $xsl = Cobrand::feed_xsl($cobrand);
- return mySociety::Alert::generate_rss('local_problems', $xsl, $qs, [$e, $n, $d], undef, $cobrand, $q);
+ if ($state eq 'all') {
+ return FixMyStreet::Alert::generate_rss('local_problems', $xsl, $qs, [$e, $n, $d], undef, $cobrand, $q);
+ } else {
+ return FixMyStreet::Alert::generate_rss('local_problems_state', $xsl, $qs, [$e, $n, $d, $state], undef, $cobrand, $q);
+ }
}
diff --git a/web/tms-signup.cgi b/web/tms-signup.cgi
index 2bafd1543..44099417f 100755
--- a/web/tms-signup.cgi
+++ b/web/tms-signup.cgi
@@ -12,7 +12,6 @@ use strict;
use Standard;
use Digest::SHA1 qw(sha1_hex);
use CrossSell;
-use mySociety::Alert;
use mySociety::AuthToken;
use mySociety::Config;
use mySociety::EmailUtil qw(is_valid_email);