Last active
December 18, 2015 12:09
-
-
Save straypacket/5780848 to your computer and use it in GitHub Desktop.
Walk server - from a road .shp file to a full walk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=utf-8"/> | |
<title>SUJ Geo walks</title> | |
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> | |
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script> | |
<script type="text/javascript"> | |
var path = []; | |
function getNewPath() { | |
// Make ajax call | |
var xhReq = new XMLHttpRequest(); | |
xhReq.open("GET", "/?lon="+$('.lon').val()+"&lat="+$('.lat').val()+"&length="+$('.length').val(), false); | |
xhReq.send(null); | |
var serverResponse = xhReq.responseText; | |
var objJSON = JSON.parse(serverResponse); | |
// Update data | |
path = []; | |
objJSON['body'].forEach( function(p) { | |
if (p) { | |
path.push(new google.maps.LatLng(p[1],p[0])); | |
} | |
else{ | |
console.log(objJSON['body']) | |
} | |
}); | |
// Dumb refresh | |
initialize(); | |
} | |
function initialize() { | |
var mapDiv = document.getElementById('map-canvas'); | |
var map = new google.maps.Map(mapDiv, { | |
center: new google.maps.LatLng($('.lat').val(),$('.lon').val()), | |
zoom: 15, | |
mapTypeId: google.maps.MapTypeId.ROADMAP | |
}); | |
var lineSymbol = { | |
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW | |
}; | |
var line = new google.maps.Polyline({ | |
path: path, | |
icons: [{ | |
icon: lineSymbol, | |
offset: '100%' | |
}], | |
strokeColor: '#ff0000', | |
strokeOpacity: 1.0, | |
strokeWeight: 2 | |
}); | |
google.maps.LatLng.prototype.kmTo = function(a){ | |
var e = Math, ra = e.PI/180; | |
var b = this.lat() * ra, c = a.lat() * ra, d = b - c; | |
var g = this.lng() * ra - a.lng() * ra; | |
var f = 2 * e.asin(e.sqrt(e.pow(e.sin(d/2), 2) + e.cos(b) * e.cos(c) * e.pow(e.sin(g/2), 2))); | |
return f * 6378.137; | |
} | |
var inKm = function(n){ | |
var a = n, len = a.length, dist = 0; | |
for(var i=0; i<len-1; i++){ | |
dist += a[i].kmTo(a[i+1]); | |
} | |
return dist; | |
} | |
$('.info').html("Route length: "+inKm(path).toPrecision(4)+" Km"); | |
line.setMap(map); | |
function animateArrow() { | |
var count = 0; | |
offsetId = window.setInterval(function() { | |
count = (count + 1) % 200; | |
var icons = line.get('icons'); | |
icons[0].offset = (count / 2) + '%'; | |
line.set('icons', icons); | |
}, 50); | |
} | |
animateArrow(); | |
} | |
google.maps.event.addDomListener(window, 'load', initialize); | |
</script> | |
</head> | |
<body style="font-family: Arial; border: 0 none;"> | |
<div id="map-canvas" style="width: 800px; height: 400px"></div> | |
<div> | |
Lon: <input class="lon" type="text" name="lon" value="139.698345"> | |
Lat: <input class="lat" type="text" name="lat" value="35.666641"> | |
Length: <input class="length" type="text" name="length" value="2000">m<br> | |
<input type="submit" onclick="getNewPath()" value="Get new route"> | |
</div> | |
<div class="info"></div> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#### | |
#Old spherical indexes | |
### | |
db.road_coords.ensureIndex({"head": "2d"}) | |
db.road_coords.ensureIndex({"tail": "2d"}) | |
db.station_coords.ensureIndex({"idx_loc": "2d"}) | |
db.road_coords.find( { head: { $near: [138.657956,35.858569], $maxDistance: 1 } } ) | |
### | |
# Near sherical indexes | |
### | |
db.road_coords.ensureIndex({"head": "2dsphere"}) | |
db.road_coords.ensureIndex({"tail": "2dsphere"}) | |
db.station_coords.ensureIndex({"idx_loc": "2dsphere"}) | |
# Find closest locations within 20Km | |
db.road_coords.find( { head: { $nearSphere: [138.657956,35.858569], $maxDistance: 18 / 6371 } } ) | |
#{ | |
# "_id":ObjectId("51baf5608876e476ac000001"), | |
# "head":{ | |
# "lat":138.857121, | |
# "lon":35.86131 | |
# }, | |
# "tail":{ | |
# "lat":138.857563, | |
# "lon":35.861237 | |
# }, | |
# "body":[ | |
# [ | |
# 138.857121, | |
# 35.86131 | |
# ], | |
# [ | |
# 138.857259, | |
# 35.861265 | |
# ], | |
# [ | |
# 138.857563, | |
# 35.861237 | |
# ] | |
# ] | |
#} | |
# Find locations within 20Km | |
db.road_coords.find( { head: { $geoWithin: { $centerSphere: [ [138.657956,35.858569], 20 / 6371 ] } } } ) | |
#{ | |
# "_id":ObjectId("51baf5608876e476ac000001"), | |
# "head":{ | |
# "lat":138.857121, | |
# "lon":35.86131 | |
# }, | |
# "tail":{ | |
# "lat":138.857563, | |
# "lon":35.861237 | |
# }, | |
# "body":[ | |
# [ | |
# 138.857121, | |
# 35.86131 | |
# ], | |
# [ | |
# 138.857259, | |
# 35.861265 | |
# ], | |
# [ | |
# 138.857563, | |
# 35.861237 | |
# ] | |
# ] | |
#} | |
# Order by and show distance | |
# This will work with only one index per collection :( | |
# distanceMultiplier: 6371 will make mongo calculate the distances in Km (ue 3959 or miles) | |
db.road_coords_head.ensureIndex({"tail": "2dsphere"}) | |
db.road_coords_tail.ensureIndex({"tail": "2dsphere"}) | |
db.station_coords.ensureIndex({"idx_loc": "2dsphere"}) | |
db.runCommand( { geoNear: "road_coords_head", near: [138.657956,35.858569], spherical: true, distanceMultiplier: 6371, limit: 5 } ) | |
#{ | |
# "ns" : "roadsimulator.road_coords", | |
# "results" : [ | |
# { | |
# "dis" : 17.950952246422005, | |
# "obj" : { | |
# "_id" : ObjectId("51baf5608876e476ac000001"), | |
# "head" : { | |
# "lat" : 138.857121, | |
# "lon" : 35.86131 | |
# }, | |
# "tail" : { | |
# "lat" : 138.857563, | |
# "lon" : 35.861237 | |
# }, | |
# "body" : [ | |
# [ | |
# 138.857121, | |
# 35.86131 | |
# ], | |
# [ | |
# 138.857259, | |
# 35.861265 | |
# ], | |
# [ | |
# 138.857563, | |
# 35.861237 | |
# ] | |
# ] | |
# } | |
# }, | |
#... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'net/http' | |
url = 'http://localhost:4570/?long=139.6941&lat=35.6572&length=2000' | |
resp = Net::HTTP.get_response(URI.parse(url)) | |
data = resp.body |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# Call with curl -i -H "Accept: application/json" "http://localhost:4570/?long=139.6941&lat=35.6572&length=2000" | |
# Or use http://localhost:4570/demo.html | |
## | |
require 'rubygems' | |
require 'active_support' | |
require 'sinatra' | |
require 'mongo' | |
require 'json' | |
encoding_options = { | |
:invalid => :replace, # Replace invalid byte sequences | |
:undef => :replace, # Replace anything not defined in ASCII | |
:replace => '', # Use a blank for those replacements | |
:universal_newline => true # Always break lines with \n | |
} | |
class Array | |
def rotate | |
push shift | |
end | |
end | |
conn = Mongo::Connection.new("localhost", 27017, :pool_size => 100, :pool_timeout => 5) | |
db = conn['roadsimulator'] | |
set :port, 4570 | |
get '/' do | |
# Get params | |
lon = params["lon"].to_f | |
lat = params["lat"].to_f | |
# Convert from Km to degrees | |
length = params["length"].to_f/111110 | |
# Build walk | |
res = make_walk(db, lon, lat, length) | |
# Make a google compatible walk | |
#res['body'].each do |w| | |
# w.rotate | |
#end | |
# Output to paste into https://code.google.com/apis/ajax/playground/#polylines_v3 | |
#res['body'].each do |w| | |
# puts "new google.maps.LatLng(#{w[1]}, #{w[0]})," | |
#end | |
# Send walk | |
res.to_json | |
end | |
#### | |
# Make big walk | |
#### | |
def make_walk(db, lon, lat, length) | |
# Initial run | |
limit = 3 | |
local_arch = get_arch(db, lon, lat, limit) | |
# Inits | |
walk = ActiveSupport::OrderedHash.new | |
walk['distance'] = local_arch['obj']['distance'] | |
walk['body'] = [local_arch['obj']['body']] | |
walked = {} | |
rewalk_thresh = 1 | |
# Loop until we reach the requested length | |
while walk['distance'] <= length | |
if local_arch['obj']['body'] | |
old_body = local_arch['obj']['body'] | |
local_arch = get_arch(db, old_body[old_body.length()-1][0], old_body[old_body.length()-1][1], limit) | |
# Is this an already discovered path | |
if walked.include? local_arch['obj'] | |
walked[local_arch['obj']] = walked[local_arch['obj']]+1 | |
# If we walk past this arch for more than 5 times, walk back until we find a fresh path | |
if walked[local_arch['obj']] >= rewalk_thresh | |
# Walk back/ Pop dubious path from walked array | |
walk['body'].pop() | |
# Update current position to last position after popping | |
local_arch['obj']['body'] = walk['body'][walk['body'].length()-1] | |
#Increase the search limit | |
limit += 1 | |
# Get another arch | |
next | |
end | |
else # New path | |
walked[local_arch['obj']] = 1 | |
end | |
else | |
# We popped too much! Starting from scratch ... | |
local_arch = get_arch(db, lon, lat, limit) | |
next | |
end | |
# Add body array to walk | |
walk['body'].push(local_arch['obj']['body']) if local_arch | |
# Accumulate distance | |
walk['distance'] += (local_arch['obj']['distance']) if local_arch | |
end | |
# Flatten array so that archs are merged into contiguous [lon,lat] pairs | |
walk['body'] = walk['body'].flatten(1) | |
return walk | |
end | |
#### | |
# Add arch to walking path | |
#### | |
def get_arch(db, lon, lat, limit) | |
# Build query | |
selector = ActiveSupport::OrderedHash.new | |
selector['geoNear'] = 'road_coords_head' | |
selector['near'] = [lon, lat] | |
selector['spherical'] = true | |
selector['distanceMultiplier'] = 6371 | |
selector['limit'] = limit | |
# Query | |
res = db.command( selector ) | |
# Get which tail_point is closer to a train station | |
selector['geoNear'] = 'station_coords' | |
ordered_res = [] | |
# TO DO | |
# When reaching a train station, travel a few train | |
# stations on that line | |
#For each of the candidate arcs (closest arcs) | |
res['results'].each do |p| | |
# Make query | |
selector['near'] = p['obj']['body'][-1] | |
station_res = db.command( selector ) | |
# Average the distance from the last point of the arc | |
# to the closest _limit_ train distances | |
# TO DO: play with this _limit_ variable | |
sdist = 0.0 | |
station_res['results'].each { |sd| sdist += sd['dis'] } | |
avg_sdist = sdist/station_res['results'].size | |
# Push to array with original data and | |
# newly calculated distance to train stations | |
ordered_res.push({'dist' => avg_sdist, 'arc' => p}) | |
end | |
# Sort new array | |
sorted = ordered_res.sort_by { |k| k["dist"] } | |
# Generate random element, based on a Gaussian distribution | |
r = (rand(limit) - (limit-2)).abs | |
return sorted[r]['arc'] | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rgeo' | |
require 'rgeo-shapefile' | |
require 'mongo' | |
conn = Mongo::Connection.new("localhost", 27017, :pool_size => 100, :pool_timeout => 5) | |
db = conn['roadsimulator'] | |
indexes = ['head','tail'] | |
# Parse Shapefile | |
col = db['station_coords'] | |
RGeo::Shapefile::Reader.open('data/N05-12_Station2.shp') do |file| | |
puts "File contains #{file.num_records} records." | |
file.each do |record| | |
#路線名 | |
line = record.attributes['N05_002'] = record.attributes['N05_002'].force_encoding('sjis').encode('utf-8') | |
#運営会社 | |
company = record.attributes['N05_003'] = record.attributes['N05_003'].force_encoding('sjis').encode('utf-8') | |
#駅名 | |
station = record.attributes['N05_011'] = record.attributes['N05_011'].force_encoding('sjis').encode('utf-8') | |
#Point | |
index_coord = record.geometry | |
#Create query | |
p = { | |
:idx_loc => { | |
:lat => index_coord.x().to_f, | |
:lon => index_coord.y().to_f | |
}, | |
:station => station, | |
:line => line, | |
:company => company | |
} | |
#Insert into MongoDB | |
col.insert(p) | |
end | |
end | |
return 0 | |
exit | |
indexes.each do |i| | |
col = db['road_coords_'+i] | |
# Parse highway Shapefile | |
RGeo::Shapefile::Reader.open('data/tokyo_highway.shp') do |file| | |
puts "File contains #{file.num_records} records." | |
count = [] | |
file.each do |record| | |
first = record.geometry[0].point_n(0) | |
last = record.geometry[0].point_n(record.geometry[0].num_points()-1) | |
total = record.geometry[0].num_points() | |
index_coord = first | |
if i == 'tail' | |
index_coord = last | |
end | |
#Create query | |
p = { | |
:idx_loc => { | |
:lat => index_coord.x().to_f, | |
:lon => index_coord.y().to_f | |
}, | |
:type => i, | |
:body => [] | |
} | |
distance = 0.0 | |
pp = nil | |
record.geometry[0].points.each do |c| | |
# Measure the distance between inner points | |
if pp | |
distance += pp.distance(c) | |
end | |
p[:body].push([c.x(),c.y()]) | |
pp = c | |
end | |
# Add arch's distance | |
p[:distance] = distance | |
#Insert into MongoDB | |
col.insert(p) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment