Skip to content

Commit

Permalink
Add new relations section
Browse files Browse the repository at this point in the history
There is a whole new relations section which gives information about different
relation types. There is a list of all relation types and there is a page for
each relation type which currently contains an empty overview tab and a roles
tab with a list of roles used with this relation type. There is currently no
link that leads to this section as it is not finished yet.
  • Loading branch information
joto committed Jan 14, 2013
1 parent 4298e37 commit 9f39838
Show file tree
Hide file tree
Showing 11 changed files with 409 additions and 2 deletions.
31 changes: 31 additions & 0 deletions web/i18n/en.yml
Expand Up @@ -12,6 +12,15 @@ osm:
ways: Ways
relation: Relation
relations: Relations
relation_type: Relation type
relation_types: Relation types
relation_member: Relation member
relation_members: Relation members
relation_member_nodes: Member nodes
relation_member_ways: Member ways
relation_member_relations: Member relations
relation_member_role: Role
relation_member_roles: Roles
object: Object
objects: Objects
all: All
Expand All @@ -33,6 +42,7 @@ taginfo:
test: Test
map: Map
maps: Maps
relations: Relations
combinations: Combinations
key_combinations: Combinations
overview: Overview
Expand Down Expand Up @@ -130,6 +140,15 @@ pages:
tags:
intro: |
This table shows the most common tags in the database.
relations:
name: Relation types
intro: Information about the different types of relations (indicated by the <a href="/keys/type?filter=relations">type</a> tag).
relations_of_type_tooltip: Number of relations of this type (and as percentage of all relations).
prevalent_roles: Prevalent roles
prevalent_roles_tooltip: Prevalent roles for this type of relation.
no_information: No information
roles_less_than_one_percent: no roles with more than 1%
empty_role: empty role
key:
description_from_wiki: Description of this key from the wiki (if available in your chosen language, otherwise in English).
no_description_in_wiki: No description for this key in the wiki.
Expand Down Expand Up @@ -197,6 +216,18 @@ pages:
choice: |
Choose style:
no_styles: No JOSM styles for this tag.
relation:
name: Relation type
overview:
tab: Overview
title: Overview
roles:
tab: Roles
title: Member roles
objects_tooltip: Relation members with this role (and as percentage of all members) for this relation type.
nodes_tooltip: Relation members of type node with this role (and as percentage of all members of type node) for this relation type.
ways_tooltip: Relation members of type way with this role (and as percentage of all members of type way) for this relation type.
relations_tooltip: Relation members of type relation with this role (and as percentage of all members of type relation) for this relation type.

flexigrid:
pagetext: Page
Expand Down
71 changes: 71 additions & 0 deletions web/lib/api/v4/relation.rb
@@ -0,0 +1,71 @@
# web/lib/api/v4/relation.rb

class Taginfo < Sinatra::Base

api(4, 'relation/roles', {
:description => 'Member role statistics for a relation of given type.',
:parameters => {
:type => 'Relation type (required).',
:query => 'Only show results where the role matches this query (substring match, optional).'
},
:paging => :optional,
:sort => %w( role count_all_members count_node_members count_way_members count_relation_members ),
:result => paging_results([
[:relation_type, :STRING, 'Relation type'],
[:role, :STRING, 'Relation member role.'],
[:count_all_members, :INT, 'Number of members with this role.'],
[:count_all_members_fraction, :FLOAT, 'Number of members with this role devided by all members.'],
[:count_node_members, :INT, 'Number of members of type node with this role.'],
[:count_node_members_fraction, :FLOAT, 'Number of members of type node with this role devided by all members of type node.'],
[:count_way_members, :INT, 'Number of members of type way with this role.'],
[:count_way_members_fraction, :FLOAT, 'Number of members of type way with this role devided by all members of type way.'],
[:count_relation_members, :INT, 'Number of members of type relation with this role.'],
[:count_relation_members_fraction, :FLOAT, 'Number of members of type relation with this role devided by all members of type relation.']
]),
:example => { :role => 'multipolygon', :page => 1, :rp => 10 },
:ui => '/reports/relation_types#roles'
}) do
rtype = params[:type]

relation_type_info = @db.select('SELECT * FROM relation_types').
condition("rtype=?", rtype).
execute()[0]

total = @db.count('relation_roles').
condition("rtype=?", rtype).
condition_if("role LIKE '%' || ? || '%'", params[:query]).
get_first_value().to_i

res = @db.select('SELECT * FROM relation_roles').
condition("rtype=?", rtype).
condition_if("role LIKE '%' || ? || '%'", params[:query]).
order_by(@ap.sortname, @ap.sortorder) { |o|
o.role
o.count_all_members :count_all
o.count_node_members :count_nodes
o.count_way_members :count_ways
o.count_relation_members :count_relations
}.
paging(@ap).
execute()

return {
:page => @ap.page,
:rp => @ap.results_per_page,
:total => total,
:data => res.map{ |row| {
:relation_type => row['rtype'],
:role => row['role'],
:count_all_members => row['count_all'].to_i,
:count_all_members_fraction => (row['count_all'].to_f / relation_type_info['members_all'].to_i).round_to(4),
:count_node_members => row['count_nodes'].to_i,
:count_node_members_fraction => (row['count_nodes'].to_f / relation_type_info['members_nodes'].to_i).round_to(4),
:count_way_members => row['count_ways'].to_i,
:count_way_members_fraction => (row['count_ways'].to_f / relation_type_info['members_ways'].to_i).round_to(4),
:count_relation_members => row['count_relations'].to_i,
:count_relation_members_fraction => (row['count_relations'].to_f / relation_type_info['members_relations'].to_i).round_to(4),
} }
}.to_json
end

end
72 changes: 72 additions & 0 deletions web/lib/api/v4/relations.rb
@@ -0,0 +1,72 @@
# web/lib/api/v4/relations.rb

class Taginfo < Sinatra::Base

api(4, 'relations/all', {
:description => 'Information about the different relation types.',
:parameters => {
:query => 'Only show results where the relation type matches this query (substring match, optional).'
},
:paging => :optional,
:sort => %w( relation_type count ),
:result => paging_results([
[:relation_type, :STRING, 'Relation type'],
[:count, :INT, 'Number of relations with this type.'],
[:count_fraction, :INT, 'Number of relations with this type divided by the overall number of relations.'],
[:prevalent_roles, :ARRAY, 'Prevalent member roles.', [
[:role, :STRING, 'Member role'],
[:count, :INT, 'Number of members with this role.'],
[:fraction, :FLOAT, 'Number of members with this role divided by all members.']
]]
]),
:notes => "prevalent_roles can be null if taginfo doesn't have role information for this relation type, or an empty array when there are no roles with more than 1% of members",
:example => { :page => 1, :rp => 10 },
:ui => '/reports/relation_types#types'
}) do
total = @db.count('relation_types').
condition_if("rtype LIKE '%' || ? || '%'", params[:query]).
get_first_value().to_i

res = @db.select('SELECT * FROM relation_types').
condition_if("rtype LIKE '%' || ? || '%'", params[:query]).
order_by(@ap.sortname, @ap.sortorder) { |o|
o.relation_type :rtype
o.count
}.
paging(@ap).
execute()

all_relations = @db.stats('relations')

prevroles = @db.select('SELECT rtype, role, count, fraction FROM db.prevalent_roles').
condition("rtype IN (#{ res.map{ |row| "'" + SQLite3::Database.quote(row['rtype']) + "'" }.join(',') })").
order_by([:count], 'DESC').
execute()

pr = {}
res.each do |row|
pr[row['rtype']] = []
end

prevroles.each do |pv|
rtype = pv['rtype']
pv.delete_if{ |k,v| k.is_a?(Integer) || k == 'rtype' }
pv['count'] = pv['count'].to_i
pv['fraction'] = pv['fraction'].to_f
pr[rtype] << pv
end

return {
:page => @ap.page,
:rp => @ap.results_per_page,
:total => total,
:data => res.map{ |row| {
:relation_type => row['rtype'],
:count => row['count'].to_i,
:count_fraction => row['count'].to_i / all_relations,
:prevalent_roles => row['members_all'] ? pr[row['rtype']][0,10] : nil
} }
}.to_json
end

end
20 changes: 20 additions & 0 deletions web/lib/ui/relation.rb
@@ -0,0 +1,20 @@
# web/lib/ui/relations.rb
class Taginfo < Sinatra::Base

get %r{^/relations/(.*)} do |rtype|
if params[:rtype].nil?
@rtype = rtype
else
@rtype = params[:rtype]
end

@title = [escape_html(@rtype), t.osm.relations]
section :relations

@desc = 'XXX'

javascript "#{ r18n.locale.code }/relation"
erb :relation
end

end
23 changes: 23 additions & 0 deletions web/lib/utils.rb
Expand Up @@ -142,6 +142,29 @@ def pp_value(value)
return escape_html(value).gsub(/ /, '&#x2423;').gsub(/\s/, '<span class="whitespace">&nbsp;</span>')
end

def pp_rtype(rtype)
if rtype == ''
return '<span class="badchar empty">empty string</span>'
end

pp_chars = '!"#$%&()*+,/;<=>?@[\\]^`{|}~' + "'";

result = ''
rtype.each_char do |c|
if (!pp_chars.index(c).nil?)
result += '<span class="badchar">' + c + '</span>'
elsif (c == ' ')
result += '<span class="badchar">&#x2423;</span>'
elsif (c.match(/\s/))
result += '<span class="whitespace">&nbsp;</span>'
else
result += c;
end
end

return result;
end

def link_to_key(key, tab='')
k = escape(key)

Expand Down
66 changes: 66 additions & 0 deletions web/public/js/taginfo.js
Expand Up @@ -214,6 +214,15 @@ function url_for_tag(key, value) {
}
}

function url_for_rtype(rtype) {
var t = encodeURIComponent(rtype);
if (rtype.match(/[=\/]/)) {
return '/relations/?rtype=' + t;
} else {
return '/relations/' + t;
}
}

function link_to_value_with_title(key, value, extra) {
return link(
url_for_tag(key, value),
Expand Down Expand Up @@ -265,6 +274,54 @@ function pp_value(value) {
return pp_value_replace(value);
}

function pp_rtype(rtype) {
if (rtype == '') {
return span(texts.misc.empty_string, 'badchar empty');
}

var result = '',
length = rtype.length;

for (var i=0; i<length; i++) {
var c = rtype.charAt(i);
if (pp_chars.indexOf(c) != -1) {
result += span(c, 'badchar');
} else if (c == ' ') {
result += span('&#x2423;', 'badchar');
} else if (c.match(/\s/)) {
result += span('&nbsp;', 'whitespace');
} else {
result += c;
}
}

return result;
}

function pp_role(role) {
if (role == '') {
return span(texts.misc.empty_string, 'badchar empty');
}

var result = '',
length = role.length;

for (var i=0; i<length; i++) {
var c = role.charAt(i);
if (pp_chars.indexOf(c) != -1) {
result += span(c, 'badchar');
} else if (c == ' ') {
result += span('&#x2423;', 'badchar');
} else if (c.match(/\s/)) {
result += span('&nbsp;', 'whitespace');
} else {
result += c;
}
}

return result;
}

function link_to_key(key, highlight) {
return link(
url_for_key(key),
Expand Down Expand Up @@ -297,6 +354,15 @@ function link_to_key_or_tag(key, value) {
return link;
}

function link_to_rtype(rtype, highlight) {
return link(
url_for_rtype(rtype),
highlight === undefined ?
pp_rtype(rtype) :
rtype.replace(new RegExp('(' + highlight + ')', 'gi'), "<b>$1</b>")
);
}

/* ============================ */

var flexigrid_defaults = {
Expand Down
7 changes: 5 additions & 2 deletions web/taginfo.rb
Expand Up @@ -156,9 +156,9 @@ def next_update

#-------------------------------------

%w(about download keys sources tags).each do |page|
%w(about download keys relations sources tags).each do |page|
get '/' + page do
@title = (page =~ /^(keys|tags)$/) ? t.osm[page] : t.taginfo[page]
@title = (page =~ /^(keys|tags|relations)$/) ? t.osm[page] : t.taginfo[page]
if File.exists?("viewsjs/#{ page }.js.erb")
javascript "#{ r18n.locale.code }/#{ page }"
end
Expand Down Expand Up @@ -190,6 +190,8 @@ def next_update
load 'lib/api/v4/key.rb'
load 'lib/api/v4/keys.rb'
# load 'lib/api/v4/langtag.rb'
load 'lib/api/v4/relation.rb'
load 'lib/api/v4/relations.rb'
load 'lib/api/v4/search.rb'
load 'lib/api/v4/site.rb'
load 'lib/api/v4/tag.rb'
Expand All @@ -198,6 +200,7 @@ def next_update

load 'lib/ui/embed.rb'
load 'lib/ui/keys_tags.rb'
load 'lib/ui/relation.rb'
load 'lib/ui/reports.rb'
load 'lib/ui/search.rb'
load 'lib/ui/taginfo.rb'
Expand Down
26 changes: 26 additions & 0 deletions web/views/relation.erb
@@ -0,0 +1,26 @@
<div class="pre">
<h1><%= t.pages.relation.name %> '<%= pp_rtype(@rtype) %>'</h1>
<p><%= @desc %></p>
</div>
<div id="tabs">
<ul>
<li><a href="#overview"><%= t.pages.relation.overview.tab %></a></li>
<li><a href="#roles"><%= t.pages.relation.roles.tab %></a></li>
</ul>
<div id="overview">
<h2><%= t.pages.relation.overview.title %></h2>
</div>
<div id="roles">
<h2><%= t.pages.relation.roles.title %></h2>
<table id="grid-roles">
</table>
</div>
</div>
<% javascript do
JS.raw(<<"JAVASCRIPT")
function page_init2() {
init_tabs([#{ @rtype.to_json }]);
}
JAVASCRIPT
end
%>

0 comments on commit 9f39838

Please sign in to comment.