# Installing compiled shared library database functions (optional)
-There are special database functions required by a (little-used) API call, the migrations and diff replication. The former two are provided as *either* pure SQL functions or a compiled shared library. The SQL versions are installed as part of the recommended install procedure above and the shared library versions are recommended only if you are running a production server making a lot of `/changes` API calls or need the diff replication functionality.
+There are special database functions required by a (little-used) API call, the migrations and diff replication. The former two are provided as *either* pure SQL functions or a compiled shared library. The SQL versions are installed as part of the recommended install procedure above and the shared library versions are recommended only if you are running a production server and need the diff replication functionality.
If you aren't sure which you need, stick with the SQL versions.
If you previously installed the SQL versions of these functions, we'll need to delete those before adding the new ones:
```
-psql -d openstreetmap -c "DROP FUNCTION IF EXISTS maptile_for_point"
psql -d openstreetmap -c "DROP FUNCTION IF EXISTS tile_for_point"
```
Then we create the functions within each database. We're using `pwd` to substitute in the current working directory, since PostgreSQL needs the full path.
```
-psql -d openstreetmap -c "CREATE FUNCTION maptile_for_point(int8, int8, int4) RETURNS int4 AS '`pwd`/db/functions/libpgosm', 'maptile_for_point' LANGUAGE C STRICT"
psql -d openstreetmap -c "CREATE FUNCTION tile_for_point(int4, int4) RETURNS int8 AS '`pwd`/db/functions/libpgosm', 'tile_for_point' LANGUAGE C STRICT"
psql -d openstreetmap -c "CREATE FUNCTION xid_to_int4(xid) RETURNS int4 AS '`pwd`/db/functions/libpgosm', 'xid_to_int4' LANGUAGE C STRICT"
```
.welcome {
display: none;
- padding-bottom: 5px;
p {
- padding: $lineheight/2 $lineheight $lineheight;
font-size: 110%;
font-weight: 300;
}
-
- .button {
- width: 50%;
- float: left;
- margin: 0;
- border-radius: 0;
- font-weight: normal;
- padding: .6em;
-
- &.learn-more {
- border-right: 1px solid #fff;
- }
- }
}
#banner {
.export_area_inputs {
margin-bottom: $lineheight/2;
input[type="text"] {
- width: 80px;
+ width: 100px;
text-align: center;
- margin-bottom: 5px;
}
}
float: right;
/* no-r2 */ margin-right: -1px;
}
- #minlat { margin-bottom: 0; }
+ #minlat { margin-bottom: -1px; }
}
.export_bound {
margin: $lineheight/4;
}
- .export_button {
- margin-top: $lineheight;
- margin-bottom: $lineheight;
- }
-
dl {
padding-left: $lineheight/2;
dd {
+++ /dev/null
-module Api
- class ChangesController < ApiController
- authorize_resource :class => false
-
- before_action :check_api_readable
- around_action :api_call_handle_error, :api_call_timeout
-
- # Get a list of the tiles that have changed within a specified time
- # period
- def index
- zoom = (params[:zoom] || "12").to_i
-
- if params.include?(:start) && params.include?(:end)
- starttime = Time.parse(params[:start])
- endtime = Time.parse(params[:end])
- else
- hours = (params[:hours] || "1").to_i.hours
- endtime = Time.now.getutc
- starttime = endtime - hours
- end
-
- if zoom >= 1 && zoom <= 16 &&
- endtime > starttime && endtime - starttime <= 24.hours
- mask = (1 << zoom) - 1
-
- tiles = Node.where(:timestamp => starttime..endtime).group("maptile_for_point(latitude, longitude, #{zoom})").count
-
- doc = OSM::API.new.get_xml_doc
- changes = XML::Node.new "changes"
- changes["starttime"] = starttime.xmlschema
- changes["endtime"] = endtime.xmlschema
-
- tiles.each do |tile, count|
- x = (tile.to_i >> zoom) & mask
- y = tile.to_i & mask
-
- t = XML::Node.new "tile"
- t["x"] = x.to_s
- t["y"] = y.to_s
- t["z"] = zoom.to_s
- t["changes"] = count.to_s
-
- changes << t
- end
-
- doc.root << changes
-
- render :xml => doc.to_s
- else
- render :plain => "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", :status => :bad_request
- end
- end
- end
-end
<% unless current_user %>
<div class="welcome">
<%= render "sidebar_header", :title => t("layouts.intro_header") %>
- <p><%= t "layouts.intro_text" %></p>
- <p><%= t "layouts.hosting_partners_html",
- :ucl => link_to(t("layouts.partners_ucl"), "https://www.ucl.ac.uk"),
- :bytemark => link_to(t("layouts.partners_bytemark"), "https://www.bytemark.co.uk"),
- :partners => link_to(t("layouts.partners_partners"), "https://hardware.openstreetmap.org/thanks/") %>
- </p>
- <div class="standard-form">
- <a class="button learn-more" href="<%= about_path %>"><%= t("layouts.learn_more") %></a>
- <a class="button sign-up" href="<%= user_new_path %>"><%= t("layouts.start_mapping") %></a>
+ <div class="px-3 pb-3">
+ <p><%= t "layouts.intro_text" %></p>
+ <p><%= t "layouts.hosting_partners_html",
+ :ucl => link_to(t("layouts.partners_ucl"), "https://www.ucl.ac.uk"),
+ :bytemark => link_to(t("layouts.partners_bytemark"), "https://www.bytemark.co.uk"),
+ :partners => link_to(t("layouts.partners_partners"), "https://hardware.openstreetmap.org/thanks/") %>
+ </p>
+ <div class="d-flex mx-n1">
+ <div class="w-50 px-1">
+ <a class="btn btn-primary w-100" href="<%= about_path %>"><%= t("layouts.learn_more") %></a>
+ </div>
+ <div class="w-50 px-1">
+ <a class="btn btn-primary w-100" href="<%= user_new_path %>"><%= t("layouts.start_mapping") %></a>
+ </div>
+ </div>
</div>
</div>
<% end %>
-<tr id="inbox-<%= message_summary.id %>" class="standard-form inbox-row<%= "-unread" unless message_summary.message_read? %>">
+<tr id="inbox-<%= message_summary.id %>" class="inbox-row<%= "-unread" unless message_summary.message_read? %>">
<td class="inbox-sender"><%= link_to message_summary.sender.display_name, user_path(message_summary.sender) %></td>
<td class="inbox-subject"><%= link_to message_summary.title, message_path(message_summary) %></td>
<td class="inbox-sent"><%= l message_summary.sent_on, :format => :friendly %></td>
- <td class="inbox-mark-unread"><%= button_to t(".unread_button"), message_mark_path(message_summary, :mark => "unread"), :remote => true %></td>
- <td class="inbox-mark-read"><%= button_to t(".read_button"), message_mark_path(message_summary, :mark => "read"), :remote => true %></td>
- <td class="inbox-destroy"><%= button_to t(".destroy_button"), message_path(message_summary, :referer => request.fullpath), :method => :delete, :remote => true %></td>
+ <td class="inbox-mark-unread"><%= button_to t(".unread_button"), message_mark_path(message_summary, :mark => "unread"), :remote => true, :class => "btn btn-sm btn-primary" %></td>
+ <td class="inbox-mark-read"><%= button_to t(".read_button"), message_mark_path(message_summary, :mark => "read"), :remote => true, :class => "btn btn-sm btn-primary" %></td>
+ <td class="inbox-destroy"><%= button_to t(".destroy_button"), message_path(message_summary, :referer => request.fullpath), :method => :delete, :remote => true, :class => "btn btn-sm btn-danger" %></td>
</tr>
<td class="inbox-sender"><%= link_to sent_message_summary.recipient.display_name, user_path(sent_message_summary.recipient) %></td>
<td class="inbox-subject"><%= link_to sent_message_summary.title, message_path(sent_message_summary) %></td>
<td class="inbox-sent"><%= l sent_message_summary.sent_on, :format => :friendly %></td>
- <td class="inbox-destroy"><%= button_to t(".destroy_button"), message_path(sent_message_summary, :referer => request.fullpath), :method => :delete, :remote => true %></td>
+ <td class="inbox-destroy"><%= button_to t(".destroy_button"), message_path(sent_message_summary, :referer => request.fullpath), :method => :delete, :remote => true, :class => "btn btn-sm btn-danger" %></td>
</tr>
<td><%= link_to token.client_application.name, token.client_application.url %></td>
<td><%= token.authorized_at %></td>
<td>
- <%= form_tag({ :controller => "oauth", :action => "revoke" }, { :class => "standard-form" }) do %>
+ <%= form_tag({ :controller => "oauth", :action => "revoke" }) do %>
<%= hidden_field_tag "token", token.token %>
- <%= submit_tag t(".revoke") %>
+ <%= submit_tag t(".revoke"), :class => "btn btn-sm btn-primary" %>
<% end %>
</td>
</tr>
<%= render "sidebar_header", :title => t(".title") %>
-<%= form_tag({ :controller => "export", :action => "finish" }, { :class => "export_form standard-form" }) do %>
+<%= form_tag({ :controller => "export", :action => "finish" }, { :class => "export_form" }) do %>
<%= hidden_field_tag "format", "osm" %>
<div class='export_area_inputs'>
<div class='export_boxy'>
- <%= text_field_tag("maxlat", nil, :size => 10, :class => "export_bound") %>
- <br />
- <%= text_field_tag("minlon", nil, :size => 10, :class => "export_bound") %>
- <%= text_field_tag("maxlon", nil, :size => 10, :class => "export_bound") %>
- <br /><br />
- <%= text_field_tag("minlat", nil, :size => 10, :class => "export_bound") %>
+ <%= text_field_tag("maxlat", nil, :size => 10, :class => "export_bound form-control mx-auto") %>
+ <div class="clearfix">
+ <%= text_field_tag("minlon", nil, :size => 10, :class => "export_bound form-control") %>
+ <%= text_field_tag("maxlon", nil, :size => 10, :class => "export_bound form-control") %>
+ </div>
+ <%= text_field_tag("minlat", nil, :size => 10, :class => "export_bound form-control mx-auto") %>
</div>
<a id='drag_box' href="#"><%= t ".manually_select" %></a>
</div>
</div>
<div id="export_commit">
- <div class="export_button">
- <%= submit_tag t(".export_button") %>
+ <div class="form-group d-flex">
+ <%= submit_tag t(".export_button"), :class => "btn btn-primary mx-auto" %>
</div>
<p><%= t ".too_large.advice" %></p>
<% end %>
<% if @user_block.ends_at > Time.now %>
-<p><b>
- <%= t(".time_future", :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %>
-</b></p>
+ <p>
+ <%= t(".time_future", :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %>
+ </p>
-<%= form_for :revoke, :url => { :action => "revoke" }, :html => { :class => "standard-form" } do |f| %>
- <%= f.error_messages %>
-<p>
- <%= check_box_tag "confirm", "yes" %>
- <%= label_tag "confirm", t(".confirm") %>
-</p>
-<p>
- <%= submit_tag t(".revoke") %>
-</p>
-<% end %>
+ <%= bootstrap_form_for :revoke, :url => { :action => "revoke" } do |f| %>
+ <div class="form-group">
+ <div class="form-check">
+ <%= check_box_tag "confirm", "yes", false, { :class => "form-check-input" } %>
+ <%= label_tag "confirm", t(".confirm"), { :class => "form-check-label" } %>
+ </div>
+ </div>
+
+ <%= f.primary t(".revoke") %>
+ <% end %>
<% else %>
-<p>
- <%= t(".past", :time => time_ago_in_words(@user_block.ends_at, :scope => :'datetime.distance_in_words_ago')) %>
-</p>
+ <p>
+ <%= t(".past", :time => time_ago_in_words(@user_block.ends_at, :scope => :'datetime.distance_in_words_ago')) %>
+ </p>
<% end %>
<%= f.text_field :home_lon, :wrapper_class => "col-sm-4", :id => "home_lon" %>
</div>
</div>
- <div class="standard-form-row">
- <input type="checkbox" name="updatehome" value="1" <% unless current_user.home_lat and current_user.home_lon %> checked="checked" <% end %> id="updatehome" />
- <label class="standard-label" for="updatehome"><%= t ".update home location on click" %></label>
+ <div class="form-check">
+ <input class="form-check-input" type="checkbox" name="updatehome" value="1" <% unless current_user.home_lat and current_user.home_lon %> checked="checked" <% end %> id="updatehome" />
+ <label class="form-check-label" for="updatehome"><%= t ".update home location on click" %></label>
</div>
<%= tag.div "", :id => "map", :class => "content_map set_location" %>
</fieldset>
get "trackpoints" => "api/tracepoints#index"
- get "changes" => "api/changes#index"
-
get "user/:id" => "api/users#show", :id => /\d+/, :as => :api_user
get "user/details" => "api/users#details"
get "user/gpx_files" => "api/users#gpx_files"
clean:
$(RM) ${DESTDIR}/*.so ${DESTDIR}/*.o
-${DESTDIR}/libpgosm.so: ${DESTDIR}/quadtile.o ${DESTDIR}/maptile.o ${DESTDIR}/xid_to_int4.o
+${DESTDIR}/libpgosm.so: ${DESTDIR}/quadtile.o ${DESTDIR}/xid_to_int4.o
cc ${LDFLAGS} -o $@ $^
${DESTDIR}/%.o: %.c
$$ LANGUAGE plpgsql IMMUTABLE;
--- maptile_for_point returns an integer representing the tile at the given zoom
--- which contains the point (scaled_lon, scaled_lat). Note that the arguments
--- are in the order (lat, lon), and should be scaled by 10^7.
---
--- The maptile_for_point function is used only for grouping the results of the
--- (deprecated?) /changes API call. Please don't use it for anything else, as
--- it might go away in the future.
-CREATE OR REPLACE FUNCTION maptile_for_point(scaled_lat int8, scaled_lon int8, zoom int4)
- RETURNS int4
- AS $$
-DECLARE
- lat CONSTANT DOUBLE PRECISION := scaled_lat / 10000000.0;
- lon CONSTANT DOUBLE PRECISION := scaled_lon / 10000000.0;
- zscale CONSTANT DOUBLE PRECISION := 2.0 ^ zoom;
- pi CONSTANT DOUBLE PRECISION := 3.141592653589793;
- r_per_d CONSTANT DOUBLE PRECISION := pi / 180.0;
- x int4;
- y int4;
-BEGIN
- -- straight port of the C code. see db/functions/maptile.c
- x := floor((lon + 180.0) * zscale / 360.0);
- y := floor((1.0 - ln(tan(lat * r_per_d) + 1.0 / cos(lat * r_per_d)) / pi) * zscale / 2.0);
-
- RETURN (x << zoom) | y;
-END;
-$$ LANGUAGE plpgsql IMMUTABLE;
-
-- xid_to_int4 converts a PostgreSQL transaction ID (xid) to a 32-bit integer
-- which can then be used to efficiently find rows which have changed between
-- two given transactions. This is currently used by Osmosis to extract a
+++ /dev/null
-#include <math.h>
-#include <postgres.h>
-#include <fmgr.h>
-
-Datum
-maptile_for_point(PG_FUNCTION_ARGS)
-{
- double lat = PG_GETARG_INT64(0) / 10000000.0;
- double lon = PG_GETARG_INT64(1) / 10000000.0;
- int zoom = PG_GETARG_INT32(2);
- double scale = pow(2, zoom);
- double r_per_d = M_PI / 180;
- unsigned int x;
- unsigned int y;
-
- x = floor((lon + 180.0) * scale / 360.0);
- y = floor((1 - log(tan(lat * r_per_d) + 1.0 / cos(lat * r_per_d)) / M_PI) * scale / 2.0);
-
- PG_RETURN_INT32((x << zoom) | y);
-}
-
-PG_FUNCTION_INFO_V1(maptile_for_point);
-
-/*
- * To bind this into PGSQL, try something like:
- *
- * CREATE FUNCTION maptile_for_point(int8, int8, int4) RETURNS int4
- * AS '/path/to/rails-port/db/functions/libpgosm', 'maptile_for_point'
- * LANGUAGE C STRICT;
- *
- * (without all the *s)
- */
-
-#ifdef PG_MODULE_MAGIC
-PG_MODULE_MAGIC;
-#endif
);
---
--- Name: maptile_for_point(bigint, bigint, integer); Type: FUNCTION; Schema: public; Owner: -
---
-
-CREATE FUNCTION public.maptile_for_point(scaled_lat bigint, scaled_lon bigint, zoom integer) RETURNS integer
- LANGUAGE plpgsql IMMUTABLE
- AS $$
-DECLARE
- lat CONSTANT DOUBLE PRECISION := scaled_lat / 10000000.0;
- lon CONSTANT DOUBLE PRECISION := scaled_lon / 10000000.0;
- zscale CONSTANT DOUBLE PRECISION := 2.0 ^ zoom;
- pi CONSTANT DOUBLE PRECISION := 3.141592653589793;
- r_per_d CONSTANT DOUBLE PRECISION := pi / 180.0;
- x int4;
- y int4;
-BEGIN
- -- straight port of the C code. see db/functions/maptile.c
- x := floor((lon + 180.0) * zscale / 360.0);
- y := floor((1.0 - ln(tan(lat * r_per_d) + 1.0 / cos(lat * r_per_d)) / pi) * zscale / 2.0);
-
- RETURN (x << zoom) | y;
-END;
-$$;
-
-
--
-- Name: tile_for_point(integer, integer); Type: FUNCTION; Schema: public; Owner: -
--
################################################################################
#pushd db/functions
#sudo -u vagrant make
-#sudo -u vagrant psql openstreetmap -c "CREATE OR REPLACE FUNCTION maptile_for_point(int8, int8, int4) RETURNS int4 AS '/srv/openstreetmap-website/db/functions/libpgosm.so', 'maptile_for_point' LANGUAGE C ST#RICT"
#sudo -u vagrant psql openstreetmap -c "CREATE OR REPLACE FUNCTION tile_for_point(int4, int4) RETURNS int8 AS '/srv/openstreetmap-website/db/functions/libpgosm.so', 'tile_for_point' LANGUAGE C STRICT"
#sudo -u vagrant psql openstreetmap -c "CREATE OR REPLACE FUNCTION xid_to_int4(xid) RETURNS int4 AS '/srv/openstreetmap-website/db/functions/libpgosm.so', 'xid_to_int4' LANGUAGE C STRICT"
#popd
+++ /dev/null
-require "test_helper"
-
-module Api
- class ChangesControllerTest < ActionDispatch::IntegrationTest
- ##
- # test all routes which lead to this controller
- def test_routes
- assert_routing(
- { :path => "/api/0.6/changes", :method => :get },
- { :controller => "api/changes", :action => "index" }
- )
- end
-
- # MySQL and Postgres require that the C based functions are installed for
- # this test to work. More information is available from:
- # http://wiki.openstreetmap.org/wiki/Rails#Installing_the_quadtile_functions
- # or by looking at the readme in db/README
- def test_changes_simple
- # create a selection of nodes
- (1..5).each do |n|
- create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => n, :lon => n)
- end
- # deleted nodes should also be counted
- create(:node, :deleted, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6)
- # nodes in the same tile won't change the total
- create(:node, :timestamp => Time.utc(2007, 1, 1, 0, 0, 0), :lat => 6, :lon => 6)
- # nodes with a different timestamp should be ignored
- create(:node, :timestamp => Time.utc(2008, 1, 1, 0, 0, 0), :lat => 7, :lon => 7)
-
- travel_to Time.utc(2010, 4, 3, 10, 55, 0) do
- get changes_path
- assert_response :success
- now = Time.now.getutc
- hourago = now - 1.hour
- assert_select "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", :count => 1 do
- assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do
- assert_select "tile", :count => 0
- end
- end
- end
-
- travel_to Time.utc(2007, 1, 1, 0, 30, 0) do
- get changes_path
- assert_response :success
- # print @response.body
- # As we have loaded the fixtures, we can assume that there are some
- # changes at the time we have frozen at
- now = Time.now.getutc
- hourago = now - 1.hour
- assert_select "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", :count => 1 do
- assert_select "changes[starttime='#{hourago.xmlschema}'][endtime='#{now.xmlschema}']", :count => 1 do
- assert_select "tile", :count => 6
- end
- end
- end
- end
-
- def test_changes_zoom_invalid
- zoom_to_test = %w[p -1 0 17 one two]
- zoom_to_test.each do |zoom|
- get changes_path(:zoom => zoom)
- assert_response :bad_request
- assert_equal("Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", @response.body)
- end
- end
-
- def test_changes_zoom_valid
- 1.upto(16) do |zoom|
- get changes_path(:zoom => zoom)
- assert_response :success
- # NOTE: there was a test here for the timing, but it was too sensitive to be a good test
- # and it was annoying.
- assert_select "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", :count => 1 do
- assert_select "changes", :count => 1
- end
- end
- end
-
- def test_changes_hours_invalid
- invalid = %w[-21 335 -1 0 25 26 100 one two three ping pong :]
- invalid.each do |hour|
- get changes_path(:hours => hour)
- assert_response :bad_request, "Problem with the hour: #{hour}"
- assert_equal("Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", @response.body, "Problem with the hour: #{hour}.")
- end
- end
-
- def test_changes_hours_valid
- 1.upto(24) do |hour|
- get changes_path(:hours => hour)
- assert_response :success
- end
- end
-
- def test_changes_start_end_invalid
- get changes_path(:start => "2010-04-03 10:55:00", :end => "2010-04-03 09:55:00")
- assert_response :bad_request
- assert_equal("Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", @response.body)
- end
-
- def test_changes_start_end_valid
- get changes_path(:start => "2010-04-03 09:55:00", :end => "2010-04-03 10:55:00")
- assert_response :success
- end
- end
-end