update manhattan grid example
This commit is contained in:
parent
a04eb376ed
commit
d07c8d44e3
1 changed files with 173 additions and 35 deletions
|
@ -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,32 +402,58 @@ 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 — 123582.49214895045
|
||||
— so the radius of the northernmost street —
|
||||
123,582th St — is 0.492 times the distance between
|
||||
streets.)
|
||||
*/
|
||||
Ox.loop(
|
||||
distances.streets * (numbers.streets % 1),
|
||||
2000,
|
||||
distances.streets
|
||||
).forEach(function(distance) {
|
||||
var circle = mapLine(Ox.getCircle(
|
||||
poles.north, distance, precision
|
||||
), g);
|
||||
image.drawPath(circle, {
|
||||
close: true,
|
||||
color: colors.streets
|
||||
});
|
||||
});
|
||||
distances.streets,
|
||||
function(distance) {
|
||||
var circle = mapLine(Ox.getCircle(
|
||||
poles.north, distance, precision
|
||||
), g);
|
||||
image.drawPath(circle, {
|
||||
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!
|
||||
*/
|
||||
});
|
Loading…
Reference in a new issue