/******************************************************************** * Functions for making globe * * Example: import globe, display; G = globe_map(200, 10, "data\\gshhs_c.b"); animate(G.render, G.node, G.rotatex, G.rotatez); * ********************************************************************/ load("zegraph.dll", "matrix.dll"); DEG_TO_RAD = 0.017453292519943; // degree to radius __R = null; /******************************************************************** * Callback functions for making globe surface ********************************************************************/ function vertex_func(u, v) { u = DEG_TO_RAD * u; // in case of a matrix, this prevent from modifying the original copy. v = DEG_TO_RAD * v; z = __R * sin(v); r = __R * cos(v); x = r * cos(u); y = r * sin(u); return [x, y, z]; } function normal_func(u, v) { u = DEG_TO_RAD * u; v = DEG_TO_RAD * v; z = sin(v); r = cos(v); x = r * cos(u); y = r * sin(u); return [x, y, z]; } /******************************************************************** * grid lines ********************************************************************/ function globe_grid(r, grid) { grid = integer(grid); global:__R = r; r = 1.001 * r; line = zegraph("line"); xyz = zegraph("vertex"); nor = zegraph("vertex"); line.type("lines"); line.color(0.4, 0.4, 0.4); line.vertex(xyz); line.normal(nor); grid = integer(grid); for (lon = 0.0; lon < 360; lon += grid) { lat = -90 + grid; a = normal_func(lon, lat); lat2 = 90.0 - grid; for (lat += 2.0; lat <= lat2; lat += 2.0) { b = normal_func(lon, lat); xyz.add(r*a[0], r*a[1], r*a[2], r*b[0], r*b[1], r*b[2]); nor.add(a[0], a[1], a[2], b[0], b[1], b[2]); a = b; } } lat2 = 90 - grid; for (lat = -90.0 + grid; lat <= lat2; lat += grid) { lon = 0; a = normal_func(lon, lat); for (lon += 2.0; lon <= 360.0; lon += 2.0) { b = normal_func(lon, lat); xyz.add(r*a[0], r*a[1], r*a[2], r*b[0], r*b[1], r*b[2]); nor.add(a[0], a[1], a[2], b[0], b[1], b[2]); a = b; } } return line; } /******************************************************************** * coastlines ********************************************************************/ function globe_coast(r, fname) { r = real(r); global.__R = r; line = zegraph("line"); xyz = zegraph("vertex"); nor = zegraph("vertex"); line.type("lines"); line.color(1, 1, 1); line.vertex(xyz); line.normal(nor); data = matrix("double"); data.readgshhs(fname, "land"); lon = data[null,0]; lat = data[null,1]; a = normal_func(lon, lat); data[null,0] = a[0]; data[null,1] = a[1]; data.insert(2, a[2]); p = data.ptr(); nor.add(p[0], p[1]); data *= 1.001*r; xyz.add(p[0], p[1]); return line; } /******************************************************************** * (lon, lat) to (x, y, z) projection. Return an array ********************************************************************/ function globe_projection(r, lon, lat) { global:__R = r; return vertex_func(lon, lat); } /******************************************************************** * globe with coastlines and grids * * Input: * r -- radius * grid -- grid of longitude and latitude (in degree) * fname -- file name of GSHHS data * * Output: * Array containing objects of render, scene, root (node), node, * light, grid (line), and coast (line) for further manipulation. * ********************************************************************/ function globe_map(r, grid, fname) { assert(r > 0 & grid > 0); global:__R = r; G = zegraph("render", "scene", "node", "light"); root = zegraph("node"); G.root = root; G.rotatex = -60; G.rotatez = -90; G.grid = globe_grid(r, grid); G.coast = globe_coast(r, fname); sphere = zegraph("polygon"); G.spere = sphere; sphere.sphere(r, 5); sphere.color(0, 0, 0.5); w = integer(3 * r); render = G.render; render.size(w, w); scene = G.scene; scene.viewport(0, 0, w, w); render.add(scene); scene.root(root); node = G.node; node.add(sphere, G.grid, G.coast); node.rotatez(G.rotatez); node.rotatex(G.rotatex); light = G.light; light.position(0.25, 0.25, 1); light.ambient(0.5, 0.5, 0.5); root.add(light, node); return G; } /******************************************************************** * globe with texture map * * Input: * r -- radius * grid -- grid of longitude and latitude (in degree) * fname -- file name of GSHHS data * * Output: * Array containing objects of render, scene, root (node), node, * light, grid (line), and coast (line) for further manipulation. * ********************************************************************/ function globe_texture(r, grid, fname) { assert(r > 0 & grid > 0); r = real(r); global:__R = r; G = zegraph("render", "scene", "node", "light"); G.root = zegraph("node"); G.grid = globe_grid(r, grid); G.rotatex = -60; G.rotatez = -90; w = integer(3 * r); render = G.render; render.size(w, w); root = G.root; scene = G.scene; scene.viewport(0, 0, w, w); render.add(scene); scene.root(root); node = G.node; light = G.light; node.rotatez(G.rotatez); node.rotatex(G.rotatex); // node.add(G.grid); light.position(0.25, 0.25, 1); light.ambient(0.5, 0.5, 0.5); root.add(light, node); node2 = zegraph("node"); texture = zegraph("texture"); polygon = zegraph("polygon"); node2.add(texture, polygon); node2.closed(true); node.add(node2); texture.image(fname, -1, -1, -1); polygon.sphere(r, 5, true); return G; } /******************************************************************** * Greate circle distance on unit globe * * Input: * lon1, lat1 -- longitude/latitude of start point (in degree) * lon2, lat2 -- longitude/latitude of end point (in degree) * * Output: * The distance * ********************************************************************/ function globe_length(lon1, lat1, lon2, lat2) { lon1 = DEG_TO_RAD * lon1; lat1 = DEG_TO_RAD * lat1; lon2 = DEG_TO_RAD * lon2; lat2 = DEG_TO_RAD * lat2; return acos(cos(lat1)*cos(lat2)*cos(lon1-lon2) + sin(lat1)*sin(lat2)); } /******************************************************************** * Create a line object to be drawn as an arc on a sphere surface. * The arc line should be less then 2 on a unit sphere. * * Input: * x1, y1, z1 -- start coordinate on a unit sphere * x2, y2, z2 -- end coordinate on a unit sphere * * Output: * A line object. The interval between vertices is about the * langth of a 1-degree arc. * ********************************************************************/ function globe_lxyz(r, x1, y1, z1, x2, y2, z2) { r = real(r); line = zegraph("line"); vertex = zegraph("vertex"); normal = zegraph("vertex"); line.type("strip"); line.vertex(vertex); line.normal(normal); dx = x2 - x1; dy = y2 - y1; dz = z2 - z1; d = sqrt(dx*dx + dy*dy + dz*dz); n = integer(d*180.0); vertex.add(x1*r, y1*r, z1*r); normal.add(x1, y1, z1); if (n < 1) { vertex.add(x2*r, y2*r, z2*r); normal.add(x2, y2, z2); return line; } dx /= n; dy /= n; dz /= n; for (i = 1; i < n; i++) { x = x1 + i*dx; y = y1 + i*dy; z = z1 + i*dz; d = sqrt(x*x + y*y + z*z); x /= d; y /= d; z /= d; vertex.add(x*r, y*r, z*r); normal.add(x, y, z); } vertex.add(x2*r, y2*r, z2*r); normal.add(x2, y2, z2); return line; } /******************************************************************** * Create a line object to be drawn as an arc on a sphere surface. * The arc line should be less then 2 on a unit sphere. * * Input: * lon1, lat1 -- longitude and latitude of the start point (in degree) * lon2, lat2 -- longitude and latitude of the end point (in degree) * r -- radius of sphere * * Output: * A line object. The interval between vertices is about the langth * of a 1-degree arc. * ********************************************************************/ function globe_line(r, lon1, lat1, lon2, lat2) { z1 = sin(lat1); x1 = cos(lat1) * cos(lon1); y1 = cos(lat1) * sin(lon1); z2 = sin(lat2); x2 = cos(lat2) * cos(lon2); y2 = cos(lat2) * sin(lon2); return globe_lxyz(r, x1, y1, z1, x2, y2, z2); }