update manhattan grid example

This commit is contained in:
rolux 2012-07-03 15:42:06 +02:00
parent a04eb376ed
commit d07c8d44e3

View file

@ -8,10 +8,10 @@ Avenue A) and Zero St (a.k.a. Houston St). Avenues east of Zero Ave, just as
Streets south of Zero St, have negative numbers. Broadway, which will split not
only Manhattan but the entire globe into an eastern and a western hemisphere,
retains its orientation, but is adjusted slightly so that it originates at the
intersection of Zero & Zero. From there, Broadway, Zero Avenue and Zero Street
continue as perfectly straight equatorial lines. All three will intersect once
more, exactly halfway, in the Indian Ocean, (southwest of Australia), at the
point furthest from Manhattan.
intersection of Zero & Zero. From there, Broadway, Zero Ave and Zero St continue
as perfectly straight equatorial lines. All three will intersect once more,
exactly halfway, in the Indian Ocean, (southwest of Australia), at the point
furthest from Manhattan.
As subsequent avenues remain exactly parallel to Zero Ave, and subsequent
streets exactly parallel to Zero St, they form smaller and smaller circles
@ -121,8 +121,8 @@ Ox.load('Image', function() {
bearings.streets
);
/*
The second intersection of Zero Avenue, Zero Street and Broadway is half of
the Earth's circumference away from the first one, in any direction.
The second intersection of Zero Ave, Zero St and Broadway is half of the
Earth's circumference away from the first one, in any direction.
*/
points['-0 & -0'] = Ox.getPoint(
points['0 & 0'],
@ -279,7 +279,7 @@ Ox.load('Image', function() {
function drawPath(image, path, options) {
var n, parts = [[]];
/*
...
Close the path by appending the first point.
*/
path.push(path[0]);
n = path.length;
@ -316,14 +316,23 @@ Ox.load('Image', function() {
}
/*
...
Now it's time to load our map image.
*/
Ox.Image('jpg/earth1024.jpg', function(image) {
mapSize = image.getSize().width;
/*
First, we draw a circle, centered at the intersection of Zero Ave and
Zero St, with a radius of the quarter of the Earth's circumference. This
is the line that runs through all four poles of our coordinate system.
*/
drawPath(image, Ox.getCircle(points['0 & 0'], C / 4, 8), {
color: 'rgba(255, 255, 255, 0.25)'
});
/*
Then, we draw the streets, avenues and Broadway. Zero St, Zero Ave and
Broadway will be twice as bold as the others.
*/
['streets', 'avenues', 'broadway'].forEach(function(type) {
lines[type].forEach(function(line, i) {
drawPath(image, line, {
@ -333,30 +342,51 @@ Ox.load('Image', function() {
});
});
/*
Now we load the map image as the background of our document, and attach
a few event handlers.
*/
$body.css({
minWidth: mapSize + 'px',
height: mapSize + 'px',
backgroundImage: 'url(' + image.src() + ')'
})
.on({
click: click,
mouseover: mouseover,
mousemove: mousemove,
mouseout: mouseout
click: onClick,
mouseover: onMouseover,
mousemove: onMousemove,
mouseout: onMouseout
});
/*
As an extra feature, we want to provide more detailed renderings at a
higher zoom level, for three points of interest. (Our point in Paris is
the closest Manhattan Grid intersection to the Étoile, a real-world
intersection of twelve large avenues.)
*/
points['Paris'] = {lat: 48.87377, lng: 2.29505};
[
{point: points['0 & 0'], title: 'Manhattan', z: 12},
{point: {lat: 48.87377, lng: 2.29505}, title: 'Paris', z: 13},
{point: getIntersection(points['Paris']), title: 'Paris', z: 13},
{point: poles.north, title: 'Uzbekistan', z: 14}
].forEach(function(marker, i) {
/*
We're trying to make this function as generic as possible: for any
given point and zoom level, it would allow us the retrieve the
corresponding Google Maps tile. Even though we are just using three
local images here, their naming scheme matches the logic of Google
Maps. Manhattan, for example, is "jpg/v=108&x=1206&y=1539&z=12.jpg".
*/
var as = getASByLatLng(marker.point),
g = {s: 256, v: 108, z: marker.z},
xy = getXYByLatLng(marker.point);
Ox.print(as)
Ox.extend(g, Ox.map(Ox.getXYByLatLng(marker.point), function(v) {
return Math.floor(v * Math.pow(2, g.z));
}));
/*
For each point, we add a marker, with a click handler that will
display the corresponding image.
*/
Ox.$('<div>')
.addClass('marker')
.css({
@ -372,15 +402,29 @@ Ox.load('Image', function() {
}
})
.appendTo($body);
/*
Now we load the image.
*/
Ox.Image(Ox.formatString(
'jpg/v={v}&x={x}&y={y}&z={z}.jpg', g
), function(image) {
/*
First, we draw the streets.
*/
if (marker.title == 'Uzbekistan') {
Ox.range(
/*
Uzbekistan, the North Pole of our projection, is a special
case, as the streets run in circles around it. (The exact
number of streets is a float &mdash; 123582.49214895045
&mdash; so the radius of the northernmost street &mdash;
123,582th St &mdash; is 0.492 times the distance between
streets.)
*/
Ox.loop(
distances.streets * (numbers.streets % 1),
2000,
distances.streets
).forEach(function(distance) {
distances.streets,
function(distance) {
var circle = mapLine(Ox.getCircle(
poles.north, distance, precision
), g);
@ -388,16 +432,28 @@ Ox.load('Image', function() {
close: true,
color: colors.streets
});
});
}
);
} else {
/*
Otherwise, we draw all streets from 200 streets "south" to
200 steets "north" of the point, using `getLine`, a helper
function defined below.
*/
Ox.loop(-200, 200, function(street) {
var line = getLine(g, marker.point, as, 'streets', street);
var line = getLine(
g, marker.point, as, 'streets', street
);
image.drawPath(line, {
color: colors.streets,
width: marker.title == 'Paris' || street ? 1 : 2
});
});
}
/*
Next, we draw all avenues from 20 avenues "east" to 20 avenues
"west" of the point.
*/
Ox.loop(-20, 20, function(avenue) {
var line = getLine(g, marker.point, as, 'avenues', avenue);
image.drawPath(line, {
@ -405,21 +461,33 @@ Ox.load('Image', function() {
width: marker.title == 'Paris' || avenue ? 1 : 2
});
});
/*
Then, on the Manhattan tile, we draw Broadway, too.
*/
if (marker.title == 'Manhattan') {
var line = mapLine(Ox.getLine(
Ox.getPoint(marker.point, -10000, bearings.broadway),
Ox.getPoint(marker.point, 10000, bearings.broadway),
1
), g);
image.drawPath(line, {color: 'rgba(0, 0, 255, 0.5)', width: 2});
image.drawPath(line, {
color: 'rgba(0, 0, 255, 0.5)',
width: 2
});
}
/*
Finally, we add the place name (white text with a black shadow).
*/
['black', 'white'].forEach(function(color, i) {
image.drawText(marker.title, [240 - i, 240 - i], {
color: color,
font: 'bold 16px Lucida Grande, sans-serif',
textAlign: 'right'
});
})
});
/*
Now we can put the image into the DOM.
*/
$images[i] = Ox.$('<img>')
.attr({src: image.src()})
.hide()
@ -429,6 +497,31 @@ Ox.load('Image', function() {
});
/*
`getIntersection` is a helper function that returns the coordinates of the
closest intersection from a given point.
*/
function getIntersection(point) {
var as = getASByLatLng(point), d = {};
['avenue', 'street'].forEach(function(type) {
var mod = Ox.mod(as[type], 1);
d[type] = ((mod < 0.5 ? 0 : 1) - mod) * distances[type + 's'];
});
return Ox.getPoint(
Ox.getPoint(
point,
d.street,
as.bearings.avenues
),
d.avenue,
as.bearings.streets
);
}
/*
`getLine` is a helper function that returns the i-th avenue or street from a
given point, as an array of x/y coordinates on the zoomed-in map tile.
*/
function getLine(g, point, as, type, i) {
point = Ox.getPoint(
point,
@ -442,6 +535,12 @@ Ox.load('Image', function() {
), g);
}
/*
`mapLine` is a helper function that, given a line of points (an array of
lat/lng pairs) and an object `g` with the properties `lat`, `lng`, `s`
(tile size) and `z` (zoom level), maps the line to an array of x/y
coordinates on the zoomed-in map tile.
*/
function mapLine(line, g) {
return line.map(function(point) {
var xy = Ox.map(Ox.getXYByLatLng(point), function(value, key) {
@ -451,7 +550,12 @@ Ox.load('Image', function() {
});
}
function click(e) {
/*
Now all that's left is adding our event handlers. Clicking on any point of
the map that is not a marker will hide the currently visible overlay image
(if any).
*/
function onClick(e) {
if (e.target.className != 'marker') {
$images.forEach(function($image) {
$image.hide();
@ -459,23 +563,46 @@ Ox.load('Image', function() {
}
}
function mouseover() {
/*
When the mouse enters the map, show a street sign (which consists of a post
an the actual sign).
*/
function onMouseover() {
$post.show();
$sign.show();
}
function mousemove(e) {
/*
When the mouse moves on the map, update the street sign.
*/
function onMousemove(e) {
/*
In case the mouse is on the overlay image, hide the sign and return.
*/
if (e.target.tagName == 'IMG') {
mouseout();
onMouseout();
return;
}
var left = window.scrollX,
right = left + window.innerWidth,
top = window.scrollY,
/*
`xy` is the actual map pixel the mouse is pointing at...
*/
xy = {x: left + e.clientX, y: top + e.clientY},
/*
... `latlng` is the latitude and longitude of that point ...
*/
latlng = getLatLngByXY(xy),
/*
... and `as` is the equivalent in avenues and streets.
*/
as = getASByXY(xy),
width, height, invertX, invertY;
/*
On the street sign, we display both the avenue/street and the lat/lng
coordinates.
*/
$sign.html(
Ox.formatNumber(as.avenue, 0) + 'th Av & '
+ as.hemisphere + ' '
@ -484,7 +611,12 @@ Ox.load('Image', function() {
+ Ox.formatDegrees(latlng.lat, 'lat') + ' / '
+ Ox.formatDegrees(((latlng.lng + 180) % 360) - 180, 'lng')
+ '</div>'
)
);
/*
As the street sign extends to the top and right of the mouse position,
we have to invert its direction in case the mouse is close to the top or
right edge of the window.
*/
width = $sign.width();
height = $sign.height();
invertX = xy.x + width > right;
@ -496,13 +628,19 @@ Ox.load('Image', function() {
$post.css({
left: xy.x - 1 + 'px',
top: xy.y + (invertY ? 0 : -32 - height) + 'px',
height: $sign.height() + 32 + 'px'
height: height + 32 + 'px'
});
}
function mouseout() {
/*
When the mouse leaves the map, hide the street sign.
*/
function onMouseout() {
$post.hide();
$sign.hide();
}
/*
And that's it!
*/
});