Having done an interactive topographic Map of Mars, I just couldn't stop: I had to do one for Venus as well.
Alas, Venus is both more difficult and less interesting. More difficult because accurate data is hard to come by. NASA's Magellan mission has used radar to create a topographic map of most of the planet's surface, but this information is considerably less accurate than the Mars Observer Laser Altimeter data. There are also areas not covered by Magellan radar.
Even so, I was eventually able to locate a suitable data set and modify the Java applet below to demonstrate what Venus would look like, were it flooded with an ocean. As it turns out, Venus, once dubbed Earth's "sister planet", is a lot less Earth-like in this respect than Mars! Whereas Mars has a well-defined "coastline" and an ocean (although the true nature of this northern depression remains a subject of debate), the surface of Venus lacks such coastlines and easily identifiable continental landmasses. Venus is also a lot more flat than Mars; set the altitude to -1200 meters, for instance, and you'll notice that all oceans will have completely disappeared. (In contrast, the deepest spot in Earth's oceans is more than 11,000 meters under mean sea level, in the Mariana trench.)
A Venus topographic map is also much less practical than a Martian one. Whereas the problem on Mars is cold and vacuum (both of which can presumably be dealt with by a future, multi-generation terraforming effort), on the surface of Venus the temperature is hot enough to melt lead, and the atmosphere, at 93 times terrestrial pressure, contains some of the most corrosive gases known to man.
To use this program, set the sea level altitude to the level of your choosing (meaningful values are between -5000 and 10000 meters), and click the Redraw button. On slower computers, it may take a bit of time for the map to update itself. Gray represents areas not covered by Magellan radar.
This program utilizes data downloaded from NASA Planetary Data System node at Washington University in St. Louis. The inspiration came from the Astronomy Picture of the Day Web site.
To see information on "live" planetary probes (alas, none near Venus at the moment), please visit my Deep Space Probes page.
Note: this is a work-in-progress. Source code, when considered to be sufficiently stable, will be provided through the open-source Maxima project at maxima.sourceforge.net.
In 1919, Theodor Kaluza proposed an extension to general relativity: using an appropriately constructed fifth dimension, he was able to incorporate electromagnetism into Einstein's theory of gravity. Recently, I endeavored to replicate the most basic of Kaluza's results: the equation of motion for a particle in empty dive-dimensional space, as seen from a four-dimensional perspective.
Now that I am working with Maxima, the question arose: can the same result be reproduced using this computer algebra system?
The answer, surprisingly, is a yes. Well, sort of. Some minor modifications were needed to the itensor package, nothing Earth-shattering really, the changes are probably best described as bug fixes. (If you're a Maxima user, these changes are not yet in the CVS version. Soon...)
One of the key features of the itensor package is the ability to define components of a symbolic tensor in an algorithmic fashion. For instance, you could specify the covariant and contravariant components of the tensor G like this:
While this formalism allows you to define separate equations for different index patterns, it does not make it possible to define components depending in the index value. "Fortunately," the components feature was, until recently, broken. Why do I think that that is good news? Because it taught people an alternate method for defining tensor components, using Maxima's function formalism:
So if this works, why could one not use a function definition that also takes into account the values of the tensor components? Why not indeed. Here is the result of my work, a definition for the five-dimensional Kaluza-Klein metric as a function of the four-dimensional metric, the electromagnetic vector potential, and the scalar component:
These functions use a small set of helper functions. One is used to determine if a predicate can be evaluated (in order to avoid errors when it cannot); one constructs the argument list to a function; and one recursively computes the derivative of a tensor product without evaluation:
One of the conditions on the Kaluza-Klein metric is that the four-dimensional metric components and the electromagnetic vector potential are not dependent on the fifth dimension; i.e., their derivative with respect to the fifth coordinate is zero. This can be expressed through the following definitions:
Equipped with these tools, we can proceed. The first step is to define the metric and the contraction rules for the two metric tensors:
Next, we declare some indices to be 4-dimensional indices:
We may also want to do a DERIVABBREV:TRUE to display derivatives in a more compact format.
The equation of motion of a particle in empty five-dimensional space can be written up as:
The main "trick" is to break down the first component in this equation depending on whether the summation indices B and C are equal to five or are in the 1..4 range:
The resulting equation breaks down into two separate equations depending on the value of A. We're only interested in the case where A is a four dimensional index:
Before we evaluate the Christoffel symbols in the resulting equation, we need to do something to protect the one not containing any 5-indices, since we do not wish to have it expanded:
Now we can evaluate the remaining Christoffel symbols:
We now break this up into two parts depending on whether %1=5:
Now is the time to factor out G55 in order to be able to apply a substitution rule that will match the electromagnetic field tensor:
We're almost done. What remains is contracting the equation, restoring the Christoffel symbol that we protected earlier, and moving one term to the right to recover the usual format:
which is the equation we sought. Except for a little bit of cheating, that is: The Christoffel-symbol in this apparently 4-dimensional equation is, in fact, the 5-dimensional Christoffel symbol. But is that really a surprise? The presence of an electromagnetic field does more than just produce an apparent force on a test particle. It also contains energy, which means it bends spacetime; which should manifest itself in our equation as a change in the Christoffel-symbol. Without further ado, here's the analysis:
I don't know for sure but I'm pretty certain that this is the correct value for the contribution of the electromagnetic field to the metric.
In the venerable tradition of simple C/C++ examples, this Win32 Automation server example from my book, Windows 98/NT Programming Unleashed, displays the string "Hello, World!". However, the string can be manipulated using the COM Automation interface. For example, the following Visual Basic lines can be attached to a button in a form; when the button is clicked, the automation server is invoked and its text is changed.
Sub Command1_Click ()
Dim hello As object
Set hello = CreateObject("HELLO.Application")
hello.text = "Hello from Visual Basic!"
End Sub
Before using it, the automation server must be registered. Copying the following lines to a file named hello.reg and running the Registry editor (regedit hello.reg) shall accomplish this task. You may also want to change the LocalServer32 line to reflect the full path name of hello.exe.
HKEY_CLASSES_ROOT\HELLO.Application = HELLO Application
HKEY_CLASSES_ROOT\HELLO.Application\CLSID = {FEB8C280-FD2D-11ce-87C3-00403321BFAC}
HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC} = HELLO Application
HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}\LocalServer32 = HELLO.EXE /Automation
HKEY_CLASSES_ROOT\CLSID\{FEB8C280-FD2D-11ce-87C3-00403321BFAC}\ProgId = HELLO.Application
Finally, here is the real beast. These 300-odd lines of code can be compiled with Visual C++ using the following command line:
cl hello.cpp user32.lib gdi32.lib ole32.lib oleaut32.lib uuid.lib
The program contains no error-checking code, so if it fails for any reason, odd things will likely happen. As always with free example code of this kind, you are on your own... don't blame me if something goes wrong.
#include <windows.h>
#include <initguid.h>
#ifndef INITGUID
#define INITGUID
#endif
DEFINE_GUID(CLSID_CHello, 0xfeb8c280, 0xfd2d, 0x11ce, 0x87, 0xc3,
0x0, 0x40, 0x33, 0x21, 0xbf, 0xac);
static PARAMDATA rgpDataTEXT = { OLESTR("TEXT"), VT_BSTR };
enum IMETH_CTEXT
{
IMETH_SET = 0,
IMETH_GET,
};
enum IDMEMBER_CTEXT
{
IDMEMBER_TEXT = DISPID_VALUE
};
static METHODDATA rgmdataCHello[] =
{
{ OLESTR("TEXT"), &rgpDataTEXT, IDMEMBER_TEXT, IMETH_SET,
CC_CDECL, 1, DISPATCH_PROPERTYPUT, VT_EMPTY },
{ OLESTR("TEXT"), NULL, IDMEMBER_TEXT, IMETH_GET,
CC_CDECL, 0, DISPATCH_PROPERTYGET, VT_BSTR }
};
INTERFACEDATA idataCHello =
{
rgmdataCHello, 2
};
class CHello;
class CText
{
public:
STDMETHOD_(void, Set)(BSTR text);
STDMETHOD_(BSTR, Get)(void);
CText(CHello *pHello, char *p = NULL);
~CText();
void Paint();
HWND m_hwnd;
private:
char *m_text;
CHello *m_pHello;
};
class CHello : public IUnknown
{
public:
static CHello *Create(char *p);
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(unsigned long, AddRef)(void);
STDMETHOD_(unsigned long, Release)(void);
CHello(char *p = NULL);
CText m_text;
private:
IUnknown *m_punkStdDisp;
unsigned long m_refs;
};
class CHelloCF : public IClassFactory
{
public:
static IClassFactory *Create();
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(unsigned long, AddRef)(void);
STDMETHOD_(unsigned long, Release)(void);
STDMETHOD(CreateInstance)(IUnknown *punkOuter,
REFIID riid, void **ppv);
STDMETHOD(LockServer)(BOOL fLock);
CHelloCF() { m_refs = 1; }
private:
unsigned long m_refs;
};
CHello *pHello;
CText::CText(CHello *pHello, char *p)
{
m_pHello = pHello;
if (p != NULL)
{
m_text = new char[strlen(p) + 1];
strcpy(m_text, p);
}
else m_text = NULL;
m_hwnd = NULL;
}
CText::~CText()
{
delete[] m_text;
}
STDMETHODIMP_(void) CText::Set(BSTR p)
{
char *bf;
int size;
size = WideCharToMultiByte(CP_ACP, NULL, p, -1, NULL, 0, NULL, NULL);
bf = new char[size];
WideCharToMultiByte(CP_ACP, NULL, p, -1, bf, size, NULL, NULL);
delete[] m_text;
if (p != NULL)
{
m_text = new char[strlen(bf) + 1];
strcpy(m_text, bf);
}
else m_text = NULL;
if (m_hwnd != NULL) InvalidateRect(m_hwnd, NULL, TRUE);
}
STDMETHODIMP_(BSTR) CText::Get()
{
static WCHAR *wbf;
BSTR bbf;
int size;
size = MultiByteToWideChar(CP_ACP, 0, m_text, -1, NULL, 0);
wbf = new WCHAR[size];
MultiByteToWideChar(CP_ACP, 0, m_text, -1, wbf, size);
bbf = SysAllocString(wbf);
delete[] wbf;
return bbf;
}
void CText::Paint()
{
HDC hDC;
PAINTSTRUCT paintStruct;
RECT clientRect;
if (m_text != NULL)
{
hDC = BeginPaint(m_hwnd, &paintStruct);
if (hDC != NULL)
{
GetClientRect(m_hwnd, &clientRect);
DPtoLP(hDC, (LPPOINT)&clientRect, 2);
DrawText(hDC, m_text, -1, &clientRect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(m_hwnd, &paintStruct);
}
}
}
CHello *CHello::Create(char *p)
{
ITypeInfo *pTI;
IUnknown *pUnk;
CHello *pHello = new CHello(p);
pHello->AddRef();
CreateDispTypeInfo(&idataCHello, LOCALE_SYSTEM_DEFAULT, &pTI);
CreateStdDispatch(pHello, &(pHello->m_text), pTI, &pUnk);
pTI->Release();
pHello->m_punkStdDisp = pUnk;
return pHello;
}
STDMETHODIMP CHello::QueryInterface(REFIID riid, void **ppv)
{
if (IsEqualIID(riid, IID_IUnknown))
*ppv = this;
else
if (IsEqualIID(riid, IID_IDispatch))
return m_punkStdDisp->QueryInterface(riid, ppv);
else
{
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
}
AddRef();
return NOERROR;
}
STDMETHODIMP_(unsigned long) CHello::AddRef()
{
return ++m_refs;
}
STDMETHODIMP_(unsigned long) CHello::Release()
{
if (--m_refs == 0)
{
if(m_punkStdDisp != NULL) m_punkStdDisp->Release();
PostQuitMessage(0);
delete this;
return 0;
}
return m_refs;
}
#pragma warning(disable:4355)
CHello::CHello(char *p) : m_text(this, p)
{
m_refs = 0;
}
IClassFactory *CHelloCF::Create()
{
return new CHelloCF;
}
STDMETHODIMP CHelloCF::QueryInterface(REFIID riid, void **ppv)
{
if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
{
AddRef();
*ppv = this;
return NOERROR;
}
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(unsigned long) CHelloCF::AddRef()
{
return m_refs++;
}
STDMETHODIMP_(unsigned long) CHelloCF::Release()
{
if (--m_refs == 0)
{
delete this;
return 0;
}
return m_refs;
}
STDMETHODIMP CHelloCF::CreateInstance(IUnknown *punkOuter,
REFIID riid, void **ppv)
{
if(punkOuter != NULL)
return ResultFromScode(CLASS_E_NOAGGREGATION);
return pHello->QueryInterface(riid, ppv);
}
STDMETHODIMP CHelloCF::LockServer(BOOL fLock)
{
return NOERROR;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_PAINT:
pHello->m_text.Paint();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR d3, int nCmdShow)
{
MSG msg;
HWND hwnd;
WNDCLASS wndClass;
IClassFactory *pHelloCF;
unsigned long dwHelloCF = 0;
unsigned long dwRegHello = 0;
OleInitialize(NULL);
pHello = CHello::Create("Hello, World!");
pHelloCF = CHelloCF::Create();
CoRegisterClassObject(CLSID_CHello, pHelloCF,
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwHelloCF);
RegisterActiveObject(pHello, CLSID_CHello, NULL, &dwRegHello);
pHelloCF->Release();
if (hPrevInstance == NULL)
{
memset(&wndClass, 0, sizeof(wndClass));
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszClassName = "HELLO";
if (!RegisterClass(&wndClass)) return FALSE;
}
hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
pHello->m_text.m_hwnd = hwnd;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
DispatchMessage(&msg);
RevokeActiveObject(dwRegHello, NULL);
CoRevokeClassObject(dwHelloCF);
pHello->Release();
OleUninitialize();
return msg.wParam;
}