Language
ログイン
言語設定
X
English
日本語 [Japanese]
オープンデータを使ってアプリを作ろう
アプリケーションの作成と公開をサポートするサイト
お問い合わせ
HOME
チュートリアル
アプリ新規作成
アプリのFork
公開アプリ一覧
アプリ作品の情報
笹川スポーツ財団チャレンジデー対戦マップ
fullscreen
デジタル・ウント・メア
笹川スポーツ財団チャレンジデーの結果データを入力し、マップに表示するアプリです。
2
評価指数
いいね!
2
Loading...
このアプリ作品で使われているデータと同じ形式のデータを作成し、このアプリに適用することができます。
ひな形として使うファイルをリストから選択し、"Create Templete"をクリックして下さい。
アプリケーション
fullscreen
play
stop
reload
Play
JavaScript
CSS
HTML
概要
保存した実行結果
笹川スポーツ財団チャレンジデー対戦マップ
http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
underscore.js
backbone.js
bootstrap.js
http://maps.googleapis.com/maps/api/js?sensor=false
raphael.js
chroma.js
Lato_Black_900.font.js
笹川スポーツ財団チャレンジデー対戦マップ
http://fonts.googleapis.com/css?family=Lato:400,700,900,400italic
http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.2.2/css/bootstrap.min.css
笹川スポーツ財団チャレンジデー対戦マップ
笹川スポーツ財団チャレンジデー対戦マップ
笹川スポーツ財団チャレンジデー対戦マップ
/* challengemap.js The MIT License (MIT) Copyright (c) 2013 Digital und MeeR Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ (function(global) { var works = LinkData.getWorks(), fileData = {} _(works).each(function(work){ var files = LinkData.getFiles(work), workName = LinkData.getWorkName(work) _(files).each(function(file){ var city = { challenge_data: [] }, latStore = [], longStore = [], latSum = 0, longSum = 0 ,subjects = LinkData.getSubjects(work, file) ,colorScale = chroma.scale("Set3").mode("hsv") _(subjects).each(function(sub){ var obj = {}, props = LinkData.getProperties(work, file) _(props).each(function(prop){ propLabel = prop.split("#")[1] obj[propLabel] = LinkData.getObjects(work, file, sub, prop)[0] }) obj.color = colorScale(Math.random()) latStore.push(parseFloat(obj.lat)) latSum += parseFloat(obj.lat) longStore.push(parseFloat(obj.long)) longSum += parseFloat(obj.long) city.challenge_data.push(obj) }) city.name = city.challenge_data[0].city city.year = city.challenge_data[0].year city.lat = latSum / city.challenge_data.length city.long = longSum / city.challenge_data.length city.bounds = { s: _.min(latStore), w: _.min(longStore), n: _.max(latStore), e: _.max(longStore) } city.id = work + "|" + file fileData[city.id] = city $(".file-selector").each(function(idx, el){ $(el).append('<option value="' + city.id + '">' + workName + " | " + file + '</option>') }) }) }) $("#submit-match").on("click", function(){ var submitData = [] $(".file-selector").each(function(idx, el){ var val = $(el).val(), data = null, id = $(el).attr("id") if(val) { data = fileData[val] data.selector = id submitData.push(data) } $("#" + id + "-total-count").html("-") $("#" + id + "-total-percentage").html("--") $("#" + id + "-total-population").html("-") $("#" + id + "-winner").hide() }) __app.match.reset(submitData) $("#data-section").slideDown() $("#selector-section").slideUp() }) $(".file-selector").on("change", function(){ var selfValue = $(this).val(), otherSelectors = $(".file-selector").not(this) if($(this).attr("id") == "c1") { otherSelectors.each(function(idx, el){ if(selfValue) $(el).prop("disabled", false) else $(el).prop("disabled", true).val("").trigger("change") }) } otherSelectors.find("option").each(function(idx, el){ var optionValue = $(el).attr("value") if(optionValue) { if(optionValue == selfValue) $(el).prop("disabled", true) else $(el).prop("disabled", false) } }) }).trigger("change") $("#show-selector").on("click", function(){ $("#data-section").slideUp() $("#selector-section").slideDown() __app.match.reset([]) }) var challengeMap = {} , map = new google.maps.Map(document.getElementById("map_canvas"), { center: new google.maps.LatLng(39.25128944533077, 140.62528600000005), draggable: false, zoom: 11, maxZoom: null, minZoom: null, disableDefaultUI: true, mapTypeControl: true, disableDoubleClickZoom: false, mapTypeId: google.maps.MapTypeId.ROADMAP }) , defaultState = { displayValue: "percentage", displayMode: "region", selectedCity: null } , __app = null , City = Backbone.Model.extend({ initialize: function() { var paper = __app.graph.paper, bounds = this.get("bounds") , sw = new google.maps.LatLng(bounds.s - 0.02, bounds.w - 0.02) , ne = new google.maps.LatLng(bounds.n + 0.02, bounds.e + 0.02) this.regions = new RegionCollection() this.regions.city = this this.regions.reset(this.get("challenge_data")) this.totalGraphPosition = new google.maps.LatLng(this.get("lat"), this.get("long")) this.position = new google.maps.LatLng(parseFloat(this.get("lat")), parseFloat(this.get("long"))) this.bounds = new google.maps.LatLngBounds(sw, ne) this.selector = new CitySelect({model: this}) this.kml = new google.maps.KmlLayer({ url: location.protocol + "//" + location.host + challengeMap.appPath + "kml/city-" + this.id + ".kml", clickable: false, preserveViewport: true, map: map }) this.elements = { circle: paper.circle(0, 0, 0).attr({ fill: "#c00", stroke: 0, "fill-opacity": 0.8, "stroke-width": 4 }), label: paper.text(0, 0, this.get("name")).attr({"font-size": 18}), value: null } this.showInformation() this.on("select", this.select) this.on("unfocus", this.unfocus) this.on("unload", this.unload) }, select: function() { var changed = this.collection._changingState , regionClickHandler = this.collection.getStateHandler({displayMode:"region"}) if(_.isEmpty(changed)) return null if(changed.displayMode && this.regionGraphTimer) {//地域-全体の高速切り替え対策 clearTimeout(this.regionGraphTimer) this.regionGraphTimer = null } if(changed.selectedCity) { if(this.isSingle()) { changed.displayMode = "total" $("#display-region").off("click").prop("disabled", true).addClass("disabled") } else { $("#display-region").on("click", regionClickHandler).prop("disabled", false).removeClass("disabled") } google.maps.event.addListenerOnce(map, "idle", _.bind(function(){//mapの位置変更を待つ this.resetCircle() this.showGraph(changed.displayMode, changed.displayValue) map.setOptions({ maxZoom: map.getZoom(), minZoom: map.getZoom() }) console.log(map.getZoom()) }, this)) console.log(this.bounds) map.setOptions({ maxZoom: 11, minZoom: null }) map.panTo(this.position) map.fitBounds(this.bounds) map.panBy(1,0) } else { this.showGraph(changed.displayMode, changed.displayValue) } }, unfocus: function() { this.regions.each(function(mdl){ _.each(mdl.elements, function(el){ if(el) el.hide() }) }) _.each(this.elements, function(el){ if(el) el.hide() }) }, unload: function() { this.regions.each(function(mdl){ _.each(mdl.elements, function(el){ if(el) el.remove() }) }) _.each(this.elements, function(el){ if(el) el.remove() }) this.kml.setMap(null) }, showInformation: function() { var selector = this.get("selector") $("#" + selector + "-total-count").html(this.getTotalValue("count")) $("#" + selector + "-total-percentage").html(this.getTotalPercentage()) $("#" + selector + "-total-population").html(this.getTotalValue("population")) }, getTotalValue: function(attr){ var result = 0 this.regions.each(function(mdl){ result += parseInt(mdl.get(attr)) }) return result }, getTotalPercentage: function() { return Math.round(this.getTotalValue("count") / this.getTotalValue("population") * 10000) / 100 }, getCircleRadius: function(value) { var worldWidth = __app.graph.getProjection().getWorldWidth(), result = 0 if(value == "percentage") { result = worldWidth / 200000 * this.getTotalPercentage() } else if(value == "count") { result = worldWidth / 140000000 * this.getTotalValue("count") } return result }, resetCircle: function(){ var proj = __app.graph.getProjection() ,center = proj.fromLatLngToDivPixel(this.totalGraphPosition) ,el = this.elements el.circle.attr({ cx: center.x, cy: center.y, r: 0 }) this.regions.each(function(mdl){ var el = mdl.elements el.circle.attr({ cx: center.x, cy: center.y, r: 0 }) }) }, showGraph: function(mode, value){ mode = mode || this.collection.state.displayMode value = value || this.collection.state.displayValue if(mode == "total") this.showTotalGraph(value) else if(mode == "region") this.showRegionGraph(value) }, showTotalGraph: function(value){ var center = __app.graph.getProjection().fromLatLngToDivPixel(this.totalGraphPosition) ,paper = __app.graph.paper ,el = this.elements this.regions.each(function(mdl){ var el = mdl.elements el.label.hide() if(el.value) el.value.hide() el.circle.animate({ cx: center.x, cy: center.y, r: 0 }, 200, ">", function(){this.hide()}) }) el.circle.show().animate({ cx: center.x, cy: center.y, r: this.getCircleRadius(value) }, 500, "bounce") el.label.attr({ x: center.x, y: center.y - 48 }).show() if(el.value) el.value.remove() if(value == "percentage") { el.value = paper.print( center.x, center.y, this.getTotalPercentage() + "%", paper.getFont("Lato"), 64 ).attr({ fill: "#fff", stroke: "#333", "stroke-width": 2, "stroke-linejoin": "round" }) } else if(value == "count") { el.value = paper.print( center.x, center.y, this.getTotalValue("count"), paper.getFont("Lato"), 64 ).attr({ fill: "#fff", stroke: "#333", "stroke-width": 2, "stroke-linejoin": "round" }) } el.value.transform("t-" + el.value.getBBox().width / 2 + ",0") }, showRegionGraph: function(value) { var el = this.elements , paper = __app.graph.paper , self = this el.label.hide() if(el.value) el.value.hide() el.circle.animate({ r: 0 }, 200, ">", function(){this.hide()}) ;(function iter(i){ var mdl = self.regions.at(i) if(mdl === undefined) { self.regionGraphTimer = null return true } var pos = __app.graph.getProjection().fromLatLngToDivPixel(mdl.position) ,el = mdl.elements ,callee = arguments.callee el.label.attr({ x: pos.x, y: pos.y - 32 }).show() el.circle.show().animate({ cx: pos.x, cy: pos.y, r: mdl.getCircleRadius(value) }, 500, "bounce") if(el.value) el.value.remove() if(value == "percentage") { el.value = paper.print( pos.x, pos.y, mdl.getPercentage() + "%", paper.getFont("Lato"), 42 * Math.pow(map.getZoom() / 11, 2) ).attr({ fill: "#fff", stroke: "#333", "stroke-width": 2, "stroke-linejoin": "round" }) } else if(value == "count") { el.value = paper.print( pos.x, pos.y, mdl.get("count"), paper.getFont("Lato"), 42 * Math.pow(map.getZoom() / 11, 2) ).attr({ fill: "#fff", stroke: "#333", "stroke-width": 2, "stroke-linejoin": "round" }) } el.value.transform("t-" + el.value.getBBox().width / 2 + ",0") self.regionGraphTimer = setTimeout(function(){ iter(++i) }, 50) })(0) }, getTimestamp: function() { var result = new Date(0) this.regions.each(function(mdl) { var time = new Date() if(result < time) result = time }) return result }, isSingle: function() { return this.regions.length == 1 } }) , Region = Backbone.Model.extend({ initialize: function() { var paper = __app.graph.paper ,fontSize = 18 * Math.pow(map.getZoom() / 11, 2) this.position = new google.maps.LatLng(this.get("lat"), this.get("long")) this.elements = { circle: paper.circle(0, 0, 0).attr({ fill: this.get("color"), stroke: 0, "fill-opacity": 0.8, "stroke-width": 4 }), label: paper.text(0, 0, this.get("region")).attr({"font-size": fontSize}), percentage: null, count: null } }, getPercentage: function() { return Math.round(parseInt(this.get("count")) / parseInt(this.get("population")) * 100) }, getCircleRadius: function(value) { var worldWidth = __app.graph.getProjection().getWorldWidth() if(value == "percentage") { return worldWidth / 600000 * this.getPercentage() } else if(value == "count") { return worldWidth / 100000000 * this.get("count") } } }) , Match = Backbone.Collection.extend({ model: City, initialize: function() { this.on("reset", function(col, opt){ this.resetState() _.each(opt.previousModels, function(pmdl){ pmdl.trigger("unload") }) if(col.length) { this.setState(_.defaults({selectedCity: this.at(0).id}, defaultState)) $("#switch-city").show() } else { $("#switch-city").hide() } if(col.length > 1) { this.judgement() } }) $("#display-percentage").on("click", this.getStateHandler({displayValue: "percentage"})) $("#display-count").on("click", this.getStateHandler({displayValue: "count"})) $("#display-region").on("click", this.getStateHandler({displayMode: "region"})) $("#display-total").on("click", this.getStateHandler({displayMode: "total"})) }, judgement: function(){ var winner = [], highest = 0 this.each(function(mdl){ var totalPercentage = mdl.getTotalPercentage() if(highest <= totalPercentage) { if(highest == totalPercentage) winner.push(mdl) else winner = [mdl] highest = totalPercentage } }) _(winner).each(function(mdl){ $("#" + mdl.get("selector") + "-winner").fadeIn() }) }, state: {}, setState: function(state) { var changed = {}, selectedCity _.each(this.state, function(val, key){ if(state[key] && state[key] != val) changed[key] = state[key] }) selectedCity = changed.selectedCity || this.state.selectedCity this._changingState = changed this.each(function(mdl){ if(mdl.id == selectedCity) { mdl.trigger("select") } else { mdl.trigger("unfocus") } }) _.extend(this.state, this._changingState) $("#display-" + this.state.displayMode).button("toggle") $("#display-" + this.state.displayValue).button("toggle") }, resetState: function() { this.state = {displayMode: null, displayValue: null, selectedCity: null} }, loadData: function(year) { __app.loadedYear = (year && year != thisYear) ? year : thisYear this.reset(city) }, getStateHandler: function(state) { var self = this return function(ev){ self.setState(state) } }, getLoadHandler: function(year) { var self = this return function(){ var loadYear = year || thisYear self.loadData(loadYear) } } }) , RegionCollection = Backbone.Collection.extend({ model: Region }) , CitySelect = Backbone.View.extend({ initialize: function(){ _.bindAll(this, "toggle", "unload") this.model.on("unload", this.unload) this.model.on("select", this.toggle) this.render() this.select = __app.match.getStateHandler({selectedCity: this.model.id}) }, events: { "click": "select" }, className: "btn btn-large btn-primary span6 toggle-button", render: function(){ $("#switch-city > div") .append(this.$el.html(this.model.get("year") + "<br>" + this.model.get("name"))) }, toggle: function(){ this.$el.button("toggle") }, unload: function(){ this.$el.remove() } }) , App = Backbone.Router.extend({ initialize: function() { this.loadedYear = null this.match = null this.graph = new GraphOverlay(map, _.bind(function(){ this.match = new Match() }, this)) } }) , GraphOverlay = function(map, callback) { this.callback = callback this.setMap(map) } GraphOverlay.prototype = new google.maps.OverlayView() GraphOverlay.prototype.onAdd = function(){ var proj = this.getProjection() var paper = Raphael(0, 0, 2000, 2000) this.paper = paper var panes = this.getPanes() panes.overlayMouseTarget.appendChild(this.paper.canvas) this.callback() } GraphOverlay.prototype.draw = function(){ this.paper.canvas.style.left = "0px" this.paper.canvas.style.top = "0px" this.paper.canvas.style.overflow = "visible" } GraphOverlay.prototype.onRemove = function(){ this.paper.remove() this.paper = null } challengeMap.boot = function() { if(challengeMap.instance) return false challengeMap.instance = __app = new App() Backbone.history.start({ pushState: true, root: "/" + location.pathname }) } global.challengeMap = challengeMap })(this) challengeMap.boot()
html { height: 100%; } body { height: 100%; margin: 0; padding: 0; font: 14px/1.231 Lato, sans-serif; } h1,h2,h3,h4,h5,h6 { font-family: Lato, sans-serif; } #map_canvas { height: 100%; } #map_canvas img, .google-maps img { max-width: none; } .counter { font-size: 24px; color: white; margin-right: 0.1em; } .counter.total-percentage { font-size: 42px; color: #ffdddd; } .winner { display: block; font-weight: bold; color: #e67e22; text-align: center } #info-box { position: absolute; right: 0px; top: 0px; background: white; width: 350px; } #info-box h2 { margin-bottom: 12px; } #info-box .toggle-button { width: 49.99%; padding-left: 0; padding-right: 0; background-color: transparent; color: #999999; border: 0 none; border-bottom: 4px solid #cccccc; margin: 0; background-image: none; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; -webkit-border-radius: 0; -moz-border-radius: 0; -ms-border-radius: 0; -o-border-radius: 0; border-radius: 0; } #info-box .toggle-button:hover, #info-box .toggle-button.hover { background-color: transparent; border-bottom-color: #f0c3c7; } #info-box .toggle-button:active, #info-box .toggle-button.active { background-color: transparent; color: #333333; border-bottom-color: #e70012; } #info-box #switch-city .toggle-button { border-bottom: 0 none; background-color: #2d59ad; color: #91ade2; } #info-box #switch-city .toggle-button:hover, #info-box #switch-city .toggle-button.hover { background-color: #234484; color: #91ade2; } #info-box #switch-city .toggle-button:active, #info-box #switch-city .toggle-button.active { background-color: #012873; color: white; } #info-box #switch-city { position: relative; } #info-box .versus { background: white; display: block; text-align: center; color: #2d59ad; line-height: 32px; width: 32px; height: 32px; border-radius: 16px; position: absolute; left: 144px; top: 8px; z-index: 9000; } #info-box .palette { color: white; margin: 0; padding: 10px 15px; text-align: center } #info-box .palette-alizarin { background-color: #e70012; } #info-box .palette-silver { background-color: #bdc3c7; } #info-box .counter-title { margin: 0; padding: 5px; color: white; text-align: center; border-bottom: 1px solid white; } #info-box .counter-title.title-percentage { background-color: #e70012; } #info-box .counter-title.title-count { background-color: #bdc3c7; } table { width: 100%; } td .count, td .population { display: block; } td .population { color: #cccccc; padding-top: 2px; margin-top: 2px; border-top: 1px dotted #cccccc; } p.count, p.population { margin: 0; } p.population { padding-top: 4px; margin-top: 4px; border-top: 1px dotted white; } #timestamp { display: block; }
<div class="row-fluid" style="margin:0;max-width:100%;width:100%;height:100%"> <div id="map_canvas" style="margin-right:350px;height:100%"></div> <div id="info-box"> <div style="padding: 10px 15px"> <div id="data-alert"></div> <h2>VS! CHALLENGE DAY<span id="target-year"></span> <small id="timestamp"></small></h2> <div id="selector-section"> <h5>表示するデータを選択</h5> <div class="row-fluid"> <select class="file-selector span12" id="c1"> <option value="">データ1</option> </select> <p style="text-align:center">VS</p> <select class="file-selector span12" id="c2"> <option value="">データ2</option> </select> </div> <button id="submit-match" class="btn btn-primary btn-block">決定</button> </div> <div id="data-section" style="display:none"> <button id="show-selector" class="btn btn-block">データ選択に戻る</button> <hr> <div class="row-fluid"> <div class="span6"> <span class="winner" id="c1-winner" style="display:none">★ WINNER</span> </div> <div class="span6"> <span class="winner" id="c2-winner" style="display:none">★ WINNER</span> </div> </div> <div style="margin-bottom:10px;display:none" id="switch-city"> <div class="btn-group row-fluid" data-toggle="buttons-radio"> </div> <span class="versus">VS</span> </div> <div class="row-fluid"> <div class="span6"> <h5 class="counter-title title-percentage">参加率</h5> <div class="palette palette-alizarin"> <span id="c1-total-percentage" class="counter total-percentage">--</span><span class="total-percentage-unit counter-unit">%</span> </div> <h5 class="counter-title title-count">参加者数/総人口</h5> <div class="palette palette-silver"> <p class="count"><span id="c1-total-count" class="counter">-</span><span class="total-count-unit counter-unit">人</span></p> <p class="population"><span id="c1-total-population" class="counter">-</span><span class="total-count-unit counter-unit">人</span></p> </div> </div> <div class="span6"> <h5 class="counter-title title-percentage">参加率</h5> <div class="palette palette-alizarin"> <span id="c2-total-percentage" class="counter total-percentage">--</span><span class="total-percentage-unit counter-unit">%</span> </div> <h5 class="counter-title title-count">参加者数/総人口</h5> <div class="palette palette-silver"> <p class="count"><span id="c2-total-count" class="counter">-</span><span class="total-count-unit counter-unit">人</span></p> <p class="population"><span id="c2-total-population" class="counter">-</span><span class="total-count-unit counter-unit">人</span></p> </div> </div> </div> <hr> <h5>地図の表示</h5> <div style="margin-bottom:10px"> <div class="btn-group row-fluid" data-toggle="buttons-radio"> <a class="btn btn-large btn-primary span6 toggle-button" id="display-percentage">参加率</a> <a class="btn btn-large btn-primary span6 toggle-button" id="display-count">参加人数</a> </div> </div> <div> <div class="btn-group row-fluid" data-toggle="buttons-radio"> <a class="btn btn-large btn-primary span6 toggle-button" id="display-region">地域ごと</a> <a class="btn btn-large btn-primary span6 toggle-button" id="display-total">総合</a> </div> </div> </div> </div> </div> <!--img src="title.png" style="position:absolute; left: 20px; bottom: 20px"--> </div>
このアプリについて
笹川スポーツ財団チャレンジデーの結果データを入力し
2つのデータを比較、擬似対戦を行うことができます。
地図上に表示される円は参加率、参加人数に応じて大きさが変化します。
入力データについて
秋田県横手市のデータ(
http://linkdata.org/work/rdf1s859i/apps
)
と同様の形式で作成すれば、アプリに追加して使用することができます。
データ作成の注意点
地域ごとのデータがある場合は1行ごとに地域のデータを入力してください。
全体のデータしかない場合は1行だけで構いません。
1つのファイル内では年(year)、自治体名(city)は全て同じにしてください。
別の年のデータを追加したい場合は別のファイルを作成してください。
http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
underscore.js
backbone.js
bootstrap.js
http://maps.googleapis.com/maps/api/js?sensor=false
raphael.js
chroma.js
Lato_Black_900.font.js
このアプリをForkして新しいアプリを作る
ダウンロード
Fork元のアプリは更新されました.
>>see
アプリは更新されました.
>>see
ツイート
このアプリ作品をwebから探す
作者
メッセージ送信
デジタル・ウント・メア
共同編集者
横手市情報政策課
実行回数
894
ウェブサイト
ライセンス
http://opensource.org/licenses/MIT
Fork count
0
作成日
2013年10月13日
最終更新日
2014年7月1日
"
" コミュニティへの投稿が完了しました。投稿したアプリ作品は、コミュニティ管理者によって承認されるとコミュニティに公開されます。
エントリー先のコミュニティとカテゴリ名を選択し、「エントリー」ボタンをクリックして下さい。
チュートリアル
アプリ作品の削除をする場合は削除をクリックしてください
送信者
名前
*
E-mail
*
件名
*
メッセージ
*
アクセス認証
*
送信
キャンセル
入力データ
関連アイデア
このアプリには入力データがないか、非公開/限定公開のデータが含まれています。
このアプリを使ったアイデアはまだ公開されていません
アイデアを作成
ニュースフィード
linkdata.org に関するツイート