diff --git a/examples/manhattan_grid/css/example.css b/examples/manhattan_grid/css/example.css new file mode 100644 index 00000000..fae840c3 --- /dev/null +++ b/examples/manhattan_grid/css/example.css @@ -0,0 +1,44 @@ +body { + margin: 0; + background-repeat: repeat-x; +} +img { + position: fixed; + left: 16px; + top: 16px; + border: 2px solid white; + box-shadow: 0 0 4px black; +} +.marker { + position: absolute; + width: 8px; + height: 8px; + border-radius: 4px; + background: rgb(255, 255, 0); + cursor: pointer; +} +.post { + position: absolute; + width: 2px; + height: 32px; + background-color: white; + box-shadow: 0 0 4px black; + z-index: 1000; +} +.sign { + position: absolute; + padding: 2px 4px; + border: 2px solid white; + background-color: rgba(0, 128, 0, 0.75); + font-family: Lucida Grande, Segoe UI, DejaVu Sans, Lucida Sans Unicode, Helvetica, Arial, sans-serif; + font-size: 16px; + text-align: center; + color: white; + box-shadow: 0 0 4px black; + white-space: nowrap; + z-index: 1001; +} +.latlng { + padding: 2px 0 1px 0; + font-size: 10px; +} diff --git a/examples/manhattan_grid/index.html b/examples/manhattan_grid/index.html new file mode 100644 index 00000000..c0dcd82e --- /dev/null +++ b/examples/manhattan_grid/index.html @@ -0,0 +1,13 @@ + + +
+-90 % 360
,
+ which in JavaScript is -90, Ox.mod(-90, 360) returns 270.
+ */
+ westBroadway: Ox.getPoint(
+ points['0 & 0'],
+ C / 4,
+ Ox.mod(bearings.broadway - 90, 360)
+ ),
+ eastBroadway: Ox.getPoint(
+ points['0 & 0'],
+ C / 4,
+ Ox.mod(bearings.broadway + 90, 360)
+ )
+ };
+ /*
+ Now we calculate circles for Broadway, Avenues and Streets. Ox.getCircle
+ returns an array of lat/lng pairs that form a circle around a given point,
+ with a given radius and a given precision (the circle will have
+ Math.pow(2, precision)
segments).
+ */
+ lines = {
+ /*
+ Since there is only one Broadway, this is an array with just one circle
+ that runs around one of the Broadway Poles, at a distance of a quarter
+ of the Earth's circumference.
+ */
+ broadway: [Ox.getCircle(poles.westBroadway, C / 4, precision)],
+ /*
+ For each 10,000th avenue, we compute a circle around the East Pole.
+ From there, avenues range from -34,966th to 34,966th, so we start at a
+ distance of 966 avenues from the pole, stop once the distance is half
+ of the Earth's circumference (the West Pole), and in each step increase
+ the distance by 10,000 avenues.
+ */
+ avenues: Ox.range(
+ distances.avenues * (numbers.avenues % step),
+ C / 2,
+ distances.avenues * step
+ ).map(function(distance) {
+ return Ox.getCircle(poles.east, distance, precision);
+ }),
+ /*
+ Then we do the same for streets, starting at the South Pole.
+ */
+ streets: Ox.range(
+ distances.streets * (numbers.streets % step),
+ C / 2,
+ distances.streets * step
+ ).map(function(distance) {
+ return Ox.getCircle(poles.south, distance, precision);
+ })
+ };
+ /*
+ Print our data to the console.
+ */
+ Ox.print(JSON.stringify({
+ bearings: bearings,
+ distances: distances,
+ numbers: numbers,
+ points: points,
+ poles: poles
+ }, null, ' '));
+
+ /*
+ Before we start drawing, we define a few helper functions.
+ getXYByLatLng
returns screen coordinates for a given point.
+ We use Ox.getXYByLatLng, which takes a lat/lng pair and returns its x/y
+ position on a 1x1 Mercator position, with {x: 0, y: 0}
at the
+ bottom left and {x: 1, y: 1}
at the top right.
+ */
+ function getXYByLatLng(point) {
+ return Ox.map(Ox.getXYByLatLng(point), function(v) {
+ return v * mapSize;
+ });
+ }
+
+ /*
+ getLatLngByXY
is just the inverse, just like Ox.getLatLngByXY.
+ */
+ function getLatLngByXY(xy) {
+ return Ox.getLatLngByXY(Ox.map(xy, function(v) {
+ return v / mapSize;
+ }));
+ }
+
+ /*
+ getASByLatLng
takes lat/lng and returns avenue/street. To
+ compute the avenue, we subtract the point's distance from the West Pole, in
+ avenues, from the total number of avenues, to compute the street, we
+ subtract the point's distance from the North Pole, in avenues, from the
+ total number of streets. We also return the bearing of the avenues at this
+ point (which form a right angle with the line from the point to the West
+ Pole), the bearing of the streets (at a right angle with the line to the
+ North Pole) and the hemisphere (east or west of Broadway).
+ */
+ function getASByLatLng(point) {
+ var n = Ox.getDistance(point, poles.north),
+ w = Ox.getDistance(point, poles.west);
+ return {
+ avenue: numbers.avenues - w / distances.avenues,
+ street: numbers.streets - n / distances.streets,
+ bearings: {
+ avenues: Ox.mod(Ox.getBearing(point, poles.west) + (
+ w < C / 4 ? -90 : 90
+ ), 360),
+ streets: Ox.mod(Ox.getBearing(point, poles.north) + (
+ n < C / 4 ? -90 : 90
+ ), 360)
+ },
+ hemisphere: Ox.getDistance(point, poles.eastBroadway) < C / 4
+ ? 'E' : 'W'
+ };
+ }
+
+ /*
+ getASByXY
returns avenue and street at the given screen
+ coordinates.
+ */
+ function getASByXY(xy) {
+ return getASByLatLng(getLatLngByXY(xy));
+ }
+
+ /*
+ drawPath
draws a path of lat/lng pairs on an image. For each
+ path segment, we have to check if it crosses the eastern or western edge of
+ the map that splits the Pacific Ocean. Note that our test (a segment
+ crosses the edge if it spans more than 180 degrees longitude) is obviously
+ incorrect, but works in our case, since all segments are sufficiently
+ short.
+ */
+ function drawPath(image, path, options) {
+ var n, parts = [[]];
+ /*
+ ...
+ */
+ path.push(path[0]);
+ n = path.length;
+ Ox.loop(n, function(i) {
+ var lat, lng, split;
+ /*
+ Append each point to the last part.
+ */
+ Ox.last(parts).push(path[i]);
+ if (Math.abs(path[i].lng - path[(i + 1) % n].lng) > 180) {
+ /*
+ If the next line crosses the edge, get the lat/lng of the
+ points where the line leaves and enters the map.
+ */
+ lat = Ox.getCenter(path[i], path[i + 1]).lat;
+ lng = path[i].lng < 0 ? [-180, 180] : [180, -180];
+ /*
+ Append the first point to the last part and create a new part
+ with the second point.
+ */
+ Ox.last(parts).push({lat: lat, lng: lng[0]});
+ parts.push([{lat: lat, lng: lng[1]}]);
+ }
+ });
+ /*
+ We draw each part, translating lat/lng to [x, y].
+ */
+ parts.forEach(function(part) {
+ image.drawPath(part.map(function(point) {
+ var xy = getXYByLatLng(point);
+ return [xy.x, xy.y];
+ }), options);
+ });
+ }
+
+ /*
+ ...
+ */
+ Ox.Image('jpg/earth1024.jpg', function(image) {
+
+ mapSize = image.getSize().width;
+ drawPath(image, Ox.getCircle(points['0 & 0'], C / 4, 8), {
+ color: 'rgba(255, 255, 255, 0.25)'
+ });
+ ['streets', 'avenues', 'broadway'].forEach(function(type) {
+ lines[type].forEach(function(line, i) {
+ drawPath(image, line, {
+ color: colors[type],
+ width: i == lines[type].length / 2 - 0.5 ? 2 : 1
+ });
+ });
+ });
+
+ $body.css({
+ minWidth: mapSize + 'px',
+ height: mapSize + 'px',
+ backgroundImage: 'url(' + image.src() + ')'
+ })
+ .bind({
+ click: click,
+ mouseover: mouseover,
+ mousemove: mousemove,
+ mouseout: mouseout
+ });
+
+ [
+ {point: points['0 & 0'], title: 'Manhattan', z: 12},
+ {point: {lat: 48.87377, lng: 2.29505}, title: 'Paris', z: 13},
+ {point: poles.north, title: 'Uzbekistan', z: 14}
+ ].forEach(function(marker, i) {
+ 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));
+ }));
+ Ox.$('