Language
Login
Language Setting
X
English
日本語 [Japanese]
about this App
笹川スポーツ財団チャレンジデー対戦マップ
useful
2
Loading...
/* 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>
Preview
Input Data
ReadMe
Snapshots
This application does not using any data or contains private/limited data.
Link http://app.linkdata.org/run/app1s442i?tab=readme
このアプリについて
笹川スポーツ財団チャレンジデーの結果データを入力し
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
Work
Add
Clear
insert work id or work name.