+
+/* The various CGI environment variables. Instead of using getenv(),
+ the programmer should refer to these, which are always
+ valid null-terminated strings (they may be empty, but they
+ will never be null). If these variables are used instead
+ of calling getenv(), then it will be possible to save
+ and restore CGI environments, which is highly convenient
+ for debugging. */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiServerSoftware;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiServerName;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiGatewayInterface;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiServerProtocol;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiServerPort;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiRequestMethod;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiPathInfo;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiPathTranslated;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiScriptName;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiQueryString;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiRemoteHost;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiRemoteAddr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiAuthType;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiRemoteUser;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiRemoteIdent;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiContentType;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiAccept;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiUserAgent;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiReferrer;
+
+/* Cookies as sent to the server. You can also get them
+ individually, or as a string array; see the documentation. */
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiCookie;
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+char *cgiSid;
+
+/* A macro providing the same incorrect spelling that is
+ found in the HTTP/CGI specifications */
+#define cgiReferer cgiReferrer
+
+/* The number of bytes of data received.
+ Note that if the submission is a form submission
+ the library will read and parse all the information
+ directly from cgiIn; the programmer need not do so. */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+int cgiContentLength;
+
+/* Pointer to CGI output. The cgiHeader functions should be used
+ first to output the mime headers; the output HTML
+ page, GIF image or other web document should then be written
+ to cgiOut by the programmer. In the standard CGIC library,
+ cgiOut is always equivalent to stdout. */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+FILE *cgiOut;
+
+/* Pointer to CGI input. The programmer does not read from this.
+ We have continued to export it for backwards compatibility
+ so that cgic 1.x applications link properly. */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+FILE *cgiIn;
+
+/* Possible return codes from the cgiForm family of functions (see below). */
+
+typedef enum {
+ cgiFormSuccess,
+ cgiFormTruncated,
+ cgiFormBadType,
+ cgiFormEmpty,
+ cgiFormNotFound,
+ cgiFormConstrained,
+ cgiFormNoSuchChoice,
+ cgiFormMemory,
+ cgiFormNoFileName,
+ cgiFormNoContentType,
+ cgiFormNotAFile,
+ cgiFormOpenFailed,
+ cgiFormIO,
+ cgiFormEOF
+} cgiFormResultType;
+
+/* These functions are used to retrieve form data. See
+ cgic.html for documentation. */
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormString(
+ char *name, char *result, int max);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormStringNoNewlines(
+ char *name, char *result, int max);
+
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormStringSpaceNeeded(
+ char *name, int *length);
+
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormStringMultiple(
+ char *name, char ***ptrToStringArray);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiStringArrayFree(char **stringArray);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormInteger(
+ char *name, int *result, int defaultV);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormIntegerBounded(
+ char *name, int *result, int min, int max, int defaultV);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormDouble(
+ char *name, double *result, double defaultV);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormDoubleBounded(
+ char *name, double *result, double min, double max, double defaultV);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormSelectSingle(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int defaultV);
+
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormSelectMultiple(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int *invalid);
+
+/* Just an alias; users have asked for this */
+#define cgiFormSubmitClicked cgiFormCheckboxSingle
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormCheckboxSingle(
+ char *name);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormCheckboxMultiple(
+ char *name, char **valuesText, int valuesTotal,
+ int *result, int *invalid);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormRadio(
+ char *name, char **valuesText, int valuesTotal,
+ int *result, int defaultV);
+
+/* The paths returned by this function are the original names of files
+ as reported by the uploading web browser and shoult NOT be
+ blindly assumed to be "safe" names for server-side use! */
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileName(
+ char *name, char *result, int max);
+
+/* The content type of the uploaded file, as reported by the browser.
+ It should NOT be assumed that browsers will never falsify
+ such information. */
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileContentType(
+ char *name, char *result, int max);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileSize(
+ char *name, int *sizeP);
+
+typedef struct cgiFileStruct *cgiFilePtr;
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileOpen(
+ char *name, cgiFilePtr *cfpp);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileRead(
+ cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormFileClose(
+ cgiFilePtr cfp);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiCookieString(
+ char *name, char *result, int max);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiCookieInteger(
+ char *name, int *result, int defaultV);
+
+cgiFormResultType cgiCookies(
+ char ***ptrToStringArray);
+
+/* path can be null or empty in which case a path of / (entire site) is set.
+ domain can be a single web site; if it is an entire domain, such as
+ 'boutell.com', it should begin with a dot: '.boutell.com' */
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiHeaderCookieSetString(char *name, char *value,
+ int secondsToLive, char *path, char *domain);
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiHeaderCookieSetInteger(char *name, int value,
+ int secondsToLive, char *path, char *domain);
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiHeaderLocation(char *redirectUrl);
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiHeaderStatus(int status, char *statusMessage);
+extern
+#ifdef __cplusplus
+"C"
+#endif
+void cgiHeaderContentType(char *mimeType);
+
+typedef enum {
+ cgiEnvironmentIO,
+ cgiEnvironmentMemory,
+ cgiEnvironmentSuccess,
+ cgiEnvironmentWrongVersion
+} cgiEnvironmentResultType;
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename);
+extern cgiEnvironmentResultType cgiReadEnvironment(char *filename);
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+int cgiMain();
+extern
+#ifdef __cplusplus
+"C"
+#endif
+int cgiMain_init();
+
+
+extern
+#ifdef __cplusplus
+"C"
+#endif
+cgiFormResultType cgiFormEntries(
+ char ***ptrToStringArray);
+
+/* Output string with the <, &, and > characters HTML-escaped.
+ 's' is null-terminated. Returns cgiFormIO in the event
+ of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiHtmlEscape(char *s);
+
+/* Output data with the <, &, and > characters HTML-escaped.
+ 'data' is not null-terminated; 'len' is the number of
+ bytes in 'data'. Returns cgiFormIO in the event
+ of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiHtmlEscapeData(char *data, int len);
+
+/* Output string with the " character HTML-escaped, and no
+ other characters escaped. This is useful when outputting
+ the contents of a tag attribute such as 'href' or 'src'.
+ 's' is null-terminated. Returns cgiFormIO in the event
+ of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscape(char *s);
+
+/* Output data with the " character HTML-escaped, and no
+ other characters escaped. This is useful when outputting
+ the contents of a tag attribute such as 'href' or 'src'.
+ 'data' is not null-terminated; 'len' is the number of
+ bytes in 'data'. Returns cgiFormIO in the event
+ of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscapeData(char *data, int len);
+
+#endif /* CGI_C */
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgic.html (revision 221)
@@ -0,0 +1,2116 @@
+
+
+cgic: an ANSI C library for CGI Programming
+
+
+cgic 2.05: an ANSI C library for CGI Programming
+
+
+The LATEST documentation is available here. Check often
+for new releases.
+
+IMPORTANT NOTICES:
+
+If you have CGIC 1.05 or earlier, you should upgrade to CGIC 1.07,
+or to CGIC 2.02 or better, in order to obtain important security fixes.
+
+If you have CGIC 2.0 or CGIC 2.01 and you use the cgiCookie routines,
+you should upgrade to CGIC 2.02 or better, in order to obtain
+important security fixes.
+
+Table of Contents
+
+
+
+
+cgic can be used free of charge, provided that a
+credit notice is provided online. Alternatively,
+a nonexclusive Commercial License can be purchased, which
+grants the right to use cgic without a public credit notice.
+
+Please see the file
+license.txt
+for the details of the Basic License and Commercial License,
+including ordering information for the Commercial License.
+
+Thanks are due to Robert Gustavsson, Ken Holervich, Bob Nestor,
+Jon Ribbens, Thomas Strangert, Wu Yongwei, and other CGIC users
+who have corresponded over the years. Although the implementation
+of multipart/form-data file upload support in CGIC 2.x is my own,
+I particularly wish to thank those who submitted their own
+implementations of this feature.
+
+
+STOP! READ THIS FIRST! REALLY!
+ Are you getting a "server error," indicating that your web server
+ "cannot allow POST to this URL," or a similar message? YOU MUST
+ CONFIGURE YOUR WEB SERVER TO ALLOW CGI PROGRAMS, AND YOU MUST
+ INSTALL CGI PROGRAMS IN THE LOCATION (OR WITH THE EXTENSION) THAT
+ YOUR WEB SERVER EXPECTS TO SEE. Please don't send me email about
+ this, unless you wish me to configure your web server for you; I can
+ certainly do that for $50/hr, but you can probably straighten this out
+ yourself or have your web server administrator do it.
+
+Free Support
+Please submit support inquiries about CGIC via our contact page.
+Please note that we receive a large volume of inquiries and cannot always
+respond personally. Sometimes
+the response must take the form of an eventual
+new release or an addition to a FAQ or other document, as opposed to an
+detailed individual response.
+Hourly Support
+Those requiring support in detail may arrange for direct support
+from the author, Thomas Boutell, at the rate of $50/hr, billed
+directly by credit card. To make arrangements, contact us via our
+our secure
+message page. To avoid delay, be sure to specifically mention
+that you wish to purchase CGIC support at the hourly rate above.
+
+Uploaded files properly closed; corrects a resource leak and enables
+file uploads to work properly on platforms with particular file
+locking semantics.
+
+Documentation fixes: the cgiHtmlEscape, cgiHtmlEscapeData,
+cgiValueEscape, and cgiValueEscapeData routines were named
+incorrectly in the manual. No code changes in version 2.04.
+
+
+- Support for setting cookies has been reimplemented. The new
+code closely follows the actual practice of web sites that successfully
+use cookies, rather than attempting to implement the specification.
+The new code can successfully set more than one cookie at a time in
+typical web browsers.
+
+
+
+- In CGIC 2.0 and 2.01, if the HTTP_COOKIE environment variable
+was exactly equal to the name of a cookie requested with cgiCookieString,
+with no value or equal sign or other characters present, a buffer
+overrun could take place. This was not normal behavior and it is
+unknown whether any actual web server would allow it to occur, however
+we have of course released a patch to correct it.
+Thanks to Nicolas Tomadakis.
+
- cgiCookieString returned cgiFormTruncated when cgiFormSuccess would
+be appropriate. Fixed; thanks to Mathieu Villeneuve-Belair.
+
- Cookies are now set using a simpler Set-Cookie: header, and with
+one header line per cookie, based on data collected by Chunfu Lai.
+
- Memory leaks in cgiReadEnvironment fixed by Merezko Oleg. These
+memory leaks were not experienced in a normal CGI situation, only
+when reading a saved CGI environment.
+
+
+
+- Makefile supports "make install"
+
- Compiles without warnings under both C and C++ with strict
+warnings and strict ANSI compliance enabled
+
- Builds out of the box on Windows (#include <fcntl.h> was needed)
+
- Rare problem in cgiReadEnvironment corrected; no impact on
+normal CGI operations
+
- cgiCookieString now sets the result to an empty string
+when returning cgiFormNotFound
+
- Minor code cleanups
+
+
+1. CGIC 2.0 provides support for file upload fields. User-uploaded
+files are kept in temporary files, to avoid the use of
+excessive swap space (Solaris users may wish to change the
+cgicTempDir
macro in cgic.c before compiling).
+The cgiFormFileName
,
+cgiFormFileContentType
,
+cgiFormFileSize
,
+cgiFormFileOpen
,
+cgiFormFileRead
, and
+cgiFormFileClose
functions
+provide a complete interface to this new functionality. Remember,
+the enctype
attribute of the form
tag
+must be set to multipart/form-data
when
+<input type="file">
tags are used.
+
+2. CGIC 2.0 provides support for setting and examining cookies
+(persistent data storage on the browser side).
+The cgiCookieString
,
+and cgiCookieInteger
+and cgiCookies
+functions retrieve cookies. The
+cgiHeaderCookieSetString
+and cgiHeaderCookieSetInteger
functions set cookies.
+
+3. CGIC 2.0 offers a convenient way to retrieve a list of all form fields.
+The new cgiFormEntries
+function performs this operation.
+
+4. CGIC 2.0 provides convenience functions to correctly escape
+text before outputting it as part of HTML, or as part of the
+value of a tag attribute, such as the HREF
or
+VALUE
attribute. See
+cgiHtmlEscape
,
+cgiHtmlEscapeData
,
+cgiValueEscape
and
+cgiValueEscapeData
.
+
+5. Users have often asked the correct way to determine which submit
+button was clicked. This could always be accomplished in previous versions,
+but CGIC 2.0 also provides
+cgiFormSubmitClicked,
+a convenient alternate label for the
+cgiFormCheckboxSingle function.
+
+A problem with the cgiFormString and related functions has been
+corrected. These functions were previously incorrectly returning cgiFormTruncated
+in cases where the returned string fit the buffer exactly.
+
+1. A potentially significant buffer overflow problem has been
+corrected. Jon Ribbens correctly pointed out to me (and to the
+Internet's bugtraq mailing list) that the cgiFormEntryString
+function, which is used directly or indirectly by almost all
+CGIC programs, can potentially write past the buffer passed
+to it by the programmer. This bug has been corrected.
+Upgrading to version 1.06 is strongly recommended.
+
+2. The function cgiSaferSystem()
has been
+removed entirely. This function escaped only a few metacharacters,
+while most shells have many, and there was no way to account for
+the many different operating system shells that might be in use
+on different operating systems. Since this led to a false sense
+of security, the function has been removed. It is our recommendation
+that user input should never be passed directly on the command line
+unless it has been carefully shown to contain only characters
+regarded as safe and appropriate by the programmer. Even then, it is
+better to design your utilities to accept their input from standard
+input rather than the command line.
+
+Non-exclusive commercial license fee reduced to $200.
+
+For consistency with other packages, the standard Makefile
+now produces a true library for cgic (libcgic.a).
+
+Version 1.03 sends line feeds only (ascii 10) to end
+Content-type:, Status:, and other HTTP protocol output lines,
+instead of CR/LF sequences. The standard specifies CR/LF.
+Unfortunately, too many servers reject CR/LF to make
+implementation of that standard practical. No server
+tested ever rejects LF alone in this context.
+
+Version 1.02 corrects bugs in previous versions:
+
+-
+cgiFormDoubleBounded specified
+its arguments in the wrong order, with surprising results.
+This bug has been corrected.
+
-
+Many small changes have been made to increase compatibility.
+cgic now compiles with no warnings under the compilers
+available at boutell.com.
+
+
+Version 1.01 adds no major functionality but corrects
+significant bugs and incompatibilities:
+
+-
+cgiFormInteger,
+cgiFormIntegerBounded,
+cgiFormDouble and
+cgiFormDoubleBounded now
+accept negative numbers properly. They also accept positive
+numbers with an explicit + sign.
+
- Hex values containing the digit
9
are
+now properly decoded.
+ - cgiFormString now
+represents each newline as a single line feed (ascii 10 decimal)
+as described in the documentation, not a carriage return
+(ascii 13 decimal) as in version 1.0. The latter approach
+pleased no one.
+
- cgiFormString and
+cgiFormStringNoNewlines
+no longer erroneously return cgiFormEmpty in place of
+cgiFormSuccess.
+
- The main() function of cgic now flushes standard output
+and sleeps for one second before exiting in order to inhibit
+problems with the completion of I/O on some platforms. This was
+not a cgic bug per se, but has been reported as a common problem
+with CGI when used with the CERN server. This change should
+improve compatibility.
+
- The single selection example in the testform.html
+example now works properly. This was an error in the
+form itself, not cgic.
+
- cgiRemoteUser and
+cgiRemoteIdent are now
+documented accurately. They were reversed earlier.
+
+
+cgic is an ANSI C-language library for the creation of CGI-based
+World Wide Web applications. For basic information about
+the CGI standard, see the
+CGI documentation at NCSA.
+
+cgic performs the following tasks:
+
+- Parses form data, correcting for defective and/or inconsistent browsers
+
- Transparently accepts both GET and POST form data
+
- Accepts uploaded files as well as regular form fields
+
- Provides functions to set and retrieve "cookies"
+(browser-side persistent information)
+
- Handles line breaks in form fields in a consistent manner
+
- Provides string, integer, floating-point, and single- and
+multiple-choice functions to retrieve form data
+
- Provides bounds checking for numeric fields
+
- Loads CGI environment variables into C strings which are always non-null
+
- Provides a way to capture CGI situations for replay in a debugging
+environment, including file uploads and cookies
+
+
+cgic is compatible with any CGI-compliant server environment, and
+compiles without modification in Posix/Unix/Linux and Windows
+environments.
+
+cgic is distributed via the web in two forms: as a Windows-compatible
+.ZIP file, and as a gzipped tar file. Most users of Windows and
+related operating systems have access to 'unzip' or 'pkunzip'. All modern Unix
+systems come with 'gunzip' and 'tar' as standard equipment, and gzip/gunzip
+is not difficult to find if yours does not. Versions
+of these programs for other operating systems are widely
+available if you do not already have them.
+
+Important: to use cgic, you will need an ANSI-standard
+C compiler. Under Unix, just obtain and use gcc. Most Unix systems have
+standardiszed on gcc. Users of Windows operating systems should not have
+ANSI C-related problems as all of the popular compilers follow the ANSI
+standard.
+
+Note for Windows Programmers: you must use a modern
+32-bit compiler. Visual C++ 2.0 or higher, Borland C++ and the
+mingw32 gcc compiler are all appropriate, as is cygwin. Do
+NOT use an ancient 16-bit DOS executable compiler, please.
+
+What Operating System Does Your WEB SERVER Run?
+Remember, the computer on your desk is usually NOT your web server.
+Compiling a Windows console executable will not give you a CGI program that
+can be installed on a Linux-based server.
+
+Your web browser should inquire whether to save the file to disk
+when you select one of the links below. Under Unix and compatible
+operating systems, save it, then issue the following
+commands to unpack it:
+
+gunzip cgic205.tar.gz
+tar -xf cgic205.tar
+
+This should produce the subdirectory 'cgic205', which will contain
+the complete cgic distribution for version 2.05, including a copy of this
+documentation in the file cgic.html.
+
+Under Windows and compatible operating systems, save it,
+open a console ("DOS") window, and issue the following commands to unpack it:
+
+unzip /d cgic205.zip
+
+Or use the unzip utility of your choice.
+
+This command also produces the subdirectory 'cgic205', which will contain
+the complete cgic distribution for version 2.0, including a copy of this
+documentation in the file cgic.html.
+
+cgic is available via the web from www.boutell.com:
+
+
+The sample application 'cgictest.c' is provided as part of the
+cgic distribution. This CGI program displays an input form,
+accepts a submission, and then displays what was submitted.
+In the process essentially all of cgic's features are tested.
+
+On a Unix system, you can build cgictest simply by typing
+'make cgictest.cgi'. cgic.c and cgictest.c will be compiled and linked
+together to produce the cgictest application. Under non-Unix
+operating systems, you will need to create and compile an appropriate
+project containing the files cgic.c and cgictest.c.
+
+IMPORTANT: after compiling cgictest.cgi, you will
+need to place it in a location on your server system which is
+designated by your server administrator as an appropriate location
+for CGI scripts. Some servers are configured to recognize any
+file ending in .cgi as a CGI program when it is found in any
+subdirectory of the server's web space, but this is not always
+the case! The right locations for CGI
+programs vary greatly from one server to another. Resolving
+this issue is between you, your web server administrator,
+and your web server documentation. Before submitting a bug
+report for cgic, make certain that the CGI example programs
+which came with your server do work for you. Otherwise
+it is very likely that you have a server configuration problem.
+
+Once you have moved cgictest.cgi (or cgictest.exe, under Windows)
+to an appropriate cgi directory,
+use the web browser of your choice to access the URL at which
+you have installed it
+(for instance, www.mysite.com/cgi-bin/cgictest.cgi
).
+Fill out the various fields in any manner you wish, then
+select the SUBMIT button.
+
+If all goes well, cgictest.cgi will respond with a page which
+indicates the various settings you submitted. If not,
+please reread the section above regarding the correct location in
+which to install your CGI program on your web server.
+
+
+- Are you using Visual C++ or Borland C++? Did you forget to add
+cgic.c to your project?
+
- Make sure you are using an ANSI C or C++ compiler.
+(All of the Windows compilers are ANSI C compliant.)
+
+If none of the above proves effective, please see the
+section regarding support.
+
+Note: All cgic applications must be linked to the cgic.c module
+itself. How to do this depends on your operating system; under Unix,
+just use the provided Makefile as an example.
+
+Since all CGI applications must perform certain initial
+tasks, such as parsing form data and examining
+environment variables, the cgic library provides its
+own main() function. When you write applications that
+use cgic, you will begin your own programs by writing
+a cgiMain() function, which cgic will invoke when
+the initial cgi work has been successfully completed. Your
+program must also be sure to #include the file cgic.h.
+
+Important: if you write your own main()
+function, your program will not link properly. Your own
+code should begin with cgiMain(). The library
+provides main() for you. (Those who prefer different behavior
+can easily modify cgic.c.)
+
+Consider the cgiMain function of cgictest.c:
+
+
+int cgiMain() {
+#ifdef DEBUG
+ LoadEnvironment();
+#endif /* DEBUG */
+ /* Load a previously saved CGI scenario if that button
+ has been pressed. */
+ if (cgiFormSubmitClicked("loadenvironment") == cgiFormSuccess) {
+ LoadEnvironment();
+ }
+ /* Set any new cookie requested. Must be done *before*
+ outputting the content type. */
+ CookieSet();
+ /* Send the content type, letting the browser know this is HTML */
+ cgiHeaderContentType("text/html");
+ /* Top of the page */
+ fprintf(cgiOut, "<HTML><HEAD>\n");
+ fprintf(cgiOut, "<TITLE>cgic test</TITLE></HEAD>\n");
+ fprintf(cgiOut, "<BODY><H1>cgic test</H1>\n");
+ /* If a submit button has already been clicked, act on the
+ submission of the form. */
+ if ((cgiFormSubmitClicked("testcgic") == cgiFormSuccess) ||
+ cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess)
+ {
+ HandleSubmit();
+ fprintf(cgiOut, "<hr>\n");
+ }
+ /* Now show the form */
+ ShowForm();
+ /* Finish up the page */
+ fprintf(cgiOut, "</BODY></HTML>\n");
+ return 0;
+}
+
+Note the DEBUG #ifdef. If DEBUG is defined at compile time, either by
+inserting the line "#define DEBUG 1" into the program or by setting
+it in the Makefile or other development environment, then the
+LoadEnvironment function is invoked. This function calls
+cgiReadEnvironment()
+to restore a captured CGI environment for debugging purposes. See
+also the discussion of the capture program, which is
+provided for use in CGI debugging. Because this is a test program,
+the cgiFormSubmitClicked function is
+also called to check for the use of a button that requests the reloading
+of a saved CGI environment. A completed CGI program typically would
+never allow the end user to make that decision.
+Setting Cookies
+Next, one of the cgiHeader functions should be called.
+This particular program demonstrates many features, including
+the setting of cookies. If the programmer wishes to set a cookie,
+the cookie-setting function must be called
+first, before other headers are output. This is done by the
+CookieSet() function of cgictest.c:
+
+void CookieSet()
+{
+ char cname[1024];
+ char cvalue[1024];
+ /* Must set cookies BEFORE calling
+ cgiHeaderContentType */
+ cgiFormString("cname", cname, sizeof(cname));
+ cgiFormString("cvalue", cvalue, sizeof(cvalue));
+ if (strlen(cname)) {
+ /* Cookie lives for one day (or until
+ browser chooses to get rid of it, which
+ may be immediately), and applies only to
+ this script on this site. */
+ cgiHeaderCookieSetString(cname, cvalue,
+ 86400, cgiScriptName, cgiServerName);
+ }
+}
+
+Since this is a test program, the cgiFormString
+function is used to fetch the name and value from the form previously filled
+in by the user. Normally, cookie names and values are chosen to meet the
+needs of the programmer and provide a means of identifying the same
+user again later.
+
+The cgiHeaderCookieSetString
+function sets the cookie by requesting that the web browser store it.
+There is never any guarantee that this will happen!
+Many browsers reject cookies completely; others do not necessarily keep
+them as long as requested or return them with their values intact.
+Always code defensively when using cookies.
+
+The cname and cvalue parameters are of course the namd and value for
+the cookie. The third argument is the time, in seconds, that the
+cookie should "live" on the browser side before it expires; in this
+case it has been set to 86,400 seconds, which is exactly one day.
+The browser may or may not respect this setting, as with everything
+else about cookies.
+
+The fourth argument identifies the "path" within the web site for which
+the cookie is considered valid. A cookie that should be sent back
+for every access to the site should be set with a path of /
.
+In this case the cookie is relevant only to the CGI program itself, so
+cgiScriptName
(the URL of the CGI program, not including the
+domain name) is sent. Similarly, a cookie can be considered relevant
+to a single web site or to an entire domain, such as
+www.boutell.com
or the entire .boutell.com
+domain. In this case, the current site on which the program is running
+is the only relevant site, so cgiServerName
is used
+as the domain.
+
Outputting the Content Type Header
+Next, cgiHeaderContentType() is
+called to indicate the MIME type of the document being output, in this case
+"text/html" (a normal HTML document). A few other common MIME types are
+"image/gif", "image/jpeg" and "audio/wav".
+
+Note that cgiHeaderStatus() or
+cgiHeaderLocation() could have
+been invoked instead to output an error code or redirect the
+request to a different URL. Only one of the cgiHeader functions
+should be called in a single execution of the program.
+
+Important: one of the cgiHeader functions,
+usually cgiHeaderContentType(),
+must be invoked before outputting any other
+response to the user. Otherwise, the result will not be a valid
+document and the browser's behavior will be unpredictable.
+You may, of course, output your own ContentType and other
+header information to cgiOut if you prefer. The cgiHeader functions
+are provided as a convenience.
+
Handling Form Submissions
+Like many CGI programs, cgictest makes decisions about the way it
+should behave based on whether various submit buttons have been clicked.
+When either the testcgic or saveenvironment button is present, cgictest
+invokes the HandleSubmit function, which invokes additional functions to
+handle various parts of the form:
+
+void HandleSubmit()
+{
+ Name();
+ Address();
+ Hungry();
+ Temperature();
+ Frogs();
+ Color();
+ Flavors();
+ NonExButtons();
+ RadioButtons();
+ File();
+ Entries();
+ Cookies();
+ /* The saveenvironment button, in addition to submitting
+ the form, also saves the resulting CGI scenario to
+ disk for later replay with the 'load saved environment'
+ button. */
+ if (cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) {
+ SaveEnvironment();
+ }
+}
+
+Handling Text Input
+The Name() function of cgictest is shown below, in its simplest
+possible form:
+
+void Name() {
+ char name[81];
+ cgiFormStringNoNewlines("name", name, 81);
+ fprintf(cgiOut, "Name: ");
+ cgicHtmlEscape(name);
+ fprintf(cgiOut, "
\n");
+}
+
+The purpose of this function is to retrieve and display the name that was
+input by the user. Since the programmer has decided that names should
+be permitted to have up to 80 characters, a buffer of 81 characters
+has been declared (allowing for the final null character).
+The cgiFormStringNoNewlines()
+function is then invoked to retrieve the name and ensure that
+carriage returns are not present in the name (despite the
+incorrect behavior of some web browsers). The first argument
+is the name of the input field in the form, the second argument
+is the buffer to which the data should be copied, and the third
+argument is the size of the buffer. cgic will never write beyond
+the size of the buffer, and will always provide a null-terminated
+string in response; if the buffer is too small, the string will
+be shortened. If this is not acceptable, the
+cgiFormStringSpaceNeeded()
+function can be used to check the amount of space needed; the
+return value of cgiFormStringNoNewlines() can also be checked
+to determine whether truncation occurred. See
+the full description of
+cgiFormStringNoNewlines().
+Handling Output
+Note that Name() writes its HTML output to cgiOut, not
+to stdout.
+
+The actual name submitted by the user may or may not contain
+characters that have special meaning in HTML, specifically the
+the <
, >
, and &
characters.
+The cgiHtmlEscape function is used to output
+the user-entered name with any occurrences of these characters
+correctly escaped as <
, >
,
+and &
.
+
+Important: cgiOut is normally equivalent
+to stdout, and there is no performance penalty for using it.
+It is recommended that you write output to cgiOut to ensure compatibility
+with modified versions of the cgic library for special
+environments that do not provide stdin and stdout for
+each cgi connection.
+
+Note that, for text input areas in which carriage returns are
+desired, the function cgiFormString
+should be used instead. cgiFormString ensures that line breaks
+are always represented by a single carriage return (ascii decimal 13),
+making life easier for the programmer. See the source code to
+the Address() function of cgictest.c for an example.
+
Handling Single Checkboxes
+Consider the Hungry() function, which determines whether
+the user has selected the "hungry" checkbox:
+
+void Hungry() {
+ if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) {
+ fprintf(cgiOut, "I'm Hungry!<BR>\n");
+ } else {
+ fprintf(cgiOut, "I'm Not Hungry!<BR>\n");
+ }
+}
+
+This function takes advantage of the
+cgiFormCheckboxSingle() function, which
+determines whether a single checkbox has been selected.
+cgiFormCheckboxSingle() accepts the name attribute of the checkbox
+as its sole argument and returns
+cgiFormSuccess if the checkbox is selected, or
+cgiFormNotFound if it is not.
+If multiple checkboxes with the same name are in use,
+consider the
+cgiFormCheckboxMultiple() and
+cgiFormStringMultiple()
+functions.
+Handling Numeric Input
+Now consider the Temperature() function, which retrieves
+a temperature in degrees (a floating-point value) and ensures
+that it lies within particular bounds:
+
+void Temperature() {
+ double temperature;
+ cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6);
+ fprintf(cgiOut, "My temperature is %f.<BR>\n", temperature);
+}
+
+The temperature is retrieved by the function
+cgiFormDoubleBounded(). The first
+argument is the name of the temperature input field in the form;
+the second argument points to the address of the variable that will
+contain the result. The next two arguments are the lower and upper
+bounds, respectively. The final argument is the default value to
+be returned if the user did not submit a value.
+
+This function always retrieves a reasonable value within the
+specified bounds; values above or below bounds are constrained
+to fit the bounds. However, the return value of
+cgiFormDoubleBounded can be checked to make sure the
+actual user entry was in bounds, not blank, and so forth;
+see the description of
+cgiFormDoubleBounded() for more details. If bounds checking
+is not desired, consider using
+cgiFormDouble() instead.
+
+Note that, for integer input, the functions
+cgiFormInteger and
+cgiFormIntegerBounded
+are available. The behavior of these functions is similar to
+that of their floating-point counterparts above.
+
Handling Single-Choice Input
+The <SELECT> tag of HTML is used to provide the user with
+several choices. Radio buttons and checkboxes can also be used
+when the number of choices is relatively small. Consider
+the Color() function of cgictest.c:
+
+char *colors[] = {
+ "Red",
+ "Green",
+ "Blue"
+};
+
+void Color() {
+ int colorChoice;
+ cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0);
+ fprintf(cgiOut, "I am: %s<BR>\n", colors[colorChoice]);
+}
+
+This function determines which of several colors the user chose
+from a <SELECT> list in the form. An array of colors is
+declared; the cgiFormSelectSingle()
+function is then invoked to determine which, if any, of those choices
+was selected. The first argument indicates the name of the input
+field in the form. The second argument points to the list of
+acceptable colors. The third argument indicates the number of
+entries in the color array. The fourth argument points to the
+variable which will accept the chosen color, and the last argument
+indicates the index of the default value to be set if no
+selection was submitted by the browser.
+
+cgiFormSelectSingle() will
+always indicate a reasonable selection value. However, if
+the programmer wishes to know for certain that a value was
+actually submitted, that the value submitted was a legal
+response, and so on, the return value of cgiFormSelectSingle()
+can be consulted. See the full description of
+cgiFormSelectSingle() for
+more information.
+
+Note that radio button groups and <SELECT> lists can both
+be handled by this function. If you are processing radio
+button groups, you may prefer to invoke
+cgiFormRadio(), which functions
+identically.
+
+"What if I won't know the acceptable choices at runtime?"
+
+If the acceptable choices aren't known until runtime,
+one can simply load the choices from disk. But if the acceptable
+choices aren't fixed at all (consider a list of country names;
+new names may be added to the form at any time and it is
+inconvenient to also update program code or a separate list
+of countries), simply invoke
+cgiFormStringNoNewlines()
+instead to retrieve the string directly. Keep in mind that, if
+you do so, validating the response to make sure it is
+safe and legitimate becomes a problem for your own
+program to solve. The advantage of cgiFormSelectSingle() is that invalid
+responses are never returned.
+
+To handle multiple-selection <SELECT> lists and
+groups of checkboxes with the same name, see the
+discussion of the NonExButtons() function of cgictest.c, immediately below.
+
Handling Multiple-Choice Input
+Consider the first half of the NonExButtons() function of cgictest.c:
+
+char *votes[] = {
+ "A",
+ "B",
+ "C",
+ "D"
+};
+
+void NonExButtons() {
+ int voteChoices[4];
+ int i;
+ int result;
+ int invalid;
+
+ char **responses;
+
+ /* Method #1: check for valid votes. This is a good idea,
+ since votes for nonexistent candidates should probably
+ be discounted... */
+ fprintf(cgiOut, "Votes (method 1):<BR>\n");
+ result = cgiFormCheckboxMultiple("vote", votes, 4,
+ voteChoices, &invalid);
+ if (result == cgiFormNotFound) {
+ fprintf(cgiOut, "I hate them all!<p>\n");
+ } else {
+ fprintf(cgiOut, "My preferred candidates are:\n");
+ fprintf(cgiOut, "<ul>\n");
+ for (i=0; (i < 4); i++) {
+ if (voteChoices[i]) {
+ fprintf(cgiOut, "<li>%s\n", votes[i]);
+ }
+ }
+ fprintf(cgiOut, "</ul>\n");
+ }
+
+This function takes advantage of
+cgiFormCheckboxMultiple(),
+which is used to identify one or more selected checkboxes with
+the same name. This function performs identically to
+cgiFormSelectMultiple().
+That is, <SELECT> tags with the MULTIPLE attribute are handled
+just like a group of several checkboxes with the same name.
+
+The first argument to
+cgiFormCheckboxMultiple() is the name given to all
+checkbox input fields in the group. The second argument
+points to an array of legitimate values; these should
+correspond to the VALUE attributes of the checkboxes
+(or OPTION tags in a <SELECT> list). The third argument
+indicates the number of entries in the array of
+legitimate values. The fourth argument points to
+an array of integers with the same number of entries
+as the array of legitimate values; each entry
+will be set true if that checkbox or option was selected,
+false otherwise.
+
+The last argument points to an integer which will be set to the
+number of invalid responses (responses not in the array of
+valid responses) that were submitted. If this value is not
+of interest, the last argument may be a null pointer (0).
+
+Note that the return value of cgiFormCheckboxMultiple is
+inspected to determine whether any choices at all were
+set. See the full description of
+cgiFormCheckboxMultiple
+for other possible return values.
+
+"What if I won't know the acceptable choices at runtime?"
+
+If the acceptable choices aren't known until runtime,
+one can simply load the choices from disk. But if the acceptable
+choices aren't fixed at all (consider a list of ice cream flavors;
+new names may be added to the form at any time and it is
+inconvenient to also update program code or a separate list
+of countries), a more dynamic approach is needed. Consider
+the second half of the NonExButtons() function of cgictest.c:
+
+ /* Method #2: get all the names voted for and trust them.
+ This is good if the form will change more often
+ than the code and invented responses are not a danger
+ or can be checked in some other way. */
+ fprintf(cgiOut, "Votes (method 2):<BR>\n");
+ result = cgiFormStringMultiple("vote", &responses);
+ if (result == cgiFormNotFound) {
+ fprintf(cgiOut, "I hate them all!<p>\n");
+ } else {
+ int i = 0;
+ fprintf(cgiOut, "My preferred candidates are:\n");
+ fprintf(cgiOut, "<ul>\n");
+ while (responses[i]) {
+ fprintf(cgiOut, "<li>%s\n", responses[i]);
+ i++;
+ }
+ fprintf(cgiOut, "</ul>\n");
+ }
+ /* We must be sure to free the string array or a memory
+ leak will occur. Simply calling free() would free
+ the array but not the individual strings. The
+ function cgiStringArrayFree() does the job completely. */
+ cgiStringArrayFree(responses);
+}
+
+This code excerpt demonstrates an alternate means of retrieving
+a list of choices. The function
+cgiFormStringMultiple() is used
+to retrieve an array consisting of all the strings submitted
+for with a particular input field name. This works both for
+<SELECT> tags with the MULTIPLE attribute and for
+groups of checkboxes with the same name.
+
+The first argument to
+cgiFormStringMultiple() is the name of the input field or
+group of input fields in question. The second argument should
+be the address of a pointer to a pointer to a string, which
+isn't as bad as it sounds. Consider the following simple call
+of the function:
+
+/* An array of strings; each C string is an array of characters */
+char **responses;
+
+cgiFormStringMultiple("vote", &responses);
+
+"How do I know how many responses there are?"
+
+After the call, the last entry in the string array will be
+a null pointer. Thus the simple loop:
+
+int i = 0;
+while (responses[i]) {
+ /* Do something with the string responses[i] */
+ i++;
+}
+
+can be used to walk through the array until the last
+entry is encountered.
+
+Important: the
+cgiFormStringMultiple function
+returns a pointer to allocated memory. Your code
+should not modify the strings in the responses array or the responses
+array itself; if modification is needed, the strings should be
+copied. When your code is done examining the responses array,
+you MUST call
+cgiStringArrayFree() with the array as an argument to free the memory
+associated with the array. Otherwise, the memory will not be available
+again until the program exists. Don't just call the
+free() function; if you do, the individual strings will not be freed.
+
Accessing Uploaded Files
+CGIC provides functions to access files that have been uploaded
+as part of a form submission. IMPORTANT: you MUST set
+the enctype
attribute of your form
tag
+to multipart/form-data
for this feature to work! For an
+example, see the ShowForm function of
+cgictest.c, examined below.
+
+The File
function of cgictest.c takes care of
+receiving uploaded files:
+
+void File()
+{
+ cgiFilePtr file;
+ char name[1024];
+ char contentType[1024];
+ char buffer[1024];
+ int size;
+ int got;
+ if (cgiFormFileName("file", name, sizeof(name)) !=
+ cgiFormSuccess)
+ {
+ printf("<p>No file was uploaded.<p>\n");
+ return;
+ }
+ fprintf(cgiOut, "The filename submitted was: ");
+ cgiHtmlEscape(name);
+ fprintf(cgiOut, "<p>\n");
+ cgiFormFileSize("file", &size);
+ fprintf(cgiOut, "The file size was: %d bytes<p>\n", size);
+ cgiFormFileContentType("file", contentType, sizeof(contentType));
+ fprintf(cgiOut, "The alleged content type of the file was: ");
+ cgiHtmlEscape(contentType);
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Of course, this is only the claim the browser "
+ "made when uploading the file. Much like the filename, "
+ "it cannot be trusted.<p>\n");
+ fprintf(cgiOut, "The file's contents are shown here:<p>\n");
+ if (cgiFormFileOpen("file", &file) != cgiFormSuccess) {
+ fprintf(cgiOut, "Could not open the file.<p>\n");
+ return;
+ }
+ fprintf(cgiOut, "<pre>\n");
+ while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==
+ cgiFormSuccess)
+ {
+ cgiHtmlEscapeData(buffer, got);
+ }
+ fprintf(cgiOut, "</pre>\n");
+ cgiFormFileClose(file);
+}
+
+First, the File function checks to determine the filename that was
+submitted by the user. VERY IMPORTANT: this filename may or
+may not bear any relation to the real name of the file on the user's
+computer, may be deliberately manipulated with malicious intent,
+and should not be used for any purpose unless you have
+determined that its content is safe for your intended use and will not,
+at the very least, overwrite another file of importance to you, especially if
+you intend to use it as a file name on the server side. The cgic library
+itself does not use this file name for temporary storage.
+
+If the cgiFormFileName function does
+not succeed, no file was uploaded.
+
+Next, the cgiFormFileSize function is called
+to determine the size of the uploaded file, in bytes.
+
+The File function then proceeds to query the content type of the uploaded
+file. Files uploaded by the user have their own content type information,
+which may be useful in determining whether the file is an image, HTML document,
+word processing document, or other type of file. However,
+as with the filename and any other claim made by the browser,
+this information should not be blindly trusted. The browser
+may upload a file with the name picture.jpg
and the
+content type image/jpeg
, but this does not guarantee that the
+actual file will contain a valid JPEG image suitable for display.
+
+The content type submitted by the browser can be queried using the
+cgiFormFileContentType function.
+
+Of course, CGIC also provides access to the actual uploded file.
+First, the programmer calls cgiFormFileOpen,
+passing the address of a cgiFilePtr
object. If this function
+succeeds, the cgiFilePtr
object becomes valid, and can be
+used in subsequent calls to cgiFormFileRead.
+Notice that the number of bytes read may be less than the number requested,
+in particular on the last successful call before cgiFormFileRead begins
+to return cgiFormEOF
. When cgiFormFileRead no longer returns
+cgiFormSuccess,
+the programmer calls cgiFormFileClose to
+release the cgiFilePtr
object.
+
+The uploaded file data may contain anything, including binary data,
+null characters, and so on. The example program uses the
+cgiHtmlEscapeData function to output the
+data with any special characters that have meaning in HTML escaped.
+Most programs will save the uploaded information to a server-side file or
+database.
+
Fetching All Form Entries
+From time to time, the programmer may not know the names of all
+form fields in advance. In such situations it is convenient to
+use the cgiFormEntries function.
+The Entries function of cgictest.c demonstrates the use of
+cgiFormEntries:
+
+void Entries()
+{
+ char **array, **arrayStep;
+ fprintf(cgiOut, "List of All Submitted Form Field Names:<p>\n");
+ if (cgiFormEntries(&array) != cgiFormSuccess) {
+ return;
+ }
+ arrayStep = array;
+ fprintf(cgiOut, "<ul>\n");
+ while (*arrayStep) {
+ fprintf(cgiOut, "<li>");
+ cgiHtmlEscape(*arrayStep);
+ fprintf(cgiOut, "\n");
+ arrayStep++;
+ }
+ fprintf(cgiOut, "</ul>\n");
+ cgiStringArrayFree(array);
+}
+
+The cgiFormEntries function retrieves an array of form field names.
+This array consists of pointers to strings, with a final null pointer
+to mark the end of the list. The above code illustrates one way of
+looping through the returned strings. Note the final call to
+cgiStringArrayFree, which is
+essential in order to return the memory used to store the strings
+and the string array.
+Retrieving Cookies
+The Cookies function of cgictest.c displays a list of all cookies
+submitted by the browser with the current form submission, along
+with their values:
+
+void Cookies()
+{
+ char **array, **arrayStep;
+ char cname[1024], cvalue[1024];
+ fprintf(cgiOut, "Cookies Submitted On This Call, With Values "
+ "(Many Browsers NEVER Submit Cookies):<p>\n");
+ if (cgiCookies(&array) != cgiFormSuccess) {
+ return;
+ }
+ arrayStep = array;
+ fprintf(cgiOut, "<table border=1>\n");
+ fprintf(cgiOut, "<tr><th>Cookie<th>Value</tr>\n");
+ while (*arrayStep) {
+ char value[1024];
+ fprintf(cgiOut, "<tr>");
+ fprintf(cgiOut, "<td>");
+ cgiHtmlEscape(*arrayStep);
+ fprintf(cgiOut, "<td>");
+ cgiCookieString(*arrayStep, value, sizeof(value));
+ cgiHtmlEscape(value);
+ fprintf(cgiOut, "\n");
+ arrayStep++;
+ }
+ fprintf(cgiOut, "</table>\n");
+ cgiFormString("cname", cname, sizeof(cname));
+ cgiFormString("cvalue", cvalue, sizeof(cvalue));
+ if (strlen(cname)) {
+ fprintf(cgiOut, "New Cookie Set On This Call:<p>\n");
+ fprintf(cgiOut, "Name: ");
+ cgiHtmlEscape(cname);
+ fprintf(cgiOut, "Value: ");
+ cgiHtmlEscape(cvalue);
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "If your browser accepts cookies "
+ "(many do not), this new cookie should appear "
+ "in the above list the next time the form is "
+ "submitted.<p>\n");
+ }
+ cgiStringArrayFree(array);
+}
+
+VERY IMPORTANT: YOUR BROWSER MIGHT NOT SUBMIT COOKIES,
+EVER, REGARDLESS OF WHAT VALUES YOU ENTER INTO THE TEST FORM.
+Many, many browsers are configured not to accept or send cookies;
+others are configured to send them as little as possible to meet the
+bare minimum requirements for entry into popular sites. Users will often
+refuse your cookies; make sure your code still works in that situation!
+
+The above code uses the cgiCookies function
+to retrieve a list of all currently set cookies as a null-terminated
+array of strings. The cgiCookieString
+function is then used to fetch the value associated with each cookie;
+this function works much like cgiFormString,
+discussed earlier. Note that a cookie set as a part of the current
+form submission process does not appear on this list immediately, as
+it has not yet been sent back by the browser. It should appear on
+future submissions, provided that the browser chooses to accept
+and resend the cookie at all.
+
Displaying a Form That Submits to the Current Program
+CGI programmers often need to display HTML pages as part of the output
+of CGI programs; these HTML pages often contain forms which should submit
+fields back to the same program they came from. Provided that your
+web server is well-configured, this can be done conveniently using
+the cgiScriptName environment variable, as shown below. Here is the
+source code of the ShowForm function of cgictest.c:
+
+void ShowForm()
+{
+ fprintf(cgiOut, "<!-- 2.0: multipart/form-data is required
+ "for file uploads. -->");
+ fprintf(cgiOut, "<form method=\"POST\" "
+ "enctype=\"multipart/form-data\" ");
+ fprintf(cgiOut, " action=\"");
+ cgiValueEscape(cgiScriptName);
+ fprintf(cgiOut, "\">\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Text Field containing Plaintext\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<input type=\"text\" name=\"name\">Your Name\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Multiple-Line Text Field\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<textarea NAME=\"address\" ROWS=4 COLS=40>\n");
+ fprintf(cgiOut, "Default contents go here. \n");
+ fprintf(cgiOut, "</textarea>\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Checkbox\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<input type=\"checkbox\" name=\"hungry\" checked>Hungry\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Text Field containing a Numeric Value\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<input type=\"text\" name=\"temperature\" value=\"98.6\">\n");
+ fprintf(cgiOut, "Blood Temperature (80.0-120.0)\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Text Field containing an Integer Value\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<input type=\"text\" name=\"frogs\" value=\"1\">\n");
+ fprintf(cgiOut, "Frogs Eaten\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "Single-SELECT\n");
+ fprintf(cgiOut, "<br>\n");
+ fprintf(cgiOut, "<select name=\"colors\">\n");
+ fprintf(cgiOut, "<option value=\"Red\">Red\n");
+ fprintf(cgiOut, "<option value=\"Green\">Green\n");
+ fprintf(cgiOut, "<option value=\"Blue\">Blue\n");
+ fprintf(cgiOut, "</select>\n");
+ fprintf(cgiOut, "<br>\n");
+ fprintf(cgiOut, "Multiple-SELECT\n");
+ fprintf(cgiOut, "<br>\n");
+ fprintf(cgiOut, "<select name=\"flavors\" multiple>\n");
+ fprintf(cgiOut, "<option value=\"pistachio\">Pistachio\n");
+ fprintf(cgiOut, "<option value=\"walnut\">Walnut\n");
+ fprintf(cgiOut, "<option value=\"creme\">Creme\n");
+ fprintf(cgiOut, "</select>\n");
+ fprintf(cgiOut, "<p>Exclusive Radio Button Group: Age of "
+ "Truck in Years\n");
+ fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
+ "value=\"1\">1\n");
+ fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
+ "value=\"2\">2\n");
+ fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
+ "value=\"3\" checked>3\n");
+ fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
+ "value=\"4\">4\n");
+ fprintf(cgiOut, "<p>Nonexclusive Checkbox Group: "
+ "Voting for Zero through Four Candidates\n");
+ fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
+ "value=\"A\">A\n");
+ fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
+ "value=\"B\">B\n");
+ fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
+ "value=\"C\">C\n");
+ fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
+ "value=\"D\">D\n");
+ fprintf(cgiOut, "<p>File Upload:\n");
+ fprintf(cgiOut, "<input type=\"file\" name=\"file\" "
+ "value=\"\"> (Select A Local File)\n");
+ fprintf(cgiOut, "<p>\n");
+ fprintf(cgiOut, "<p>Set a Cookie<p>\n");
+ fprintf(cgiOut, "<input name=\"cname\" "
+ "value=\"\"> Cookie Name\n");
+ fprintf(cgiOut, "<input name=\"cvalue\" "
+ "value=\"\"> Cookie Value<p>\n");
+ fprintf(cgiOut, "<input type=\"submit\" "
+ "name=\"testcgic\" value=\"Submit Request\">\n");
+ fprintf(cgiOut, "<input type=\"reset\" "
+ "value=\"Reset Request\">\n");
+ fprintf(cgiOut, "<p>Save the CGI Environment<p>\n");
+ fprintf(cgiOut, "Pressing this button will submit the form, then "
+ "save the CGI environment so that it can be replayed later "
+ "by calling cgiReadEnvironment (in a debugger, for "
+ "instance).<p>\n");
+ fprintf(cgiOut, "<input type=\"submit\" name=\"saveenvironment\" "
+ "value=\"Save Environment\">\n");
+ fprintf(cgiOut, "</form>\n");
+}
+
+Note the use of enctype="multipart/form-data"
in the
+FORM
tag. This is absolutely required if the form
+will contain file upload fields, as in the above example. Most
+browsers will not even attempt file uploads without the
+presence of this attribute.
+Examining CGI environment variables
+The CGI standard specifies a number of environment variables
+which are set by the server. However, servers are somewhat
+unpredictable as to whether these variables will be null or
+point to empty strings when an environment variable is not set.
+Also, in order to allow the programmer to restore saved
+CGI environments, the cgic library needs have a way of insulating
+the programmer from the actual environment variables.
+
+Instead of calling getenv() to determine the value of a
+variable such as HTTP_USER_AGENT (the browser software being used),
+always use the
+cgic copies of the environment variables,
+which are always valid C strings (they are never null, although
+they may point to an empty string). For instance, the cgic
+variable containing the name of the browser software is
+cgiUserAgent. The referring URL appears
+in the variable cgiReferrer.
+
+cgic can be used in conjunction with the
+gd graphics library, which
+can produce GIF images on the fly.
+
+The following short sample program hints at the possibilities:
+
+#include "cgic.h"
+#include "gd.h"
+
+char *colors[] = {
+ "red", "green", "blue"
+};
+
+#define colorsTotal 3
+
+int cgiMain() {
+ int colorChosen;
+ gdImagePtr im;
+ int r, g, b;
+ /* Use gd to create an image */
+ im = gdImageCreate(64, 64);
+ r = gdImageColorAllocate(im, 255, 0, 0);
+ g = gdImageColorAllocate(im, 0, 255, 0);
+ b = gdImageColorAllocate(im, 0, 0, 255);
+ /* Now use cgic to find out what color the user requested */
+ cgiFormSelectSingle("color", 3, &colorChosen, 0);
+ /* Now fill with the desired color */
+ switch(colorChosen) {
+ case 0:
+ gdImageFill(im, 32, 32, r);
+ break;
+ case 1:
+ gdImageFill(im, 32, 32, g);
+ break;
+ case 2:
+ gdImageFill(im, 32, 32, b);
+ break;
+ }
+ /* Now output the image. Note the content type! */
+ cgiHeaderContentType("image/gif");
+ /* Send the image to cgiOut */
+ gdImageGif(im, cgiOut);
+ /* Free the gd image */
+ gdImageDestroy(im);
+ return 0;
+}
+
+Note that this program would need to be linked with both cgic.o
+and libgd.a. Often programs of this type respond to one
+cgiPathInfo value or set of form fields by returning an HTML page
+with an inline image reference that, in turn, generates a GIF image.
+
+Debugging CGI applications can be a painful task. Since CGI applications
+run in a special environment created by the web server, it is difficult
+to execute them in a debugger. However, the cgic library provides a way
+of capturing "live" CGI environments to a file, and also provides a way
+to reload saved environments.
+
+The provided program 'capture.c' can be used to capture CGI
+environments. Just change the first line of the cgiMain() function
+of capture.c to save the CGI environment to a filename appropriate
+on your system and type 'make capture'. Then place capture in your
+cgi directory and set the form action or other link you want to test
+to point to it. When the form submission or other link takes place,
+capture will write the CGI environment active at that time to
+the filename you specified in the source. The
+cgiReadEnvironment() function can then
+be invoked on the same filename at the beginning of the cgiMain() function
+of the application you want to test in order to restore the captured
+environment. You can then execute your program in the debugger of your choice,
+and it should perform exactly as it would have performed had
+it been launched by the actual web server, including file uploads,
+cookies and all other phenomena within the purview of cgic.
+
+Important: Make sure you specify the full path, as the
+current working directory of a CGI script may not be what you
+think it is!
+
+Even More Important: If you call getenv() yourself
+in your code, instead of using the provided
+cgic copies of the CGI environment variables, you will
+not get the values you expect when running with
+a saved CGI environment. Always use the cgic variables instead
+of calling getenv().
+
+
+
- cgiFormResultType cgiFormString(
+ char *name, char *result, int max)
+
- cgiFormString attempts to retrieve the string sent for the
+ specified input field. The text will be copied into
+ the buffer specified by result, up to but not
+ exceeding max-1 bytes; a terminating null is then
+ added to complete the string. Regardless of the newline
+ format submitted by the browser, cgiFormString always
+ encodes each newline as a single line feed (ascii decimal 10); as
+ a result the final string may be slightly shorter than indicated
+ by a call to
+ cgiFormStringSpaceNeeded but will never be longer.
+ cgiFormString returns cgiFormSuccess if the string was
+ successfully retrieved,
+ cgiFormTruncated if the string was
+ retrieved but was truncated to fit the buffer,
+ cgiFormEmpty if the string was
+ retrieved but was empty, and cgiFormNotFound if no
+ such input field was submitted. In the last case,
+ an empty string is copied to result.
+
-
+cgiFormResultType cgiFormStringNoNewlines(
+ char *name, char *result, int max)
+
-
+cgiFormStringNoNewlines() is exactly equivalent to
+ cgiFormString(), except
+ that any carriage returns or line feeds that occur in the input
+ will be stripped out. The use of this function is recommended
+ for single-line text input fields, as some browsers will submit
+ carriage returns and line feeds when they should not.
+
-
+cgiFormResultType cgiFormStringSpaceNeeded(
+ char *name, int *length)
+
-
+cgiFormStringSpaceNeeded() is used to determine the length of the input text
+ buffer needed to receive the contents of the specified input field.
+ This is useful if the programmer wishes to allocate sufficient memory
+ for input of arbitrary length. The actual length of the string
+ retrieved by a subsequent call to cgiFormString() may be slightly shorter
+ but will never be longer than *result. On success, cgiFormStringSpaceNeeded()
+ sets the value pointed to by length to the number of bytes of data,
+ including the terminating null, and returns cgiFormSuccess. If no
+ value was submitted for the specified field, cgiFormStringSpaceNeeded sets
+ the value pointed to by length to 1 and returns cgiFormNotFound. 1 is
+ set to ensure space for an empty string (a single null
+ character) if cgiFormString is called despite the return value.
+
+
- cgiFormResultType cgiFormStringMultiple(
+ char *name, char ***ptrToStringArray)
+
- cgiFormStringMultiple is useful in the unusual case in which several
+ input elements in the form have the same name and, for whatever
+ reason, the programmer does not wish to use the checkbox, radio
+ button and selection menu functions provided below. This is
+ occasionally needed if the programmer cannot know
+ in advance what values might appear in a multiple-selection list
+ or group of checkboxes on a form. The value pointed to
+ by result will be set to a pointer to an array of strings; the last
+ entry in the array will be a null pointer. This array is allocated
+ by the CGI library. Important: when done working with the array,
+ you must call cgiStringArrayFree() with the array pointer as the
+ argument. cgiFormStringMultiple() returns cgiFormSuccess if at least
+ one occurrence of the name is found, cgiFormNotFound
+ if no occurrences are found, or cgiFormMemory if not enough
+ memory is available to allocate the array to be returned.
+ In all cases except the last, ptrToStringArray is set to point to a
+ valid array of strings, with the last element in the array being a
+ null pointer; in the out-of-memory case ptrToStringArray is set to
+ a null pointer.
+
+
- cgiFormResultType cgiFormEntries(
+ char ***ptrToStringArray)
+
- cgiFormEntries is useful when the programmer cannot know the names
+ of all relevant form fields in advance. The value pointed to
+ by result will be set to a pointer to an array of strings; the last
+ entry in the array will be a null pointer. This array is allocated
+ by the CGI library. Important: when done working with the array,
+ you must call cgiStringArrayFree() with the array pointer as the
+ argument. cgiFormEntries() returns cgiFormSuccess except in the event of an out of memory error.
+ On success, ptrToStringArray is set to point to a
+ valid array of strings, with the last element in the array being a
+ null pointer; in the out-of-memory case ptrToStringArray is set to
+ a null pointer, and
+ cgiFormOutOfMemory is returned.
+
+
- void cgiStringArrayFree(char **stringArray)
+
+
-
+cgiStringArrayFree() is used to free the memory associated with
+ a string array created by
+ cgiFormStringMultiple(),
+ cgiFormEntries(), or
+ cgiFormCookies().
+
- cgiFormResultType cgiFormInteger(
+ char *name, int *result, int defaultV)
+
- cgiFormInteger() attempts to retrieve the integer sent for the
+ specified input field. The value pointed to by result
+ will be set to the value submitted. cgiFormInteger() returns
+ cgiFormSuccess if the value was successfully retrieved,
+ cgiFormEmpty if the value submitted is an empty string,
+ cgiFormBadType if the value submitted is not an integer,
+ and cgiFormNotFound if no such input field was submitted.
+ In the last three cases, the value pointed to by result
+ is set to the specified default.
+
-
+cgiFormResultType cgiFormIntegerBounded(
+ char *name, int *result, int min, int max, int defaultV)
+
- cgiFormIntegerBounded() attempts to retrieve the integer sent for the
+ specified input field, and constrains the result to be within
+ the specified bounds. The value pointed to by result
+ will be set to the value submitted. cgiFormIntegerBounded() returns
+ cgiFormSuccess if the value was successfully retrieved,
+ cgiFormConstrained if the value was out of bounds and result
+ was adjusted accordingly, cgiFormEmpty if the value submitted is
+ an empty string, cgiFormBadType if the value submitted is not an
+ integer, and cgiFormNotFound if no such input field was submitted.
+ In the last three cases, the value pointed to by result
+ is set to the specified default.
+
+
- cgiFormResultType cgiFormDouble(
+ char *name, double *result, double defaultV)
+
- cgiFormDouble attempts to retrieve the floating-point value sent for
+ the specified input field. The value pointed to by result
+ will be set to the value submitted. cgiFormDouble returns
+ cgiFormSuccess if the value was successfully retrieved,
+ cgiFormEmpty if the value submitted is an empty string,
+ cgiFormBadType if the value submitted is not a number,
+ and cgiFormNotFound if no such input field was submitted.
+ In the last three cases, the value pointed to by result
+ is set to the specified default.
+
-
+cgiFormResultType cgiFormDoubleBounded(
+ char *name, double *result, double min, double max,
+ double defaultV)
+
-
+cgiFormDoubleBounded() attempts to retrieve the floating-point
+ value sent for the specified input field, and constrains the
+ result to be within the specified bounds. The value pointed to by
+ result will be set to the value submitted. cgiFormDoubleBounded() returns
+ cgiFormSuccess if the value was successfully retrieved,
+ cgiFormConstrained if the value was out of bounds and result
+ was adjusted accordingly, cgiFormEmpty if the value submitted is
+ an empty string, cgiFormBadType if the value submitted is not a
+ number, and cgiFormNotFound if no such input field was submitted.
+ In the last three cases, the value pointed to by result
+ is set to the specified default.
+
+
-
+cgiFormResultType cgiFormSelectSingle(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int defaultV)
+
-
+cgiFormSelectSingle() retrieves the selection number associated with a
+ <SELECT> element that does not allow multiple selections. name
+ should identify the NAME attribute of the <SELECT> element. choicesText
+ should point to an array of strings identifying each choice;
+ choicesTotal should indicate the total number of choices. The value
+ pointed to by result will be set to the position of the actual choice
+ selected within the choicesText array, if any, or to the value of
+ default, if no selection was submitted or an invalid selection was
+ made. cgiFormSelectSingle() returns cgiFormSuccess if the value was
+ successfully retrieved, cgiFormNotFound if no selection
+ was submitted, and cgiFormNoSuchChoice if the selection
+ does not match any of the possibilities in the choicesText array.
+
-
+
+cgiFormResultType cgiFormSelectMultiple(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int *invalid)
+
- cgiFormSelectMultiple() retrieves the selection numbers associated with a
+ <SELECT> element that does allow multiple selections. name should
+ identify the NAME attribute of the <SELECT> element. choicesText
+ should point to an array of strings identifying each choice;
+ choicesTotal should indicate the total number of choices. result
+ should point to an array of integers with as many elements as there
+ are strings in the choicesText array. For each choice in the
+ choicesText array that is selected, the corresponding integer in
+ the result array will be set to one; other entries in the result
+ array will be set to zero. cgiFormSelectMultiple() returns cgiFormSuccess
+ if at least one valid selection was successfully retrieved or
+ cgiFormNotFound if no valid selections were submitted.
+ The integer pointed to by invalid is set to the number of
+ invalid selections that were submitted, which should be zero
+ unless the form and the choicesText array do not agree.
+
+
-
+
+cgiFormResultType cgiFormSubmitClicked(
+ char *name)
+
-
+It is often desirable to know whether a particular submit button was clicked,
+ when multiple submit buttons with different name attributes exist.
+ cgiFormSubmitClicked is an alternative name for the
+ cgiFormCheckboxSingle function,
+ which is suitable for testing whether a particular submit button
+ was used.
+
-
+
+cgiFormResultType cgiFormCheckboxSingle(
+ char *name)
+
-
+cgiFormCheckboxSingle determines whether the checkbox with the specified name
+ is checked. cgiFormCheckboxSingle returns cgiFormSuccess if the
+ button is checked, cgiFormNotFound if the checkbox is
+ not checked. cgiFormCheckboxSingle is intended for single
+ checkboxes with a unique name; see below for functions to
+ deal with multiple checkboxes with the same name, and
+ with radio buttons.
+
+
-
+cgiFormResultType cgiFormCheckboxMultiple(
+ char *name, char **valuesText, int valuesTotal,
+ int *result, int *invalid)
+
- cgiFormCheckboxMultiple() determines which checkboxes among a group
+ of checkboxes with the same name are checked. This is distinct
+ from radio buttons (see cgiFormRadio).
+ valuesText
+ should point to an array of strings identifying the VALUE
+ attribute of each checkbox; valuesTotal should indicate the total
+ number of checkboxes. result should point to an array of integers with
+ as many elements as there are strings in the valuesText array. For
+ each choice in the valuesText array that is selected, the corresponding
+ integer in the result array will be set to one; other entries in the
+ result array will be set to zero. cgiFormCheckboxMultiple returns
+ cgiFormSuccess if at least one valid checkbox was checked or
+ cgiFormNotFound if no valid checkboxes were checked.
+ The integer pointed to by invalid is set to the number of
+ invalid selections that were submitted, which should be zero
+ unless the form and the valuesText array do not agree.
+
-
+cgiFormResultType cgiFormRadio(
+ char *name, char **valuesText, int valuesTotal,
+ int *result, int defaultV)
+
- cgiFormRadio() determines which, if any, of a group of radio boxes with
+ the same name was selected. valuesText should point to an array of
+ strings identifying the VALUE attribute of each radio box;
+ valuesTotal should indicate the total number of radio boxes. The value
+ pointed to by result will be set to the position of the actual choice
+ selected within the valuesText array, if any, or to the value of
+ default, if no radio box was checked or an invalid selection was
+ made. cgiFormRadio() returns cgiFormSuccess if a checked radio box was
+ found in the group, cgiFormNotFound if no box was checked, and
+ cgiFormNoSuchChoice if the radio box submitted does not match any of
+ the possibilities in the valuesText array.
+
+
- cgiFormResultType cgiFormFileName(
+ char *name, char *fileName, int max)
+
- cgiFormFileName attempts to retrieve the file name uploaded by the
+ user for the specified form input field of type
file
.
+ NEVER, EVER TRUST THIS FILENAME TO BE REASONABLE AND
+ SAFE FOR DIRECT USE ON THE SERVER SIDE.
+ The text will be copied into
+ the buffer specified by fileName, up to but not
+ exceeding max-1 bytes; a terminating null is then
+ added to complete the string. cgiFormFileName returns
+ cgiFormSuccess if the string was
+ successfully retrieved and was not empty,
+ cgiFormNoFileName if the string was
+ successfully retrieved but empty indicating that no file was uploaded,
+ cgiFormTruncated if the string was
+ retrieved but was truncated to fit the buffer,
+ and cgiFormNotFound if no
+ such input field was submitted. In the last case,
+ an empty string is copied to result.
+
- cgiFormResultType cgiFormFileSize(
+ char *name, int *sizeP)
+
- cgiFormFileSize attempts to retrieve the size, in bytes, of a
+ file uploaded by the browser in response to the
+ input field of type
file
specified by the
+ name
parameter. On success, the size is stored
+ to *sizeP, and this function returns
+ cgiFormSuccess. If the form
+ field does not exist, this function returns
+ cgiFormNotFound.
+ If the form field exists but no file was uploaded, this function
+ returns cgiFormNotAFile.
+
- cgiFormResultType cgiFormFileContentType(
+ char *name, char *contentType, int max)
+
- cgiFormString attempts to retrieve the content name claimed by the
+ user for the specified form input field of type
file
.
+ THERE IS NO GUARANTEE THAT THE CONTENT TYPE WILL BE
+ ACCURATE.
+ The content type string will be copied into
+ the buffer specified by contentType, up to but not
+ exceeding max-1 bytes; a terminating null is then
+ added to complete the string. cgiFormFileContentType returns
+ cgiFormSuccess if the string was
+ successfully retrieved and was not empty,
+ cgiFormNoContentType if the string was
+ successfully retrieved but empty indicating that no file was uploaded
+ or the browser did not know the content type,
+ cgiFormTruncated if the string was
+ retrieved but was truncated to fit the buffer,
+ and cgiFormNotFound if no
+ such input field was submitted. In the last case,
+ an empty string is copied to result.
+
+
- cgiFormResultType cgiFormFileOpen(
+ char *name, cgiFilePtr *cfpp)
+
- cgiFormFileOpen attempts to open the actual uploaded file data for
+ the specified form field of type
file
. Upon success,
+ this function returns retrieve the content name claimed by the
+ user for the specified form input field of type file
.
+ On success, this function sets *cfpp to a valid cgiFilePtr
+ object for use with cgiFormSuccess.
+ On failure, this function sets *cfpp to a null pointer, and
+ returns cgiFormNotFound,
+ cgiFormNotAFile,
+ cgiFormMemory or
+ cgiFormIO as appropriate.
+
+ See also cgiFormFileRead
+and cgiFormFileClose.
+
- cgiFormResultType cgiFormFileRead(
+ cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP)
+
- cgiFormFileRead attempts to read up to
bufferSize
+ bytes from a cgiFilePtr object previously opened with
+ cgiFormFileOpen. If any data
+ is successfully read, it is copied to buffer
,
+ and the number of bytes successfully read is stored
+ to *gotP
. This function returns
+ cgiFormSuccess if any data
+ is successfully read. At end of file, this function
+ returns cgiFormEOF. In the event
+ of an I/O error, this function returns
+ cgiFormIO. If cfp is a null pointer,
+ this function returns cgiFormOpenFailed.
+
+ See also cgiFormFileOpen
+and cgiFormFileClose.
+
- cgiFormResultType cgiFormFileClose(
+ cgiFilePtr cfp)
+
- cgiFormFileClose closes a cgiFilePtr object previously opened
+ with cgiFormFileOpen, freeing
+ memory and other system resources. This
+ function returns cgiFormSuccess
+ unless cfp is null, in which case
+ cgiFormOpenFailed is returned.
+
+ See also cgiFormFileOpen
+and cgiFormFileRead.
+
-
+void cgiHeaderLocation(char *redirectUrl)
+
-
+cgiHeaderLocation() should be called if the programmer wishes to
+redirect the user to a different URL. No futher output
+is needed in this case.
+
+If you wish to set cookies,
+you must make your calls to
+cgiHeaderCookieSetString
+and
+cgiHeaderCookieSetInteger
+ BEFORE invoking cgiHeaderLocation.
+
-
+void cgiHeaderStatus(int status, char *statusMessage)
+
-
+cgiHeaderStatus() should be called if the programmer wishes to
+output an HTTP error status code instead of a document. The status
+code is the first argument; the second argument is the status
+message to be displayed to the user.
+
+If you wish to set cookies,
+you must make your calls to
+cgiHeaderCookieSetString
+and
+cgiHeaderCookieSetInteger
+ BEFORE invoking cgiHeaderStatus.
+
-
+void cgiHeaderContentType(char *mimeType)
+
-
+cgiHeaderContentType() should be called if the programmer wishes to
+output a new document in response to the user's request. This is
+the normal case. The single argument is the MIME document type
+of the response; typical values are "text/html" for HTML documents,
+"text/plain" for plain ASCII without HTML tags, "image/gif" for
+a GIF image and "audio/basic" for .au-format audio.
+
+If you wish to set cookies,
+you must make your calls to
+cgiHeaderCookieSetString
+and
+cgiHeaderCookieSetInteger
+ BEFORE invoking cgiHeaderContentType.
+
-
+void cgiHeaderCookieSetString(char *name, char *value,
+ int secondsToLive, char *path, char *domain)
+
-
+cgiHeaderCookieSetString() should be called when the programmer wishes
+to store a piece of information in the user's browser, so that the
+stored information is again presented to the server on subsequent
+accesses to the relevant site. The first argument is the name of the
+cookie to be stored; for best results in all browsers, use a short
+name without spaces or unusual punctuation. The second argument is
+the value of the cookie to be stored. Again, for best results, use
+a short string; it is recommended that cookies be used to store a
+unique identifier which is then used to look up more detailed
+information in a database on the server side. Attempts to store
+elaborate information on the browser side are much more likely to fail.
+The third argument is the number of seconds that the cookie should
+be kept by the browser; 86400 is a single full day, 365*86400 is
+roughly one year. The fourth argument is the partial URL of the
+web site within which the cookie is relevant. If the cookie should
+be sent to the server for every access to the entire site,
+set this argument to
/
. The final argument is the
+web site name or entire domain for which this cookie should be
+submitted; if you choose to have the cookie sent back for an
+entire domain, this argument must begin with a dot, such as
+.boutell.com
. The cgic variables cgiServerName are convenient
+values for the fourth and fifth arguments.
+See also cgiHeaderCookieSetInteger,
+cgiCookieString,
+cgiCookieInteger and
+cgiCookies.
+
-
+void cgiHeaderCookieSetInteger(char *name, int value,
+ int secondsToLive, char *path, char *domain)
+
-
+cgiHeaderCookieSetInteger() is identical to
+cgiHeaderCookieSetString,
+except that the value to be set is an integer rather than a string.
+See cgiHeaderCookieSetString
+for complete information.
+
+
- cgiFormResultType cgiCookieString(
+ char *name, char *result, int max)
+
- cgiFormString attempts to retrieve the string sent for the
+ specified cookie (browser-side persistent storage). The
+ text will be copied into
+ the buffer specified by result, up to but not
+ exceeding max-1 bytes; a terminating null is then
+ added to complete the string.
+ cgiCookieString returns cgiFormSuccess if the string was
+ successfully retrieved,
+ cgiFormTruncated if the string was
+ retrieved but was truncated to fit the buffer,
+ cgiFormEmpty if the string was
+ retrieved but was empty, and cgiFormNotFound if no
+ such cookie was submitted. In the last case,
+ an empty string is copied to result.
+
- cgiFormResultType cgiCookieInteger(
+ char *name, int *result, int defaultV)
+ See also cgiCookieInteger,
+ cgiCookies,
+ cgiHeaderCookieSetInteger.
+
- cgiCookieInteger() attempts to retrieve the integer sent for the
+ specified cookie (browser-side persistent storage). The value
+ pointed to by result will be set to the value submitted.
+ cgiCookieInteger() returns
+ cgiFormSuccess if the value was successfully retrieved,
+ cgiFormEmpty if the value submitted is an empty string,
+ cgiFormBadType if the value submitted is not an integer,
+ and cgiFormNotFound if no such
+ input field was submitted. In the last three cases, the value
+ pointed to by result is set to the specified default.
+ See also cgiCookieString,
+ cgiCookies,
+ cgiHeaderCookieSetInteger.
+
- cgiFormResultType cgiCookies(
+ char *name, char ***ptrToStringArray)
+
- cgiCookies is useful when the programmer cannot know the names
+ of all relevant cookies (browser-side persistent strings) in advance.
+ The value pointed to by result will be set to a pointer to an array
+ of strings; the last
+ entry in the array will be a null pointer. This array is allocated
+ by the CGI library. Important: when done working with the array,
+ you must call cgiStringArrayFree() with the array pointer as the
+ argument. cgiCookies() returns cgiFormSuccess except in the event of an out of memory error.
+ On success, ptrToStringArray is set to point to a
+ valid array of strings, with the last element in the array being a
+ null pointer; in the out-of-memory case ptrToStringArray is set to
+ a null pointer, and
+ cgiFormOutOfMemory is returned.
+
-
+cgiFormResultType cgiHtmlEscape(char *s)
+
-
+cgiHtmlEscape() outputs the specified null-terminated string to
+cgiOut,
+escaping any <, &, and > characters encountered correctly so that
+they do not interfere with HTML markup. Returns
+cgiFormSuccess, or
+cgiFormIO in the event of an I/O error.
+
+
-
+cgiFormResultType cgiHtmlEscapeData(char *data, int len)
+
-
+cgiHtmlEscapeData() is identical to cgiHtmlEscape,
+except that the data is not null-terminated. This version of the function
+outputs
len
bytes. See cgiHtmlEscape
+for more information.
+
-
+cgiFormResultType cgiValueEscape(char *s)
+
-
+cgiValueEscape() outputs the specified null-terminated string to
+cgiOut,
+escaping any " characters encountered correctly so that
+they do not interfere with the quotation marks of HTML attribute
+values. This is useful when outputting a string as part of the
+value attribute of an input tag, or the href attribute of a link
+or form tag. This function returns
+cgiFormSuccess, or
+cgiFormIO in the event of an I/O error.
+
+
-
+cgiFormResultType cgiValueEscapeData(char *data, int len)
+
-
+cgiValueEscapeData() is identical to cgiValueEscape,
+except that the data is not null-terminated. This version of the function
+outputs
len
bytes. See cgiValueEscape
+for more information.
+
-
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename)
+
-
+cgiWriteEnvironment() can
+ be used to write the entire CGI environment, including
+ form data, to the specified output file;
+ cgiReadEnvironment()
+ can then be used to restore that environment from the specified
+ input file for debugging. Of course, these will only work as expected
+ if you use the cgic copies of the CGI environment
+ variables and cgiIn and
+ cgiOut rather than stdin and
+ stdout (also see above). These functions are useful in order
+ to capture real CGI situations while the web server is running, then
+ recreate them in a debugging environment. Both functions
+ return cgiEnvironmentSuccess on
+ success, cgiEnvironmentIO on an I/O
+ error, and cgiEnvironmentMemory
+ on an out-of-memory error.
+
-
+cgiEnvironmentResultType cgiReadEnvironment(char *filename)
+
-
+cgiReadEnvironment() restores a CGI environment saved to the specified file by
+ cgiWriteEnvironment().
+ Of course, these will only work as expected
+ if you use the cgic copies of the CGI environment
+ variables and cgiIn and
+ cgiOut rather than stdin and
+ stdout (also see above). These functions are useful in order
+ to capture real CGI situations while the web server is running, then
+ recreate them in a debugging environment. Both functions
+ return cgiEnvironmentSuccess on success,
+ cgiEnvironmentIO on an I/O error, and
+ cgiEnvironmentMemory
+ on an out-of-memory error.
+
- int cgiMain()
+
- The programmer must write this function, which performs
+ the unique task of the program and is invoked by the true main()
+ function, found in the cgic library itself. The return value from
+ cgiMain will be the return value of the program. It is expected that
+ the user will make numerous calls to the cgiForm functions
+ from within this function. See how to write
+ a cgic application for details.
+
+
+This section provides a reference guide to the various global
+variables provided by cgic for the programmer to utilize.
+These variables should always be used in preference to
+stdin, stdout, and calls to getenv() in order to ensure
+compatibility with the cgic CGI debugging features.
+
+Most of these variables are equivalent to various CGI environment
+variables. The most important difference is that the cgic
+environment string variables are never null pointers. They will always
+point to valid C strings of zero or more characters.
+
+
- char *cgiServerSoftware
+
- Points to the name of the server software,
+or to an empty string if unknown.
+
- char *cgiServerName
+
- Points to the name of the server,
+or to an empty string if unknown.
+
- char *cgiGatewayInterface
+
- Points to the name of the gateway interface (usually CGI/1.1),
+or to an empty string if unknown.
+
- char *cgiServerProtocol
+
- Points to the protocol in use (usually HTTP/1.0),
+or to an empty string if unknown.
+
- char *cgiServerPort
+
- Points to the port number on which the server is listening
+for HTTP connections (usually 80), or an empty string if unknown.
+
- char *cgiRequestMethod
+
- Points to the method used in the request (usually GET or POST),
+or an empty string if unknown (this should not happen).
+
- char *cgiPathInfo
+
- Most web servers recognize any additional path information in
+the URL of the request beyond the name of the CGI program itself and
+pass that information on to the program. cgiPathInfo points to this
+additional path information.
+
- char *cgiPathTranslated
+
- Most web servers recognize any additional path information in
+the URL of the request beyond the name of the CGI program itself and
+pass that information on to the program. cgiPathTranslated points
+to this additional path information, translated by the server into a
+filesystem path on the local server.
+
- char *cgiScriptName
+
- Points to the name under which the program was invoked.
+
- char *cgiQueryString
+
- Contains any query information submitted by the user as a result
+of a GET-method form or an <ISINDEX> tag. Note that this
+information need not be parsed directly unless an <ISINDEX> tag
+was used; normally it is parsed automatically by the cgic library. Use
+the cgiForm family of functions to retrieve the values associated
+with form input fields. See how to write
+a cgic application for more information.
+
- char *cgiRemoteHost
+
- Points to the fully resolved hostname of the browser, if known,
+or an empty string if unknown.
+
- char *cgiRemoteAddr
+
- Points to the dotted-decimal IP address of the browser, if known,
+or an empty string if unknown.
+
- char *cgiAuthType
+
- Points to the type of authorization used for the request,
+if any, or an empty string if none or unknown.
+
- char *cgiRemoteUser
+
- Points to the user name under which the user has
+authenticated; an empty string if no authentication has
+taken place. The certainty of this information depends on
+the type of authorization in use; see
+cgiAuthType.
+
- char *cgiRemoteIdent
+
- Points to the user name volunteered by the user via
+the user identification protocol; an empty
+string if unknown. This information is not secure.
+Identification demons can be installed by users on
+insecure systems such as Windows machines.
+
- char *cgiContentType
+
- Points to the MIME content type of the information
+submitted by the user, if any; an empty string if no
+information was submitted. If this string is equal to
+
application/x-www-form-urlencoded
or
+multipart/form-data
, the cgic
+library will automatically examine the form data submitted.
+If this string has any other non-empty value, a different
+type of data has been submitted. This is currently very rare,
+as most browsers can only submit forms and file uploads which
+cgic parses directly.
+
- char *cgiCookie
+
- Points to the raw cookie (browser-side persistent storage)
+data submitted by the web browser.
+Programmers should use the functions cgiCookies,
+cgiCookieString and
+cgiCookieInteger instead of
+examining this string directly.
+
- char *cgiAccept
+
- Points to a space-separated list of MIME content types
+acceptable to the browser (see
+cgiHeaderContentType() ), or an empty string. Unfortunately, this variable
+is not supplied in a useful form by most current browsers. Programmers wishing
+to make decisions based on the capabilities of the browser
+are advised to check the cgiUserAgent
+variable against a list of browsers and capabilities instead.
+
- char *cgiUserAgent
+
-
+Points to the name of the browser in use, or an empty
+string if this information is not available.
+
- char *cgiReferrer
+
-
+Points to the URL of the previous page visited by the user. This is
+often the URL of the form that brought the user to your program.
+Note that reporting this information is entirely up to the browser,
+which may choose not do so, and may choose not to do so truthfully.
+However, this variable is typically accurate. The frequently
+used misspelling cgiReferer is also supplied as a macro.
+
- int cgiContentLength
+
- The number of bytes of form or query data received.
+ Note that if the submission is a form or query submission
+ the library will read and parse all the information
+ directly from cgiIn and/or cgiQueryString. The programmer should
+ not do so, and indeed the cgiIn pointer will be at end-of-file
+ in such cases.
+
- FILE *cgiOut
+
- Pointer to CGI output. The cgiHeader functions, such as
+ cgiHeaderContentType, should
+ be used first to output the mime headers; the output HTML
+ page, GIF image or other web document should then be written
+ to cgiOut by the programmer using standard C I/O functions
+ such as fprintf() and fwrite(). cgiOut is normally equivalent
+ to stdout; however, it is recommended that cgiOut be used to
+ ensure compatibility with future versions of cgic for
+ specialized environments.
+
- FILE *cgiIn
+
- Pointer to CGI input. In 99.99% of cases, you will not
+ need this. CGIC 2.0 supports both regular POST form submissions
+ and multipart/form-data file upload form submissions directly.
+
+
+
+In most cases, cgic functions are designed to produce reasonable results
+even when browsers and users do unreasonable things. However, it is sometimes
+important to know precisely which unreasonable things took place, especially
+when assigning a default value or bounding a value is an inadequate
+solution. The following result codes are useful in making this determination.
+
+
- cgiFormSuccess
+
- Indicates that the function successfully performed at least one
+action (or retrieved at least one value, where applicable).
+
- cgiFormTruncated
+
- Indicates that a string value retrieved from the user was
+cut short to avoid overwriting the end of a buffer.
+
- cgiFormBadType
+
- Indicates that a "numeric" value submitted by the user was
+in fact not a legal number.
+
- cgiFormEmpty
+
- Indicates that a field was retrieved but contained no data.
+
- cgiFormNotFound
+
- Indicates that no value was submitted for a particular field.
+
- cgiFormConstrained
+
- Indicates that a numeric value was beyond the specified bounds
+and was forced to the lower or upper bound as appropriate.
+
- cgiFormNoSuchChoice
+
- Indicates that the value submitted for a single-choice field
+(such as a radio-button group) was not one of the acceptable values.
+This usually indicates a discrepancy between the form and the program.
+
- cgiFormEOF
+
- Returned by cgiFormFileRead
+when, at the start of the call, the cgiFilePtr object is already
+positioned at the end of the uploaded file data.
+
- cgiFormIO
+
- Returned by cgiFormFileRead
+when an I/O error occurs while reading uploaded file data.
+
- cgiFormNotAFile
+
- Returned in response to an attempt to manipulate a form field
+that is not a file upload field using a file-related function.
+
- cgiFormNoContentType
+
- Returned in response to an attempt to fetch the content type of
+a file-upload field when the content type is not specified by the browser.
+
- cgiFormNoFileName
+
- Returned in response to an attempt to fetch the file name of
+a file-upload field when a file name is not specified by the browser.
+
- cgiFormOpenFailed
+
- Returned in response to an attempt to read from a null
+cgiFilePtr object, typically when the programmer has failed to
+check the result of a call to cgiFormFileOpen.
+
- cgiEnvironmentMemory
+
- Indicates that an attempt to read or write the CGI environment
+to or from a capture file failed due to an out-of-memory error.
+
- cgiEnvironmentSuccess
+
- Indicates that an attempt to read or write the CGI environment
+to or from a capture file was successful.
+
- cgiEnvironmentIO
+
- Indicates that an attempt to read or write the CGI environment
+to or from a capture file failed due to an I/O error.
+
- cgiEnvironmentWrongVersion
+
- Indicates that an attempt to read from a saved debugging CGI environment
+produced by a pre-2.0 version of CGIC was made.
+
+
+cgiAccept |
+cgiAuthType |
+cgiContentLength |
+cgiContentType |
+cgiEnvironmentIO |
+cgiEnvironmentMemory |
+cgiEnvironmentSuccess |
+cgiCookieInteger |
+cgiCookies |
+cgiCookieSetInteger |
+cgiCookieSetString |
+cgiCookieString |
+cgiHtmlEscape |
+cgiHtmlEscapeData |
+cgiValueEscape |
+cgiValueEscapeData |
+cgiFormBadType |
+cgiFormCheckboxMultiple() |
+cgiFormCheckboxSingle() |
+cgiFormConstrained |
+cgiFormDouble() |
+cgiFormDoubleBounded() |
+cgiFormEOF |
+cgiFormEmpty |
+cgiFormEntries |
+cgiFormFileClose |
+cgiFormFileContentType |
+cgiFormFileName |
+cgiFormFileOpen |
+cgiFormFileRead |
+cgiFormFileSize |
+cgiFormInteger() |
+cgiFormIntegerBounded() |
+cgiFormNoFileName |
+cgiFormNoSuchChoice |
+cgiFormNotFound |
+cgiFormRadio() |
+cgiFormSelectMultiple() |
+cgiFormSelectSingle() |
+cgiFormString() |
+cgiFormStringMultiple() |
+cgiFormStringNoNewlines() |
+cgiFormStringSpaceNeeded() |
+cgiFormSuccess |
+cgiFormTruncated |
+cgiGatewayInterface |
+cgiHeaderContentType() |
+cgiHeaderLocation() |
+cgiHeaderStatus() |
+cgiIn |
+cgiMain()
+cgiOut |
+cgiPathInfo |
+cgiPathTranslated |
+cgiQueryString |
+cgiReadEnvironment() |
+cgiReferrer() |
+cgiRemoteAddr |
+cgiRemoteHost |
+cgiRemoteIdent |
+cgiRemoteUser |
+cgiRequestMethod |
+cgiScriptName |
+cgiServerName |
+cgiServerPort |
+cgiServerProtocol |
+cgiServerSoftware |
+cgiStringArrayFree() |
+cgiUserAgent |
+cgiWriteEnvironment()
+
+
+Boutell.Com, Inc.
+
+
+
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/cgictest.c (revision 221)
@@ -0,0 +1,222 @@
+
+/*
+ $Id: cgictest.c,v 1.2 2004/04/07 17:09:27 fox Exp $
+ */
+
+#include
+#include "cgic.h"
+
+void Name();
+void Address();
+void Hungry();
+void Temperature();
+void Frogs();
+void Color();
+void Flavors();
+void NonExButtons();
+void RadioButtons();
+
+
+int cgiMain() {
+#if DEBUG
+ /* Load a saved CGI scenario if we're debugging */
+ cgiReadEnvironment("/home/boutell/public_html/capcgi.dat");
+#endif
+ dup2(cgiOut,stdout);
+ printf("Content-Type: text/html; charset=utf-8\r\nStatus: 200 OK\r\n\r\n");
+ //cgiHeaderContentType("text/html");
+ printf( "\n");
+ printf( "cgic test\n");
+ printf( "cgic test
\n");
+ Name();
+ Address();
+ Hungry();
+ Temperature();
+ Frogs();
+ Color();
+ Flavors();
+ NonExButtons();
+ RadioButtons();
+ printf( "\n");
+ return 0;
+}
+
+void Name() {
+ char name[81];
+ int result = cgiFormStringNoNewlines("name", name, 81);
+ switch (result) {
+ case cgiFormSuccess:
+ printf( "Name fetched, result code: cgiFormSuccess
\n");
+ break;
+ case cgiFormTruncated:
+ printf( "Name fetched, result code: cgiFormTruncated
\n");
+ break;
+ case cgiFormEmpty:
+ printf( "Name fetched, result code: cgiFormEmpty
\n");
+ break;
+ case cgiFormNotFound:
+ printf( "Name fetched, result code: cgiFormNotFound
\n");
+ break;
+ case cgiFormMemory:
+ printf( "Name fetched, result code: cgiFormMemory
\n");
+ break;
+ default:
+ printf( "Name fetched, unexpected result code: %d\n", result);
+ break;
+ }
+ printf( "Name: %s
\n", name);
+}
+
+void Address() {
+ char address[241];
+ cgiFormString("address", address, 241);
+ printf( "Address: \n%s
\n", address);
+}
+
+void Hungry() {
+ if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) {
+ printf( "I'm Hungry!
\n");
+ } else {
+ printf( "I'm Not Hungry!
\n");
+ }
+}
+
+void Temperature() {
+ double temperature;
+ cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6);
+ printf( "My temperature is %f.
\n", temperature);
+}
+
+void Frogs() {
+ int frogsEaten;
+ cgiFormInteger("frogs", &frogsEaten, 0);
+ printf( "I have eaten %d frogs.
\n", frogsEaten);
+}
+
+char *colors[] = {
+ "Red",
+ "Green",
+ "Blue"
+};
+
+void Color() {
+ int colorChoice;
+ cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0);
+ printf( "I am: %s
\n", colors[colorChoice]);
+}
+
+char *flavors[] = {
+ "pistachio",
+ "walnut",
+ "creme"
+};
+
+void Flavors() {
+ int flavorChoices[3];
+ int i;
+ int result;
+ int invalid;
+ result = cgiFormSelectMultiple("flavors", flavors, 3,
+ flavorChoices, &invalid);
+ if (result == cgiFormNotFound) {
+ printf( "I hate ice cream.\n");
+ } else {
+ printf( "My favorite ice cream flavors are:\n");
+ printf( "
\n");
+ for (i=0; (i < 3); i++) {
+ if (flavorChoices[i]) {
+ printf( "- %s\n", flavors[i]);
+ }
+ }
+ printf( "
\n");
+ }
+}
+
+char *ages[] = {
+ "1",
+ "2",
+ "3",
+ "4"
+};
+
+void RadioButtons() {
+ int ageChoice;
+ char ageText[10];
+ /* Approach #1: check for one of several valid responses.
+ Good if there are a short list of possible button values and
+ you wish to enumerate them. */
+ cgiFormRadio("age", ages, 4, &ageChoice, 0);
+
+ printf( "Age of Truck: %s (method #1)
\n",
+ ages[ageChoice]);
+
+ /* Approach #2: just get the string. Good
+ if the information is not critical or if you wish
+ to verify it in some other way. Note that if
+ the information is numeric, cgiFormInteger,
+ cgiFormDouble, and related functions may be
+ used instead of cgiFormString. */
+ cgiFormString("age", ageText, 10);
+
+ printf( "Age of Truck: %s (method #2)
\n", ageText);
+}
+
+char *votes[] = {
+ "A",
+ "B",
+ "C",
+ "D"
+};
+
+void NonExButtons() {
+ int voteChoices[4];
+ int i;
+ int result;
+ int invalid;
+
+ char **responses;
+
+ /* Method #1: check for valid votes. This is a good idea,
+ since votes for nonexistent candidates should probably
+ be discounted... */
+ printf( "Votes (method 1):
\n");
+ result = cgiFormCheckboxMultiple("vote", votes, 4,
+ voteChoices, &invalid);
+ if (result == cgiFormNotFound) {
+ printf( "I hate them all!\n");
+ } else {
+ printf( "My preferred candidates are:\n");
+ printf( "
\n");
+ for (i=0; (i < 4); i++) {
+ if (voteChoices[i]) {
+ printf( "- %s\n", votes[i]);
+ }
+ }
+ printf( "
\n");
+ }
+
+ /* Method #2: get all the names voted for and trust them.
+ This is good if the form will change more often
+ than the code and invented responses are not a danger
+ or can be checked in some other way. */
+ printf( "Votes (method 2):
\n");
+ result = cgiFormStringMultiple("vote", &responses);
+ if (result == cgiFormNotFound) {
+ printf( "I hate them all!\n");
+ } else {
+ int i = 0;
+ printf( "My preferred candidates are:\n");
+ printf( "
\n");
+ while (responses[i]) {
+ printf( "- %s\n", responses[i]);
+ i++;
+ }
+ printf( "
\n");
+ }
+ /* We must be sure to free the string array or a memory
+ leak will occur. Simply calling free() would free
+ the array but not the individual strings. The
+ function cgiStringArrayFree() does the job completely. */
+ cgiStringArrayFree(responses);
+}
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/license.txt (revision 221)
@@ -0,0 +1,109 @@
+CGIC License Terms
+------------------
+
+Basic License
+-------------
+
+CGIC, copyright 2009 GeoLabs SARL.
+CGIC, copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+2004 by Thomas Boutell and Boutell.Com, Inc.. Permission is granted to
+use CGIC in any application, commercial or noncommercial, at no cost.
+HOWEVER, this copyright paragraph must appear on a "credits" page
+accessible in the public online and offline documentation of the program.
+Modified versions of the CGIC library should not be distributed without
+the attachment of a clear statement regarding the author of the
+modifications, and this notice may in no case be removed.
+Modifications may also be submitted to the author for inclusion
+in the main CGIC distribution.
+
+IF YOU WOULD PREFER NOT TO ATTACH THE ABOVE NOTICE to
+the public documentation of your application, consult the
+information which follows regarding the availability
+of a nonexclusive commercial license for CGIC.
+
+Commercial License
+------------------
+
+The price of a nonexclusive commercial license is $200 U.S.
+To purchase the license:
+
+1. Print and sign two copies of the document below.
+
+2. Send both copies, along with Visa, Mastercard, Discover, or
+Amex card number, cardholder's name, and expiration date
+OR a check for $200 in U.S. funds drawn on a U.S. bank, to:
+
+Boutell.Com, Inc.
+PO Box 63767
+Philadelphia, PA 19147 USA
+
+Credit card orders may alternatively be faxed to:
+
+Boutell.Com, Inc.
+(208) 445-0327
+
+BE SURE TO INCLUDE AN EMAIL ADDRESS, as well as
+a postal address and phone number.
+
+3. We will return one signed copy to you promptly.
+
+You will also receive swift notification of new
+versions of CGIC, in addition to ongoing
+email support.
+
+* * *
+
+CGIC Nonexclusive Commercial License
+
+The licensee named below is granted the right
+to utilize CGIC, major version 1 or 2, any minor
+version thereof, in CGI applications without the
+need for a credit notice of any kind. CGI applications
+developed by the holder of this license may be
+distributed freely in source code or binary form
+without additional fees or royalties. This license does
+not grant the right to use CGIC to create a development tool
+which passes on substantially all of the capabilities of the
+CGIC library to the user of the tool, unless that tool is
+to be used internally by the license holder only in order
+to develop CGI applications. This license may not
+be resold, but applications developed in accordance
+with the terms of the license may be distributed
+freely subject to the limitations described above.
+
+Future minor (2.x) versions of CGIC will be covered by this
+license free of charge. If significant defects of workmanship
+are discovered in version 2.x, minor releases to correct them
+will be made available before or at the same time that
+those defects are addressed in any future major version.
+Future "major" (3.x) versions will be available to
+licensees at an upgrade price of $50.
+
+If, for any reason, any portion of this license is found
+to be invalid, that portion of the license only
+is invalidated and the remainder of the agreement
+remains in effect.
+
+If this license has not been signed by Thomas Boutell
+or M. L. Grant on behalf of Boutell. Com, Inc.,
+it is invalid.
+
+Licensee's Name: _____________________
+
+Signed for Licensee: _____________________
+
+Complete Mailing Address: _____________________
+
+ _____________________
+
+ _____________________
+
+Phone Number: _____________________
+
+Email Address: _____________________
+
+Signed for Boutell.Com, Inc.: _____________________
+
+Date: _____________________
+
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/makefile.vc (revision 221)
@@ -0,0 +1,35 @@
+GEODIR=C:/OSGeo4W
+DESTDIR=C:\OSGeo4W
+CFLAGS=-g -Wall
+CC=cl /TP
+LIBS=$(GEODIR)/lib/libfcgi.lib /nologo
+LIBS1=./libcgic.lib
+
+CFLAGS=/EHa /nologo /DCRT_SECURE_NO_WARNING /MT /W2 /O2 /D "WIN32" \
+ -I $(GEODIR)/include
+
+all: libcgic.lib #cgictest.exe capture.exe
+
+install: libcgic.lib
+ copy libcgic.lib $(DESTDIR)\lib
+ copy cgic.h $(DESTDIR)\include
+ @echo libcgic.lib is now installed in $(DESTDIR)\lib and cgic.h is in $(DESTDIR)\include.
+
+libcgic.lib: cgic.obj cgic.h
+ if exist cgic.lib del cgic.lib
+ lib /out:libcgic.lib cgic.obj $(LIBS)
+
+cgictest.obj: cgictest.c libcgic.lib
+ $(CC) $(CFLAGS) -c cgictest.c
+
+cgictest.exe: cgictest.obj
+ link cgictest.obj $(LIBS) /out:cgictest.exe
+
+cgic.obj:
+ $(CC) $(CFLAGS) -c cgic.c
+
+capture.exe: capture.obj libcgic.lib
+ $(CC) $(CFLAGS) capture.c $(LIBS)
+
+clean:
+ erase *.obj *.lib *.exe *.manifest
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/readme.txt (revision 221)
@@ -0,0 +1,14 @@
+This is the CGIC CGI development library for C programmers.
+Copyright 2009 GeoLabs SARL
+Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+2004, Thomas Boutell and Boutell.Com, Inc.
+
+See the file cgic.html for complete documentation in a single
+HTML hypertext file to be accessed with your web browser. If you
+preferer, there is a plaintext version in the file cgic.txt.
+Or see http://www.boutell.com/cgic/ for the latest copy of
+the documentation.
+
+See the files license.txt and support.txt for terms of use and
+technical support information.
+
Index: /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/cgic206/support.txt (revision 221)
@@ -0,0 +1,46 @@
+Support for CGIC
+----------------
+
+ ____
+ / \
+ | STOP |
+ \____/
+
+ Are you getting a "server error," indicating that your web server
+ "cannot allow POST to this URL," or a similar message? YOU MUST
+ CONFIGURE YOUR WEB SERVER TO ALLOW CGI PROGRAMS, AND YOU MUST
+ INSTALL CGI PROGRAMS IN THE LOCATION (OR WITH THE EXTENSION) THAT
+ YOUR WEB SERVER EXPECTS TO SEE. Please don't send me email about
+ this, unless you are purchasing commercial support. It is strictly
+ between you and your web server's documentation, or between you and
+ your ISP. If you wish to purchase commercial support, we will gladly
+ attempt to help you resolve such problems, but the cooperation of
+ your web server administrator will be necessary in most cases. Thanks!
+
+Free Support
+------------
+
+Anyone can mail questions about the gd and cgic libraries to
+boutell@boutell.com. However, I receive a very large volume of email on
+many subjects, and while I do my best to respond to all queries this can
+take some time. Sometimes the response must take the form of an eventual
+new release or an addition to a FAQ or other document, as opposed to an
+detailed individual response.
+
+Hourly Support
+--------------
+
+Those requiring support in detail may arrange for direct support
+from the author, Thomas Boutell, at the rate of $50/hr, billed
+directly by credit card. Purchase orders are also accepted from
+Fortune 500 corporations and institutions in good standing.
+To make arrangements, contact boutell@boutell.com. Alternatively,
+use our free-form secure message page at:
+
+https://www.boutell.com/freeform/
+
+To avoid delay and/or confusion, be sure to specifically mention that
+you wish to purchase CGIC support at the hourly rate above.
+
+-Thomas Boutell, boutell@boutell.com
+
Index: /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.c (revision 221)
@@ -0,0 +1,146 @@
+/*
+
+ Implementation of POSIX directory browsing functions and types for Win32.
+
+ Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+ History: Created March 1997. Updated June 2003.
+ Rights: See end of file.
+
+*/
+
+#include
+#include
+#include /* _findfirst and _findnext set errno iff they return -1 */
+#include
+#include
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct DIR
+{
+ long handle; /* -1 for failed rewind */
+ struct _finddata_t info;
+ struct dirent result; /* d_name null iff first time */
+ char *name; /* null-terminated char string */
+};
+
+DIR *opendir(const char *name)
+{
+ DIR *dir = 0;
+
+ if(name && name[0])
+ {
+ size_t base_length = strlen(name);
+ const char *all = /* search pattern must end with suitable wildcard */
+ strchr("/\\", name[base_length - 1]) ? "*" : "/*";
+
+ if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
+ (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
+ {
+ strcat(strcpy(dir->name, name), all);
+
+ if((dir->handle = (long) _findfirst(dir->name, &dir->info)) != -1)
+ {
+ dir->result.d_name = 0;
+ }
+ else /* rollback */
+ {
+ free(dir->name);
+ free(dir);
+ dir = 0;
+ }
+ }
+ else /* rollback */
+ {
+ free(dir);
+ dir = 0;
+ errno = ENOMEM;
+ }
+ }
+ else
+ {
+ errno = EINVAL;
+ }
+
+ return dir;
+}
+
+int closedir(DIR *dir)
+{
+ int result = -1;
+
+ if(dir)
+ {
+ if(dir->handle != -1)
+ {
+ result = _findclose(dir->handle);
+ }
+
+ free(dir->name);
+ free(dir);
+ }
+
+ if(result == -1) /* map all errors to EBADF */
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+ struct dirent *result = 0;
+
+ if(dir && dir->handle != -1)
+ {
+ if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
+ {
+ result = &dir->result;
+ result->d_name = dir->info.name;
+ }
+ }
+ else
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+void rewinddir(DIR *dir)
+{
+ if(dir && dir->handle != -1)
+ {
+ _findclose(dir->handle);
+ dir->handle = (long) _findfirst(dir->name, &dir->info);
+ dir->result.d_name = 0;
+ }
+ else
+ {
+ errno = EBADF;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+
+ Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives.
+
+ This software is supplied "as is" without express or implied warranty.
+
+ But that said, if there are any problems please get in touch.
+
+*/
+
Index: /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h
===================================================================
--- /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h (revision 221)
+++ /tags/rel-1.2.0-rc1/thirds/dirent-win32/dirent.h (revision 221)
@@ -0,0 +1,50 @@
+#ifndef DIRENT_INCLUDED
+#define DIRENT_INCLUDED
+
+/*
+
+ Declaration of POSIX directory browsing functions and types for Win32.
+
+ Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+ History: Created March 1997. Updated June 2003.
+ Rights: See end of file.
+
+*/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct DIR DIR;
+
+struct dirent
+{
+ char *d_name;
+};
+
+DIR *opendir(const char *);
+int closedir(DIR *);
+struct dirent *readdir(DIR *);
+void rewinddir(DIR *);
+
+/*
+
+ Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives.
+
+ This software is supplied "as is" without express or implied warranty.
+
+ But that said, if there are any problems please get in touch.
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js (revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-api.js (revision 221)
@@ -0,0 +1,6163 @@
+/**
+ * Author : René-Luc D'Hont
+ *
+ * Copyright 2010 3liz SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Copyright 2005-2010 OpenLayers Contributors, released under the Clear BSD
+ * license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt
+ * for the full text of the license.
+ */
+
+/**
+ * Class: ZOO
+ */
+ZOO = {
+ /**
+ * Constant: SERVICE_ACCEPTED
+ * {Integer} used for
+ */
+ SERVICE_ACCEPTED: 0,
+ /**
+ * Constant: SERVICE_STARTED
+ * {Integer} used for
+ */
+ SERVICE_STARTED: 1,
+ /**
+ * Constant: SERVICE_PAUSED
+ * {Integer} used for
+ */
+ SERVICE_PAUSED: 2,
+ /**
+ * Constant: SERVICE_SUCCEEDED
+ * {Integer} used for
+ */
+ SERVICE_SUCCEEDED: 3,
+ /**
+ * Constant: SERVICE_FAILED
+ * {Integer} used for
+ */
+ SERVICE_FAILED: 4,
+ /**
+ * Function: removeItem
+ * Remove an object from an array. Iterates through the array
+ * to find the item, then removes it.
+ *
+ * Parameters:
+ * array - {Array}
+ * item - {Object}
+ *
+ * Return
+ * {Array} A reference to the array
+ */
+ removeItem: function(array, item) {
+ for(var i = array.length - 1; i >= 0; i--) {
+ if(array[i] == item) {
+ array.splice(i,1);
+ }
+ }
+ return array;
+ },
+ /**
+ * Function: indexOf
+ *
+ * Parameters:
+ * array - {Array}
+ * obj - {Object}
+ *
+ * Returns:
+ * {Integer} The index at, which the first object was found in the array.
+ * If not found, returns -1.
+ */
+ indexOf: function(array, obj) {
+ for(var i=0, len=array.length; i} (or any object with both .x, .y properties)
+ * p2 - {} (or any object with both .x, .y properties)
+ *
+ * Returns:
+ * {Float} The distance (in km) between the two input points as measured on an
+ * ellipsoid. Note that the input point objects must be in geographic
+ * coordinates (decimal degrees) and the return distance is in kilometers.
+ */
+ distVincenty: function(p1, p2) {
+ var a = 6378137, b = 6356752.3142, f = 1/298.257223563;
+ var L = ZOO.rad(p2.x - p1.y);
+ var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y)));
+ var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y)));
+ var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+ var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+ var lambda = L, lambdaP = 2*Math.PI;
+ var iterLimit = 20;
+ while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+ var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+ var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+ (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+ if (sinSigma==0) {
+ return 0; // co-incident points
+ }
+ var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+ var sigma = Math.atan2(sinSigma, cosSigma);
+ var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+ var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+ var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+ var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+ lambdaP = lambda;
+ lambda = L + (1-C) * f * Math.sin(alpha) *
+ (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+ }
+ if (iterLimit==0) {
+ return NaN; // formula failed to converge
+ }
+ var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+ var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+ var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+ var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+ B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+ var s = b*A*(sigma-deltaSigma);
+ var d = s.toFixed(3)/1000; // round to 1mm precision
+ return d;
+ },
+ /**
+ * Function: Class
+ * Method used to create ZOO classes. Includes support for
+ * multiple inheritance.
+ */
+ Class: function() {
+ var Class = function() {
+ this.initialize.apply(this, arguments);
+ };
+ var extended = {};
+ var parent;
+ for(var i=0; i 0) {
+ var separator = (url.indexOf('?') > -1) ? '&' : '?';
+ url += separator + paramString;
+ }
+ return ZOORequest('GET',url);
+ },
+ /**
+ * Function: POST
+ * Send an HTTP POST request.
+ *
+ * Parameters:
+ * url - {String} The URL to request.
+ * body - {String} The request's body to send.
+ * headers - {Object} A key-value object of headers to push to
+ * the request's head
+ *
+ * Returns:
+ * {String} Request result.
+ */
+ Post: function(url,body,headers) {
+ if(!(headers instanceof Array)) {
+ var headersArray = [];
+ for (var name in headers) {
+ headersArray.push(name+': '+headers[name]);
+ }
+ headers = headersArray;
+ }
+ return ZOORequest('POST',url,body,headers);
+ }
+};
+
+/**
+ * Class: ZOO.Bounds
+ * Instances of this class represent bounding boxes. Data stored as left,
+ * bottom, right, top floats. All values are initialized to null,
+ * however, you should make sure you set them before using the bounds
+ * for anything.
+ */
+ZOO.Bounds = ZOO.Class({
+ /**
+ * Property: left
+ * {Number} Minimum horizontal coordinate.
+ */
+ left: null,
+ /**
+ * Property: bottom
+ * {Number} Minimum vertical coordinate.
+ */
+ bottom: null,
+ /**
+ * Property: right
+ * {Number} Maximum horizontal coordinate.
+ */
+ right: null,
+ /**
+ * Property: top
+ * {Number} Maximum vertical coordinate.
+ */
+ top: null,
+ /**
+ * Constructor: ZOO.Bounds
+ * Construct a new bounds object.
+ *
+ * Parameters:
+ * left - {Number} The left bounds of the box. Note that for width
+ * calculations, this is assumed to be less than the right value.
+ * bottom - {Number} The bottom bounds of the box. Note that for height
+ * calculations, this is assumed to be more than the top value.
+ * right - {Number} The right bounds.
+ * top - {Number} The top bounds.
+ */
+ initialize: function(left, bottom, right, top) {
+ if (left != null)
+ this.left = parseFloat(left);
+ if (bottom != null)
+ this.bottom = parseFloat(bottom);
+ if (right != null)
+ this.right = parseFloat(right);
+ if (top != null)
+ this.top = parseFloat(top);
+ },
+ /**
+ * Method: clone
+ * Create a cloned instance of this bounds.
+ *
+ * Returns:
+ * {} A fresh copy of the bounds
+ */
+ clone:function() {
+ return new ZOO.Bounds(this.left, this.bottom,
+ this.right, this.top);
+ },
+ /**
+ * Method: equals
+ * Test a two bounds for equivalence.
+ *
+ * Parameters:
+ * bounds - {}
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object has the same left,
+ * right, top, bottom components as this. Note that if bounds
+ * passed in is null, returns false.
+ */
+ equals:function(bounds) {
+ var equals = false;
+ if (bounds != null)
+ equals = ((this.left == bounds.left) &&
+ (this.right == bounds.right) &&
+ (this.top == bounds.top) &&
+ (this.bottom == bounds.bottom));
+ return equals;
+ },
+ /**
+ * Method: toString
+ *
+ * Returns:
+ * {String} String representation of bounds object.
+ * (ex."left-bottom=(5,42) right-top=(10,45)")
+ */
+ toString:function() {
+ return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+ + " right-top=(" + this.right + "," + this.top + ")" );
+ },
+ /**
+ * APIMethod: toArray
+ *
+ * Returns:
+ * {Array} array of left, bottom, right, top
+ */
+ toArray: function() {
+ return [this.left, this.bottom, this.right, this.top];
+ },
+ /**
+ * Method: toBBOX
+ *
+ * Parameters:
+ * decimal - {Integer} How many significant digits in the bbox coords?
+ * Default is 6
+ *
+ * Returns:
+ * {String} Simple String representation of bounds object.
+ * (ex. "5,42,10,45")
+ */
+ toBBOX:function(decimal) {
+ if (decimal== null)
+ decimal = 6;
+ var mult = Math.pow(10, decimal);
+ var bbox = Math.round(this.left * mult) / mult + "," +
+ Math.round(this.bottom * mult) / mult + "," +
+ Math.round(this.right * mult) / mult + "," +
+ Math.round(this.top * mult) / mult;
+ return bbox;
+ },
+ /**
+ * Method: toGeometry
+ * Create a new polygon geometry based on this bounds.
+ *
+ * Returns:
+ * {} A new polygon with the coordinates
+ * of this bounds.
+ */
+ toGeometry: function() {
+ return new ZOO.Geometry.Polygon([
+ new ZOO.Geometry.LinearRing([
+ new ZOO.Geometry.Point(this.left, this.bottom),
+ new ZOO.Geometry.Point(this.right, this.bottom),
+ new ZOO.Geometry.Point(this.right, this.top),
+ new ZOO.Geometry.Point(this.left, this.top)
+ ])
+ ]);
+ },
+ /**
+ * Method: getWidth
+ *
+ * Returns:
+ * {Float} The width of the bounds
+ */
+ getWidth:function() {
+ return (this.right - this.left);
+ },
+ /**
+ * Method: getHeight
+ *
+ * Returns:
+ * {Float} The height of the bounds (top minus bottom).
+ */
+ getHeight:function() {
+ return (this.top - this.bottom);
+ },
+ /**
+ * Method: add
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ *
+ * Returns:
+ * {} A new bounds whose coordinates are the same as
+ * this, but shifted by the passed-in x and y values.
+ */
+ add:function(x, y) {
+ if ( (x == null) || (y == null) )
+ return null;
+ return new ZOO.Bounds(this.left + x, this.bottom + y,
+ this.right + x, this.top + y);
+ },
+ /**
+ * Method: extend
+ * Extend the bounds to include the point, lonlat, or bounds specified.
+ * Note, this function assumes that left < right and bottom < top.
+ *
+ * Parameters:
+ * object - {Object} Can be Point, or Bounds
+ */
+ extend:function(object) {
+ var bounds = null;
+ if (object) {
+ // clear cached center location
+ switch(object.CLASS_NAME) {
+ case "ZOO.Geometry.Point":
+ bounds = new ZOO.Bounds(object.x, object.y,
+ object.x, object.y);
+ break;
+ case "ZOO.Bounds":
+ bounds = object;
+ break;
+ }
+ if (bounds) {
+ if ( (this.left == null) || (bounds.left < this.left))
+ this.left = bounds.left;
+ if ( (this.bottom == null) || (bounds.bottom < this.bottom) )
+ this.bottom = bounds.bottom;
+ if ( (this.right == null) || (bounds.right > this.right) )
+ this.right = bounds.right;
+ if ( (this.top == null) || (bounds.top > this.top) )
+ this.top = bounds.top;
+ }
+ }
+ },
+ /**
+ * APIMethod: contains
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ * inclusive - {Boolean} Whether or not to include the border.
+ * Default is true.
+ *
+ * Returns:
+ * {Boolean} Whether or not the passed-in coordinates are within this
+ * bounds.
+ */
+ contains:function(x, y, inclusive) {
+ //set default
+ if (inclusive == null)
+ inclusive = true;
+ if (x == null || y == null)
+ return false;
+ x = parseFloat(x);
+ y = parseFloat(y);
+
+ var contains = false;
+ if (inclusive)
+ contains = ((x >= this.left) && (x <= this.right) &&
+ (y >= this.bottom) && (y <= this.top));
+ else
+ contains = ((x > this.left) && (x < this.right) &&
+ (y > this.bottom) && (y < this.top));
+ return contains;
+ },
+ /**
+ * Method: intersectsBounds
+ * Determine whether the target bounds intersects this bounds. Bounds are
+ * considered intersecting if any of their edges intersect or if one
+ * bounds contains the other.
+ *
+ * Parameters:
+ * bounds - {} The target bounds.
+ * inclusive - {Boolean} Treat coincident borders as intersecting. Default
+ * is true. If false, bounds that do not overlap but only touch at the
+ * border will not be considered as intersecting.
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object intersects this bounds.
+ */
+ intersectsBounds:function(bounds, inclusive) {
+ if (inclusive == null)
+ inclusive = true;
+ var intersects = false;
+ var mightTouch = (
+ this.left == bounds.right ||
+ this.right == bounds.left ||
+ this.top == bounds.bottom ||
+ this.bottom == bounds.top
+ );
+ if (inclusive || !mightTouch) {
+ var inBottom = (
+ ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
+ ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
+ );
+ var inTop = (
+ ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
+ ((this.top > bounds.bottom) && (this.top < bounds.top))
+ );
+ var inLeft = (
+ ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
+ ((this.left >= bounds.left) && (this.left <= bounds.right))
+ );
+ var inRight = (
+ ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
+ ((this.right >= bounds.left) && (this.right <= bounds.right))
+ );
+ intersects = ((inBottom || inTop) && (inLeft || inRight));
+ }
+ return intersects;
+ },
+ /**
+ * Method: containsBounds
+ * Determine whether the target bounds is contained within this bounds.
+ *
+ * bounds - {} The target bounds.
+ * partial - {Boolean} If any of the target corners is within this bounds
+ * consider the bounds contained. Default is false. If true, the
+ * entire target bounds must be contained within this bounds.
+ * inclusive - {Boolean} Treat shared edges as contained. Default is
+ * true.
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object is contained within this bounds.
+ */
+ containsBounds:function(bounds, partial, inclusive) {
+ if (partial == null)
+ partial = false;
+ if (inclusive == null)
+ inclusive = true;
+ var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
+ var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
+ var topLeft = this.contains(bounds.left, bounds.top, inclusive);
+ var topRight = this.contains(bounds.right, bounds.top, inclusive);
+ return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
+ : (bottomLeft && bottomRight && topLeft && topRight);
+ },
+ CLASS_NAME: 'ZOO.Bounds'
+});
+
+/**
+ * Class: ZOO.Projection
+ * Class for coordinate transforms between coordinate systems.
+ * Depends on the zoo-proj4js library. zoo-proj4js library
+ * is loaded by the ZOO Kernel with zoo-api.
+ */
+ZOO.Projection = ZOO.Class({
+ /**
+ * Property: proj
+ * {Object} Proj4js.Proj instance.
+ */
+ proj: null,
+ /**
+ * Property: projCode
+ * {String}
+ */
+ projCode: null,
+ /**
+ * Constructor: ZOO.Projection
+ * This class offers several methods for interacting with a wrapped
+ * zoo-pro4js projection object.
+ *
+ * Parameters:
+ * projCode - {String} A string identifying the Well Known Identifier for
+ * the projection.
+ * options - {Object} An optional object to set additional properties.
+ *
+ * Returns:
+ * {} A projection object.
+ */
+ initialize: function(projCode, options) {
+ ZOO.extend(this, options);
+ this.projCode = projCode;
+ if (Proj4js) {
+ this.proj = new Proj4js.Proj(projCode);
+ }
+ },
+ /**
+ * Method: getCode
+ * Get the string SRS code.
+ *
+ * Returns:
+ * {String} The SRS code.
+ */
+ getCode: function() {
+ return this.proj ? this.proj.srsCode : this.projCode;
+ },
+ /**
+ * Method: getUnits
+ * Get the units string for the projection -- returns null if
+ * zoo-proj4js is not available.
+ *
+ * Returns:
+ * {String} The units abbreviation.
+ */
+ getUnits: function() {
+ return this.proj ? this.proj.units : null;
+ },
+ /**
+ * Method: toString
+ * Convert projection to string (getCode wrapper).
+ *
+ * Returns:
+ * {String} The projection code.
+ */
+ toString: function() {
+ return this.getCode();
+ },
+ /**
+ * Method: equals
+ * Test equality of two projection instances. Determines equality based
+ * soley on the projection code.
+ *
+ * Returns:
+ * {Boolean} The two projections are equivalent.
+ */
+ equals: function(projection) {
+ if (projection && projection.getCode)
+ return this.getCode() == projection.getCode();
+ else
+ return false;
+ },
+ /* Method: destroy
+ * Destroy projection object.
+ */
+ destroy: function() {
+ this.proj = null;
+ this.projCode = null;
+ },
+ CLASS_NAME: 'ZOO.Projection'
+});
+/**
+ * Method: transform
+ * Transform a point coordinate from one projection to another. Note that
+ * the input point is transformed in place.
+ *
+ * Parameters:
+ * point - {{ZOO.Geometry.Point> | Object} An object with x and y
+ * properties representing coordinates in those dimensions.
+ * sourceProj - {ZOO.Projection} Source map coordinate system
+ * destProj - {ZOO.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate. The original point is modified.
+ */
+ZOO.Projection.transform = function(point, source, dest) {
+ if (source.proj && dest.proj)
+ point = Proj4js.transform(source.proj, dest.proj, point);
+ return point;
+};
+
+/**
+ * Class: ZOO.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ * of ZOO.Format are expected to have read and write methods.
+ */
+ZOO.Format = ZOO.Class({
+ /**
+ * Property: options
+ * {Object} A reference to options passed to the constructor.
+ */
+ options:null,
+ /**
+ * Property: externalProjection
+ * {} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The externalProjection is the projection used by
+ * the content which is passed into read or which comes out of write.
+ * In order to reproject, a projection transformation function for the
+ * specified projections must be available. This support is provided
+ * via zoo-proj4js.
+ */
+ externalProjection: null,
+ /**
+ * Property: internalProjection
+ * {} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The internalProjection is the projection used by
+ * the geometries which are returned by read or which are passed into
+ * write. In order to reproject, a projection transformation function
+ * for the specified projections must be available. This support is
+ * provided via zoo-proj4js.
+ */
+ internalProjection: null,
+ /**
+ * Property: data
+ * {Object} When is true, this is the parsed string sent to
+ * .
+ */
+ data: null,
+ /**
+ * Property: keepData
+ * {Object} Maintain a reference () to the most recently read data.
+ * Default is false.
+ */
+ keepData: false,
+ /**
+ * Constructor: ZOO.Format
+ * Instances of this class are not useful. See one of the subclasses.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * format
+ *
+ * Valid options:
+ * keepData - {Boolean} If true, upon , the data property will be
+ * set to the parsed object (e.g. the json or xml object).
+ *
+ * Returns:
+ * An instance of ZOO.Format
+ */
+ initialize: function(options) {
+ ZOO.extend(this, options);
+ this.options = options;
+ },
+ /**
+ * Method: destroy
+ * Clean up.
+ */
+ destroy: function() {
+ },
+ /**
+ * Method: read
+ * Read data from a string, and return an object whose type depends on the
+ * subclass.
+ *
+ * Parameters:
+ * data - {string} Data to read/parse.
+ *
+ * Returns:
+ * Depends on the subclass
+ */
+ read: function(data) {
+ },
+ /**
+ * Method: write
+ * Accept an object, and return a string.
+ *
+ * Parameters:
+ * object - {Object} Object to be serialized
+ *
+ * Returns:
+ * {String} A string representation of the object.
+ */
+ write: function(data) {
+ },
+ CLASS_NAME: 'ZOO.Format'
+});
+/**
+ * Class: ZOO.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.WKT = ZOO.Class(ZOO.Format, {
+ /**
+ * Constructor: ZOO.Format.WKT
+ * Create a new parser for WKT
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance
+ *
+ * Returns:
+ * {} A new WKT parser.
+ */
+ initialize: function(options) {
+ this.regExes = {
+ 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+ 'spaces': /\s+/,
+ 'parenComma': /\)\s*,\s*\(/,
+ 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
+ 'trimParens': /^\s*\(?(.*?)\)?\s*$/
+ };
+ ZOO.Format.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * Method: read
+ * Deserialize a WKT string and return a vector feature or an
+ * array of vector features. Supports WKT for POINT,
+ * MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON,
+ * MULTIPOLYGON, and GEOMETRYCOLLECTION.
+ *
+ * Parameters:
+ * wkt - {String} A WKT string
+ *
+ * Returns:
+ * {|Array} A feature or array of features for
+ * GEOMETRYCOLLECTION WKT.
+ */
+ read: function(wkt) {
+ var features, type, str;
+ var matches = this.regExes.typeStr.exec(wkt);
+ if(matches) {
+ type = matches[1].toLowerCase();
+ str = matches[2];
+ if(this.parse[type]) {
+ features = this.parse[type].apply(this, [str]);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ if (features &&
+ features.CLASS_NAME == "ZOO.Feature") {
+ features.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ } else if (features &&
+ type != "geometrycollection" &&
+ typeof features == "object") {
+ for (var i=0, len=features.length; i|Array} A feature or array of
+ * features
+ *
+ * Returns:
+ * {String} The WKT string representation of the input geometries
+ */
+ write: function(features) {
+ var collection, geometry, type, data, isCollection;
+ if(features.constructor == Array) {
+ collection = features;
+ isCollection = true;
+ } else {
+ collection = [features];
+ isCollection = false;
+ }
+ var pieces = [];
+ if(isCollection)
+ pieces.push('GEOMETRYCOLLECTION(');
+ for(var i=0, len=collection.length; i0)
+ pieces.push(',');
+ geometry = collection[i].geometry;
+ type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+ if(!this.extract[type])
+ return null;
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ data = this.extract[type].apply(this, [geometry]);
+ pieces.push(type.toUpperCase() + '(' + data + ')');
+ }
+ if(isCollection)
+ pieces.push(')');
+ return pieces.join('');
+ },
+ /**
+ * Property: extract
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual data extraction.
+ */
+ extract: {
+ /**
+ * Return a space delimited string of point coordinates.
+ * @param {} point
+ * @returns {String} A string of coordinates representing the point
+ */
+ 'point': function(point) {
+ return point.x + ' ' + point.y;
+ },
+ /**
+ * Return a comma delimited string of point coordinates from a multipoint.
+ * @param {} multipoint
+ * @returns {String} A string of point coordinate strings representing
+ * the multipoint
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i} linestring
+ * @returns {String} A string of point coordinate strings representing
+ * the linestring
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i} multilinestring
+ * @returns {String} A string of of linestring strings representing
+ * the multilinestring
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i} polygon
+ * @returns {String} An array of linear ring arrays representing the polygon
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i} multipolygon
+ * @returns {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i} A point feature
+ */
+ 'point': function(str) {
+ var coords = ZOO.String.trim(str).split(this.regExes.spaces);
+ return new ZOO.Feature(
+ new ZOO.Geometry.Point(coords[0], coords[1])
+ );
+ },
+ /**
+ * Method: parse.multipoint
+ * Return a multipoint feature given a multipoint WKT fragment.
+ *
+ * Parameters:
+ * str - {String} A WKT fragment representing the multipoint
+ *
+ * Returns:
+ * {} A multipoint feature
+ */
+ 'multipoint': function(str) {
+ var points = ZOO.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i} A linestring feature
+ */
+ 'linestring': function(str) {
+ var points = ZOO.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i} A multilinestring feature
+ */
+ 'multilinestring': function(str) {
+ var line;
+ var lines = ZOO.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=lines.length; i} A polygon feature
+ */
+ 'polygon': function(str) {
+ var ring, linestring, linearring;
+ var rings = ZOO.String.trim(str).split(this.regExes.parenComma);
+ var components = [];
+ for(var i=0, len=rings.length; i} A multipolygon feature
+ */
+ 'multipolygon': function(str) {
+ var polygon;
+ var polygons = ZOO.String.trim(str).split(this.regExes.doubleParenComma);
+ var components = [];
+ for(var i=0, len=polygons.length; i constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.JSON = ZOO.Class(ZOO.Format, {
+ /**
+ * Property: indent
+ * {String} For "pretty" printing, the indent string will be used once for
+ * each indentation level.
+ */
+ indent: " ",
+ /**
+ * Property: space
+ * {String} For "pretty" printing, the space string will be used after
+ * the ":" separating a name/value pair.
+ */
+ space: " ",
+ /**
+ * Property: newline
+ * {String} For "pretty" printing, the newline string will be used at the
+ * end of each name/value pair or array item.
+ */
+ newline: "\n",
+ /**
+ * Property: level
+ * {Integer} For "pretty" printing, this is incremented/decremented during
+ * serialization.
+ */
+ level: 0,
+ /**
+ * Property: pretty
+ * {Boolean} Serialize with extra whitespace for structure. This is set
+ * by the method.
+ */
+ pretty: false,
+ /**
+ * Constructor: ZOO.Format.JSON
+ * Create a new parser for JSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ ZOO.Format.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * Method: read
+ * Deserialize a json string.
+ *
+ * Parameters:
+ * json - {String} A JSON string
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} An object, array, string, or number .
+ */
+ read: function(json, filter) {
+ /**
+ * Parsing happens in three stages. In the first stage, we run the text
+ * against a regular expression which looks for non-JSON
+ * characters. We are especially concerned with '()' and 'new'
+ * because they can cause invocation, and '=' because it can cause
+ * mutation. But just to be safe, we will reject all unexpected
+ * characters.
+ */
+ try {
+ if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+ /**
+ * In the second stage we use the eval function to compile the
+ * text into a JavaScript structure. The '{' operator is
+ * subject to a syntactic ambiguity in JavaScript - it can
+ * begin a block or an object literal. We wrap the text in
+ * parens to eliminate the ambiguity.
+ */
+ var object = eval('(' + json + ')');
+ /**
+ * In the optional third stage, we recursively walk the new
+ * structure, passing each name/value pair to a filter
+ * function for possible transformation.
+ */
+ if(typeof filter === 'function') {
+ function walk(k, v) {
+ if(v && typeof v === 'object') {
+ for(var i in v) {
+ if(v.hasOwnProperty(i)) {
+ v[i] = walk(i, v[i]);
+ }
+ }
+ }
+ return filter(k, v);
+ }
+ object = walk('', object);
+ }
+ if(this.keepData) {
+ this.data = object;
+ }
+ return object;
+ }
+ } catch(e) {
+ // Fall through if the regexp test fails.
+ }
+ return null;
+ },
+ /**
+ * Method: write
+ * Serialize an object into a JSON string.
+ *
+ * Parameters:
+ * value - {String} The object, array, string, number, boolean or date
+ * to be serialized.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The JSON string representation of the input value.
+ */
+ write: function(value, pretty) {
+ this.pretty = !!pretty;
+ var json = null;
+ var type = typeof value;
+ if(this.serialize[type]) {
+ try {
+ json = this.serialize[type].apply(this, [value]);
+ } catch(err) {
+ //OpenLayers.Console.error("Trouble serializing: " + err);
+ }
+ }
+ return json;
+ },
+ /**
+ * Method: writeIndent
+ * Output an indentation string depending on the indentation level.
+ *
+ * Returns:
+ * {String} An appropriate indentation string.
+ */
+ writeIndent: function() {
+ var pieces = [];
+ if(this.pretty) {
+ for(var i=0; i 0)
+ pieces.push(',');
+ pieces.push(this.writeNewline(), this.writeIndent(), json);
+ }
+ }
+ this.level -= 1;
+ pieces.push(this.writeNewline(), this.writeIndent(), ']');
+ return pieces.join('');
+ },
+ /**
+ * Method: serialize.string
+ * Transform a string into a JSON string.
+ *
+ * Parameters:
+ * string - {String} The string to be serialized
+ *
+ * Returns:
+ * {String} A JSON string representing the string.
+ */
+ 'string': function(string) {
+ var m = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+ if(/["\\\x00-\x1f]/.test(string)) {
+ return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+ var c = m[b];
+ if(c)
+ return c;
+ c = b.charCodeAt();
+ return '\\u00' +
+ Math.floor(c / 16).toString(16) +
+ (c % 16).toString(16);
+ }) + '"';
+ }
+ return '"' + string + '"';
+ },
+ /**
+ * Method: serialize.number
+ * Transform a number into a JSON string.
+ *
+ * Parameters:
+ * number - {Number} The number to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the number.
+ */
+ 'number': function(number) {
+ return isFinite(number) ? String(number) : "null";
+ },
+ /**
+ * Method: serialize.boolean
+ * Transform a boolean into a JSON string.
+ *
+ * Parameters:
+ * bool - {Boolean} The boolean to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the boolean.
+ */
+ 'boolean': function(bool) {
+ return String(bool);
+ },
+ /**
+ * Method: serialize.date
+ * Transform a date into a JSON string.
+ *
+ * Parameters:
+ * date - {Date} The date to be serialized.
+ *
+ * Returns:
+ * {String} A JSON string representing the date.
+ */
+ 'date': function(date) {
+ function format(number) {
+ // Format integers to have at least two digits.
+ return (number < 10) ? '0' + number : number;
+ }
+ return '"' + date.getFullYear() + '-' +
+ format(date.getMonth() + 1) + '-' +
+ format(date.getDate()) + 'T' +
+ format(date.getHours()) + ':' +
+ format(date.getMinutes()) + ':' +
+ format(date.getSeconds()) + '"';
+ }
+ },
+ CLASS_NAME: 'ZOO.Format.JSON'
+});
+/**
+ * Class: ZOO.Format.GeoJSON
+ * Read and write GeoJSON. Create a new parser with the
+ * constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.GeoJSON = ZOO.Class(ZOO.Format.JSON, {
+ /**
+ * Constructor: ZOO.Format.GeoJSON
+ * Create a new parser for GeoJSON.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ ZOO.Format.JSON.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * Method: read
+ * Deserialize a GeoJSON string.
+ *
+ * Parameters:
+ * json - {String} A GeoJSON string
+ * type - {String} Optional string that determines the structure of
+ * the output. Supported values are "Geometry", "Feature", and
+ * "FeatureCollection". If absent or null, a default of
+ * "FeatureCollection" is assumed.
+ * filter - {Function} A function which will be called for every key and
+ * value at every level of the final result. Each value will be
+ * replaced by the result of the filter function. This can be used to
+ * reform generic objects into instances of classes, or to transform
+ * date strings into Date objects.
+ *
+ * Returns:
+ * {Object} The return depends on the value of the type argument. If type
+ * is "FeatureCollection" (the default), the return will be an array
+ * of . If type is "Geometry", the input json
+ * must represent a single geometry, and the return will be an
+ * . If type is "Feature", the input json must
+ * represent a single feature, and the return will be an
+ * .
+ */
+ read: function(json, type, filter) {
+ type = (type) ? type : "FeatureCollection";
+ var results = null;
+ var obj = null;
+ if (typeof json == "string")
+ obj = ZOO.Format.JSON.prototype.read.apply(this,[json, filter]);
+ else
+ obj = json;
+ if(!obj) {
+ //ZOO.Console.error("Bad JSON: " + json);
+ } else if(typeof(obj.type) != "string") {
+ //ZOO.Console.error("Bad GeoJSON - no type: " + json);
+ } else if(this.isValidType(obj, type)) {
+ switch(type) {
+ case "Geometry":
+ try {
+ results = this.parseGeometry(obj);
+ } catch(err) {
+ //ZOO.Console.error(err);
+ }
+ break;
+ case "Feature":
+ try {
+ results = this.parseFeature(obj);
+ results.type = "Feature";
+ } catch(err) {
+ //ZOO.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ // for type FeatureCollection, we allow input to be any type
+ results = [];
+ switch(obj.type) {
+ case "Feature":
+ try {
+ results.push(this.parseFeature(obj));
+ } catch(err) {
+ results = null;
+ //ZOO.Console.error(err);
+ }
+ break;
+ case "FeatureCollection":
+ for(var i=0, len=obj.features.length; i.
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {} A feature.
+ */
+ parseFeature: function(obj) {
+ var feature, geometry, attributes, bbox;
+ attributes = (obj.properties) ? obj.properties : {};
+ bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
+ try {
+ geometry = this.parseGeometry(obj.geometry);
+ } catch(err) {
+ // deal with bad geometries
+ throw err;
+ }
+ feature = new ZOO.Feature(geometry, attributes);
+ if(bbox)
+ feature.bounds = ZOO.Bounds.fromArray(bbox);
+ if(obj.id)
+ feature.fid = obj.id;
+ return feature;
+ },
+ /**
+ * Method: parseGeometry
+ * Convert a geometry object from GeoJSON into an .
+ *
+ * Parameters:
+ * obj - {Object} An object created from a GeoJSON object
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ parseGeometry: function(obj) {
+ if (obj == null)
+ return null;
+ var geometry, collection = false;
+ if(obj.type == "GeometryCollection") {
+ if(!(obj.geometries instanceof Array)) {
+ throw "GeometryCollection must have geometries array: " + obj;
+ }
+ var numGeom = obj.geometries.length;
+ var components = new Array(numGeom);
+ for(var i=0; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "point": function(array) {
+ if(array.length != 2) {
+ throw "Only 2D points are supported: " + array;
+ }
+ return new ZOO.Geometry.Point(array[0], array[1]);
+ },
+ /**
+ * Method: parseCoords.multipoint
+ * Convert a coordinate array from GeoJSON into an
+ * .
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "multipoint": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "linestring": function(array) {
+ var points = [];
+ var p = null;
+ for(var i=0, len=array.length; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "multilinestring": function(array) {
+ var lines = [];
+ var l = null;
+ for(var i=0, len=array.length; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "polygon": function(array) {
+ var rings = [];
+ var r, l;
+ for(var i=0, len=array.length; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "multipolygon": function(array) {
+ var polys = [];
+ var p = null;
+ for(var i=0, len=array.length; i.
+ *
+ * Parameters:
+ * array - {Object} The coordinates array from the GeoJSON fragment.
+ *
+ * Returns:
+ * {} A geometry.
+ */
+ "box": function(array) {
+ if(array.length != 2) {
+ throw "GeoJSON box coordinates must have 2 elements";
+ }
+ return new ZOO.Geometry.Polygon([
+ new ZOO.Geometry.LinearRing([
+ new ZOO.Geometry.Point(array[0][0], array[0][1]),
+ new ZOO.Geometry.Point(array[1][0], array[0][1]),
+ new ZOO.Geometry.Point(array[1][0], array[1][1]),
+ new ZOO.Geometry.Point(array[0][0], array[1][1]),
+ new Z0O.Geometry.Point(array[0][0], array[0][1])
+ ])
+ ]);
+ }
+ },
+ /**
+ * Method: write
+ * Serialize a feature, geometry, array of features into a GeoJSON string.
+ *
+ * Parameters:
+ * obj - {Object} An , ,
+ * or an array of features.
+ * pretty - {Boolean} Structure the output with newlines and indentation.
+ * Default is false.
+ *
+ * Returns:
+ * {String} The GeoJSON string representation of the input geometry,
+ * features, or array of features.
+ */
+ write: function(obj, pretty) {
+ var geojson = {
+ "type": null
+ };
+ if(obj instanceof Array) {
+ geojson.type = "FeatureCollection";
+ var numFeatures = obj.length;
+ geojson.features = new Array(numFeatures);
+ for(var i=0; i}
+ *
+ * Returns:
+ * {Object} An object which can be assigned to the crs property
+ * of a GeoJSON object.
+ */
+ createCRSObject: function(object) {
+ //var proj = object.layer.projection.toString();
+ var proj = object.projection.toString();
+ var crs = {};
+ if (proj.match(/epsg:/i)) {
+ var code = parseInt(proj.substring(proj.indexOf(":") + 1));
+ if (code == 4326) {
+ crs = {
+ "type": "OGC",
+ "properties": {
+ "urn": "urn:ogc:def:crs:OGC:1.3:CRS84"
+ }
+ };
+ } else {
+ crs = {
+ "type": "EPSG",
+ "properties": {
+ "code": code
+ }
+ };
+ }
+ }
+ return crs;
+ },
+ /**
+ * Property: extract
+ * Object with properties corresponding to the GeoJSON types.
+ * Property values are functions that do the actual value extraction.
+ */
+ extract: {
+ /**
+ * Method: extract.feature
+ * Return a partial GeoJSON object representing a single feature.
+ *
+ * Parameters:
+ * feature - {}
+ *
+ * Returns:
+ * {Object} An object representing the point.
+ */
+ 'feature': function(feature) {
+ var geom = this.extract.geometry.apply(this, [feature.geometry]);
+ return {
+ "type": "Feature",
+ "id": feature.fid == null ? feature.id : feature.fid,
+ "properties": feature.attributes,
+ "geometry": geom
+ };
+ },
+ /**
+ * Method: extract.geometry
+ * Return a GeoJSON object representing a single geometry.
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {Object} An object representing the geometry.
+ */
+ 'geometry': function(geometry) {
+ if (geometry == null)
+ return null;
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var geometryType = geometry.CLASS_NAME.split('.')[2];
+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
+ var json;
+ if(geometryType == "Collection")
+ json = {
+ "type": "GeometryCollection",
+ "geometries": data
+ };
+ else
+ json = {
+ "type": geometryType,
+ "coordinates": data
+ };
+ return json;
+ },
+ /**
+ * Method: extract.point
+ * Return an array of coordinates from a point.
+ *
+ * Parameters:
+ * point - {}
+ *
+ * Returns:
+ * {Array} An array of coordinates representing the point.
+ */
+ 'point': function(point) {
+ return [point.x, point.y];
+ },
+ /**
+ * Method: extract.multipoint
+ * Return an array of coordinates from a multipoint.
+ *
+ * Parameters:
+ * multipoint - {}
+ *
+ * Returns:
+ * {Array} An array of point coordinate arrays representing
+ * the multipoint.
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i}
+ *
+ * Returns:
+ * {Array} An array of coordinate arrays representing
+ * the linestring.
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i}
+ *
+ * Returns:
+ * {Array} An array of linestring arrays representing
+ * the multilinestring.
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i}
+ *
+ * Returns:
+ * {Array} An array of linear ring arrays representing the polygon.
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i}
+ *
+ * Returns:
+ * {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i}
+ *
+ * Returns:
+ * {Array} An array of geometry objects representing the geometry
+ * collection.
+ */
+ 'collection': function(collection) {
+ var len = collection.components.length;
+ var array = new Array(len);
+ for(var i=0; i
+ * constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.KML = ZOO.Class(ZOO.Format, {
+ /**
+ * Property: kmlns
+ * {String} KML Namespace to use. Defaults to 2.2 namespace.
+ */
+ kmlns: "http://www.opengis.net/kml/2.2",
+ /**
+ * Property: foldersName
+ * {String} Name of the folders. Default is "ZOO export".
+ * If set to null, no name element will be created.
+ */
+ foldersName: "ZOO export",
+ /**
+ * Property: foldersDesc
+ * {String} Description of the folders. Default is "Exported on [date]."
+ * If set to null, no description element will be created.
+ */
+ foldersDesc: "Created on " + new Date(),
+ /**
+ * Property: placemarksDesc
+ * {String} Name of the placemarks. Default is "No description available".
+ */
+ placemarksDesc: "No description available",
+ /**
+ * Property: extractAttributes
+ * {Boolean} Extract attributes from KML. Default is true.
+ * Extracting styleUrls requires this to be set to true
+ */
+ extractAttributes: true,
+ /**
+ * Constructor: ZOO.Format.KML
+ * Create a new parser for KML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g),
+ kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
+ kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
+ straightBracket: (/\$\[(.*?)\]/g)
+ };
+ // KML coordinates are always in longlat WGS84
+ this.externalProjection = new ZOO.Projection("EPSG:4326");
+ ZOO.Format.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * APIMethod: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} data to read/parse.
+ *
+ * Returns:
+ * {Array()} List of features.
+ */
+ read: function(data) {
+ this.features = [];
+ data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+ data = new XML(data);
+ var placemarks = data..*::Placemark;
+ this.parseFeatures(placemarks);
+ return this.features;
+ },
+ /**
+ * Method: parseFeatures
+ * Loop through all Placemark nodes and parse them.
+ * Will create a list of features
+ *
+ * Parameters:
+ * nodes - {Array} of {E4XElement} data to read/parse.
+ * options - {Object} Hash of options
+ *
+ */
+ parseFeatures: function(nodes) {
+ var features = new Array(nodes.length());
+ for(var i=0, len=nodes.length(); i} A vector feature.
+ */
+ parseFeature: function(node) {
+ // only accept one geometry per feature - look for highest "order"
+ var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
+ var type, nodeList, geometry, parser;
+ for(var i=0, len=order.length; i 0) {
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+ // construct feature (optionally with attributes)
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new ZOO.Feature(geometry, attributes);
+ var fid = node.@id || node.@name;
+ if(fid != null)
+ feature.fid = fid;
+ return feature;
+ },
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+ /**
+ * Method: parseGeometry.point
+ * Given a KML node representing a point geometry, create a ZOO
+ * point geometry.
+ *
+ * Parameters:
+ * node - {E4XElement} A KML Point node.
+ *
+ * Returns:
+ * {} A point geometry.
+ */
+ 'point': function(node) {
+ var coordString = node.*::coordinates.toString();
+ coordString = coordString.replace(this.regExes.removeSpace, "");
+ coords = coordString.split(",");
+ var point = null;
+ if(coords.length > 1) {
+ // preserve third dimension
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ point = new ZOO.Geometry.Point(coords[0], coords[1], coords[2]);
+ }
+ return point;
+ },
+ /**
+ * Method: parseGeometry.linestring
+ * Given a KML node representing a linestring geometry, create a
+ * ZOO linestring geometry.
+ *
+ * Parameters:
+ * node - {E4XElement} A KML LineString node.
+ *
+ * Returns:
+ * {} A linestring geometry.
+ */
+ 'linestring': function(node, ring) {
+ var line = null;
+ var coordString = node.*::coordinates.toString();
+ coordString = coordString.replace(this.regExes.trimSpace,
+ "");
+ coordString = coordString.replace(this.regExes.trimComma,
+ ",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ var numPoints = pointList.length;
+ var points = new Array(numPoints);
+ var coords, numCoords;
+ for(var i=0; i 1) {
+ if(coords.length == 2) {
+ coords[2] = null;
+ }
+ points[i] = new ZOO.Geometry.Point(coords[0],
+ coords[1],
+ coords[2]);
+ }
+ }
+ if(numPoints) {
+ if(ring) {
+ line = new ZOO.Geometry.LinearRing(points);
+ } else {
+ line = new ZOO.Geometry.LineString(points);
+ }
+ } else {
+ throw "Bad LineString coordinates: " + coordString;
+ }
+ return line;
+ },
+ /**
+ * Method: parseGeometry.polygon
+ * Given a KML node representing a polygon geometry, create a
+ * ZOO polygon geometry.
+ *
+ * Parameters:
+ * node - {E4XElement} A KML Polygon node.
+ *
+ * Returns:
+ * {} A polygon geometry.
+ */
+ 'polygon': function(node) {
+ var nodeList = node..*::LinearRing;
+ var numRings = nodeList.length();
+ var components = new Array(numRings);
+ if(numRings > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0, len=nodeList.length(); i} A geometry collection.
+ */
+ 'multigeometry': function(node) {
+ var child, parser;
+ var parts = [];
+ var children = node.*::*;
+ for(var i=0, len=children.length(); i 0) {
+ attributes = this.parseExtendedData(edNodes[0])
+ }
+ var child, grandchildren;
+ var children = node.*::*;
+ for(var i=0, len=children.length(); i 0)
+ ed['value'] = valueNode[0].toString();
+ var nameNode = data.*::displayName;
+ if (nameNode.length() > 0)
+ ed['displayName'] = valueNode[0].toString();
+ attributes[key] = ed;
+ }
+ return attributes;
+ },
+ /**
+ * Method: write
+ * Accept Feature Collection, and return a string.
+ *
+ * Parameters:
+ * features - {Array(} An array of features.
+ *
+ * Returns:
+ * {String} A KML string.
+ */
+ write: function(features) {
+ if(!(features instanceof Array))
+ features = [features];
+ var kml = new XML('');
+ var folder = kml.Document.Folder;
+ folder.name = this.foldersName;
+ folder.description = this.foldersDesc;
+ for(var i=0, len=features.length; i}
+ *
+ * Returns:
+ * {E4XElement}
+ */
+ createPlacemark: function(feature) {
+ var placemark = new XML('');
+ placemark.name = (feature.attributes.name) ?
+ feature.attributes.name : feature.id;
+ placemark.description = (feature.attributes.description) ?
+ feature.attributes.description : this.placemarksDesc;
+ if(feature.fid != null)
+ placemark.@id = feature.fid;
+ placemark.*[2] = this.buildGeometryNode(feature.geometry);
+ return placemark;
+ },
+ /**
+ * Method: buildGeometryNode
+ * Builds and returns a KML geometry node with the given geometry.
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {E4XElement}
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ var node = null;
+ if(builder) {
+ node = builder.apply(this, [geometry]);
+ }
+ return node;
+ },
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ /**
+ * Method: buildGeometry.point
+ * Given a ZOO point geometry, create a KML point.
+ *
+ * Parameters:
+ * geometry - {} A point geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML point node.
+ */
+ 'point': function(geometry) {
+ var kml = new XML('');
+ kml.coordinates = this.buildCoordinatesNode(geometry);
+ return kml;
+ },
+ /**
+ * Method: buildGeometry.multipoint
+ * Given a ZOO multipoint geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {} A multipoint geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML GeometryCollection node.
+ */
+ 'multipoint': function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+ /**
+ * Method: buildGeometry.linestring
+ * Given a ZOO linestring geometry, create a KML linestring.
+ *
+ * Parameters:
+ * geometry - {} A linestring geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML linestring node.
+ */
+ 'linestring': function(geometry) {
+ var kml = new XML('');
+ kml.coordinates = this.buildCoordinatesNode(geometry);
+ return kml;
+ },
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given a ZOO multilinestring geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {} A multilinestring geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML GeometryCollection node.
+ */
+ 'multilinestring': function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+ /**
+ * Method: buildGeometry.linearring
+ * Given a ZOO linearring geometry, create a KML linearring.
+ *
+ * Parameters:
+ * geometry - {} A linearring geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML linearring node.
+ */
+ 'linearring': function(geometry) {
+ var kml = new XML('');
+ kml.coordinates = this.buildCoordinatesNode(geometry);
+ return kml;
+ },
+ /**
+ * Method: buildGeometry.polygon
+ * Given a ZOO polygon geometry, create a KML polygon.
+ *
+ * Parameters:
+ * geometry - {} A polygon geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML polygon node.
+ */
+ 'polygon': function(geometry) {
+ var kml = new XML('');
+ var rings = geometry.components;
+ var ringMember, ringGeom, type;
+ for(var i=0, len=rings.length; i'+type+'>');
+ ringMember.LinearRing = this.buildGeometry.linearring.apply(this,[rings[i]]);
+ kml.*[i] = ringMember;
+ }
+ return kml;
+ },
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given a ZOO multipolygon geometry, create a KML
+ * GeometryCollection.
+ *
+ * Parameters:
+ * geometry - {} A multipolygon geometry.
+ *
+ * Returns:
+ * {E4XElement} A KML GeometryCollection node.
+ */
+ 'multipolygon': function(geometry) {
+ return this.buildGeometry.collection.apply(this, [geometry]);
+ },
+ /**
+ * Method: buildGeometry.collection
+ * Given a ZOO geometry collection, create a KML MultiGeometry.
+ *
+ * Parameters:
+ * geometry - {} A geometry collection.
+ *
+ * Returns:
+ * {E4XElement} A KML MultiGeometry node.
+ */
+ 'collection': function(geometry) {
+ var kml = new XML('');
+ var child;
+ for(var i=0, len=geometry.components.length; i...
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Return:
+ * {E4XElement}
+ */
+ buildCoordinatesNode: function(geometry) {
+ var cooridnates = new XML('');
+ var points = geometry.components;
+ if(points) {
+ // LineString or LinearRing
+ var point;
+ var numPoints = points.length;
+ var parts = new Array(numPoints);
+ for(var i=0; i
+ * constructor. Supports the GML simple features profile.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.GML = ZOO.Class(ZOO.Format, {
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ogr: "http://ogr.maptools.org/",
+ gml: "http://www.opengis.net/gml",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance",
+ wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
+ },
+ /**
+ * Property: defaultPrefix
+ */
+ defaultPrefix: 'ogr',
+ /**
+ * Property: collectionName
+ * {String} Name of featureCollection element.
+ */
+ collectionName: "FeatureCollection",
+ /*
+ * Property: featureName
+ * {String} Element name for features. Default is "sql_statement".
+ */
+ featureName: "sql_statement",
+ /**
+ * Property: geometryName
+ * {String} Name of geometry element. Defaults to "geometryProperty".
+ */
+ geometryName: "geometryProperty",
+ /**
+ * Property: xy
+ * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
+ * Changing is not recommended, a new Format should be instantiated.
+ */
+ xy: true,
+ /**
+ * Property: extractAttributes
+ * {Boolean} Could we extract attributes
+ */
+ extractAttributes: true,
+ /**
+ * Constructor: ZOO.Format.GML
+ * Create a new parser for GML.
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance.
+ */
+ initialize: function(options) {
+ // compile regular expressions once instead of every time they are used
+ this.regExes = {
+ trimSpace: (/^\s*|\s*$/g),
+ removeSpace: (/\s*/g),
+ splitSpace: (/\s+/),
+ trimComma: (/\s*,\s*/g)
+ };
+ ZOO.Format.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * Method: read
+ * Read data from a string, and return a list of features.
+ *
+ * Parameters:
+ * data - {String} data to read/parse.
+ *
+ * Returns:
+ * {Array()} An array of features.
+ */
+ read: function(data) {
+ this.features = [];
+ data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+ data = new XML(data);
+
+ var gmlns = Namespace(this.namespaces['gml']);
+ var featureNodes = data..gmlns::featureMember;
+ if (data.localName() == 'featureMember')
+ featureNodes = data;
+ var features = [];
+ for(var i=0,len=featureNodes.length(); i 0) {
+ var parser = this.parseGeometry[type.toLowerCase()];
+ if(parser) {
+ geometry = parser.apply(this, [nodeList[0]]);
+ if (this.internalProjection && this.externalProjection) {
+ geometry.transform(this.externalProjection,
+ this.internalProjection);
+ }
+ }
+ // stop looking for different geometry types
+ break;
+ }
+ }
+ var attributes;
+ if(this.extractAttributes) {
+ attributes = this.parseAttributes(node);
+ }
+ var feature = new ZOO.Feature(geometry, attributes);
+ return feature;
+ },
+ /**
+ * Property: parseGeometry
+ * Properties of this object are the functions that parse geometries based
+ * on their type.
+ */
+ parseGeometry: {
+ /**
+ * Method: parseGeometry.point
+ * Given a GML node representing a point geometry, create a ZOO
+ * point geometry.
+ *
+ * Parameters:
+ * node - {E4XElement} A GML node.
+ *
+ * Returns:
+ * {} A point geometry.
+ */
+ 'point': function(node) {
+ /**
+ * Three coordinate variations to consider:
+ * 1) x y z
+ * 2) x, y, z
+ * 3) xy
+ */
+ var nodeList, coordString;
+ var coords = [];
+ // look for
+ var nodeList = node..*::pos;
+ if(nodeList.length() > 0) {
+ coordString = nodeList[0].toString();
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+ // look for
+ if(coords.length == 0) {
+ nodeList = node..*::coordinates;
+ if(nodeList.length() > 0) {
+ coordString = nodeList[0].toString();
+ coordString = coordString.replace(this.regExes.removeSpace,"");
+ coords = coordString.split(",");
+ }
+ }
+ // look for
+ if(coords.length == 0) {
+ nodeList = node..*::coord;
+ if(nodeList.length() > 0) {
+ var xList = nodeList[0].*::X;
+ var yList = nodeList[0].*::Y;
+ if(xList.length() > 0 && yList.length() > 0)
+ coords = [xList[0].toString(),
+ yList[0].toString()];
+ }
+ }
+ // preserve third dimension
+ if(coords.length == 2)
+ coords[2] = null;
+ if (this.xy)
+ return new ZOO.Geometry.Point(coords[0],coords[1],coords[2]);
+ else
+ return new ZOO.Geometry.Point(coords[1],coords[0],coords[2]);
+ },
+ /**
+ * Method: parseGeometry.multipoint
+ * Given a GML node representing a multipoint geometry, create a
+ * ZOO multipoint geometry.
+ *
+ * Parameters:
+ * node - {E4XElement} A GML node.
+ *
+ * Returns:
+ * {} A multipoint geometry.
+ */
+ 'multipoint': function(node) {
+ var nodeList = node..*::Point;
+ var components = [];
+ if(nodeList.length() > 0) {
+ var point;
+ for(var i=0, len=nodeList.length(); i} A linestring geometry.
+ */
+ 'linestring': function(node, ring) {
+ /**
+ * Two coordinate variations to consider:
+ * 1) x0 y0 z0 x1 y1 z1
+ * 2) x0, y0, z0 x1, y1, z1
+ */
+ var nodeList, coordString;
+ var coords = [];
+ var points = [];
+ // look for
+ nodeList = node..*::posList;
+ if(nodeList.length() > 0) {
+ coordString = nodeList[0].toString();
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ var dim = parseInt(nodeList[0].@dimension);
+ var j, x, y, z;
+ for(var i=0; i
+ if(coords.length == 0) {
+ nodeList = node..*::coordinates;
+ if(nodeList.length() > 0) {
+ coordString = nodeList[0].toString();
+ coordString = coordString.replace(this.regExes.trimSpace,"");
+ coordString = coordString.replace(this.regExes.trimComma,",");
+ var pointList = coordString.split(this.regExes.splitSpace);
+ for(var i=0; i} A multilinestring geometry.
+ */
+ 'multilinestring': function(node) {
+ var nodeList = node..*::LineString;
+ var components = [];
+ if(nodeList.length() > 0) {
+ var line;
+ for(var i=0, len=nodeList.length(); i} A polygon geometry.
+ */
+ 'polygon': function(node) {
+ nodeList = node..*::LinearRing;
+ var components = [];
+ if(nodeList.length() > 0) {
+ // this assumes exterior ring first, inner rings after
+ var ring;
+ for(var i=0, len = nodeList.length(); i} A multipolygon geometry.
+ */
+ 'multipolygon': function(node) {
+ var nodeList = node..*::Polygon;
+ var components = [];
+ if(nodeList.length() > 0) {
+ var polygon;
+ for(var i=0, len=nodeList.length(); i} A polygon geometry.
+ */
+ 'envelope': function(node) {
+ var components = [];
+ var coordString;
+ var envelope;
+ var lpoint = node..*::lowerCorner;
+ if (lpoint.length() > 0) {
+ var coords = [];
+ if(lpoint.length() > 0) {
+ coordString = lpoint[0].toString();
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+ if(coords.length == 2)
+ coords[2] = null;
+ if (this.xy)
+ var lowerPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+ else
+ var lowerPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ var upoint = node..*::upperCorner;
+ if (upoint.length() > 0) {
+ var coords = [];
+ if(upoint.length > 0) {
+ coordString = upoint[0].toString();
+ coordString = coordString.replace(this.regExes.trimSpace, "");
+ coords = coordString.split(this.regExes.splitSpace);
+ }
+ if(coords.length == 2)
+ coords[2] = null;
+ if (this.xy)
+ var upperPoint = new ZOO.Geometry.Point(coords[0], coords[1],coords[2]);
+ else
+ var upperPoint = new ZOO.Geometry.Point(coords[1], coords[0],coords[2]);
+ }
+ if (lowerPoint && upperPoint) {
+ components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+ components.push(new ZOO.Geometry.Point(upperPoint.x, lowerPoint.y));
+ components.push(new ZOO.Geometry.Point(upperPoint.x, upperPoint.y));
+ components.push(new ZOO.Geometry.Point(lowerPoint.x, upperPoint.y));
+ components.push(new ZOO.Geometry.Point(lowerPoint.x, lowerPoint.y));
+ var ring = new ZOO.Geometry.LinearRing(components);
+ envelope = new ZOO.Geometry.Polygon([ring]);
+ }
+ return envelope;
+ }
+ },
+ /**
+ * Method: parseAttributes
+ *
+ * Parameters:
+ * node - {}
+ *
+ * Returns:
+ * {Object} An attributes object.
+ */
+ parseAttributes: function(node) {
+ var attributes = {};
+ // assume attributes are children of the first type 1 child
+ var childNode = node.*::*[0];
+ var child, grandchildren;
+ var children = childNode.*::*;
+ for(var i=0, len=children.length(); i)} List of features to
+ * serialize into a string.
+ *
+ * Returns:
+ * {String} A string representing the GML document.
+ */
+ write: function(features) {
+ if(!(features instanceof Array)) {
+ features = [features];
+ }
+ var pfx = this.defaultPrefix;
+ var name = pfx+':'+this.collectionName;
+ var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" xmlns:gml="'+this.namespaces['gml']+'" xmlns:xsi="'+this.namespaces['xsi']+'" xsi:schemaLocation="'+this.schemaLocation+'">'+name+'>');
+ for(var i=0; i} The feature to be built as GML.
+ *
+ * Returns:
+ * {E4XElement} A node reprensting the feature in GML.
+ */
+ createFeature: function(feature) {
+ var pfx = this.defaultPrefix;
+ var name = pfx+':'+this.featureName;
+ var fid = feature.fid || feature.id;
+ var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'" fid="'+fid+'">'+name+'>');
+ var geometry = feature.geometry;
+ gml.*::*[0].*::* = this.buildGeometryNode(geometry);
+ for(var attr in feature.attributes) {
+ var attrNode = new XML('<'+pfx+':'+attr+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+feature.attributes[attr]+''+pfx+':'+attr+'>');
+ gml.*::*[0].appendChild(attrNode);
+ }
+ return gml;
+ },
+ /**
+ * Method: buildGeometryNode
+ *
+ * Parameters:
+ * geometry - {} The geometry to be built as GML.
+ *
+ * Returns:
+ * {E4XElement} A node reprensting the geometry in GML.
+ */
+ buildGeometryNode: function(geometry) {
+ if (this.externalProjection && this.internalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ var className = geometry.CLASS_NAME;
+ var type = className.substring(className.lastIndexOf(".") + 1);
+ var builder = this.buildGeometry[type.toLowerCase()];
+ var pfx = this.defaultPrefix;
+ var name = pfx+':'+this.geometryName;
+ var gml = new XML('<'+name+' xmlns:'+pfx+'="'+this.namespaces[pfx]+'">'+name+'>');
+ if (builder)
+ gml.*::* = builder.apply(this, [geometry]);
+ return gml;
+ },
+ /**
+ * Property: buildGeometry
+ * Object containing methods to do the actual geometry node building
+ * based on geometry type.
+ */
+ buildGeometry: {
+ /**
+ * Method: buildGeometry.point
+ * Given a ZOO point geometry, create a GML point.
+ *
+ * Parameters:
+ * geometry - {} A point geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML point node.
+ */
+ 'point': function(geometry) {
+ var gml = new XML('');
+ gml.*::*[0] = this.buildCoordinatesNode(geometry);
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.multipoint
+ * Given a ZOO multipoint geometry, create a GML multipoint.
+ *
+ * Parameters:
+ * geometry - {} A multipoint geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML multipoint node.
+ */
+ 'multipoint': function(geometry) {
+ var gml = new XML('');
+ var points = geometry.components;
+ var pointMember;
+ for(var i=0; i');
+ pointMember.*::* = this.buildGeometry.point.apply(this,[points[i]]);
+ gml.*::*[i] = pointMember;
+ }
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.linestring
+ * Given a ZOO linestring geometry, create a GML linestring.
+ *
+ * Parameters:
+ * geometry - {} A linestring geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML linestring node.
+ */
+ 'linestring': function(geometry) {
+ var gml = new XML('');
+ gml.*::*[0] = this.buildCoordinatesNode(geometry);
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.multilinestring
+ * Given a ZOO multilinestring geometry, create a GML
+ * multilinestring.
+ *
+ * Parameters:
+ * geometry - {} A multilinestring
+ * geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML multilinestring node.
+ */
+ 'multilinestring': function(geometry) {
+ var gml = new XML('');
+ var lines = geometry.components;
+ var lineMember;
+ for(var i=0; i');
+ lineMember.*::* = this.buildGeometry.linestring.apply(this,[lines[i]]);
+ gml.*::*[i] = lineMember;
+ }
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.linearring
+ * Given a ZOO linearring geometry, create a GML linearring.
+ *
+ * Parameters:
+ * geometry - {} A linearring geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML linearring node.
+ */
+ 'linearring': function(geometry) {
+ var gml = new XML('');
+ gml.*::*[0] = this.buildCoordinatesNode(geometry);
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.polygon
+ * Given an ZOO polygon geometry, create a GML polygon.
+ *
+ * Parameters:
+ * geometry - {} A polygon geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML polygon node.
+ */
+ 'polygon': function(geometry) {
+ var gml = new XML('');
+ var rings = geometry.components;
+ var ringMember, type;
+ for(var i=0; i');
+ ringMember.*::* = this.buildGeometry.linearring.apply(this,[rings[i]]);
+ gml.*::*[i] = ringMember;
+ }
+ return gml;
+ },
+ /**
+ * Method: buildGeometry.multipolygon
+ * Given a ZOO multipolygon geometry, create a GML multipolygon.
+ *
+ * Parameters:
+ * geometry - {} A multipolygon
+ * geometry.
+ *
+ * Returns:
+ * {E4XElement} A GML multipolygon node.
+ */
+ 'multipolygon': function(geometry) {
+ var gml = new XML('');
+ var polys = geometry.components;
+ var polyMember;
+ for(var i=0; i');
+ polyMember.*::* = this.buildGeometry.polygon.apply(this,[polys[i]]);
+ gml.*::*[i] = polyMember;
+ }
+ return gml;
+ }
+ },
+ /**
+ * Method: buildCoordinatesNode
+ * builds the coordinates XmlNode
+ * (code)
+ * ...
+ * (end)
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {E4XElement} created E4XElement
+ */
+ buildCoordinatesNode: function(geometry) {
+ var parts = [];
+ if(geometry instanceof ZOO.Bounds){
+ parts.push(geometry.left + "," + geometry.bottom);
+ parts.push(geometry.right + "," + geometry.top);
+ } else {
+ var points = (geometry.components) ? geometry.components : [geometry];
+ for(var i=0; i'+parts.join(" ")+'');
+ },
+ CLASS_NAME: 'ZOO.Format.GML'
+});
+/**
+ * Class: ZOO.Format.WPS
+ * Read/Write WPS. Create a new instance with the
+ * constructor. Supports only parseExecuteResponse.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.WPS = ZOO.Class(ZOO.Format, {
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ows: "http://www.opengis.net/ows/1.1",
+ wps: "http://www.opengis.net/wps/1.0.0",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance",
+ },
+ /**
+ * Method: read
+ *
+ * Parameters:
+ * data - {String} A WPS xml document
+ *
+ * Returns:
+ * {Object} Execute response.
+ */
+ read:function(data) {
+ data = data.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, "");
+ data = new XML(data);
+ switch (data.localName()) {
+ case 'ExecuteResponse':
+ return this.parseExecuteResponse(data);
+ default:
+ return null;
+ }
+ },
+ /**
+ * Method: parseExecuteResponse
+ *
+ * Parameters:
+ * node - {E4XElement} A WPS ExecuteResponse document
+ *
+ * Returns:
+ * {Object} Execute response.
+ */
+ parseExecuteResponse: function(node) {
+ var outputs = node.*::ProcessOutputs.*::Output;
+ if (outputs.length() > 0) {
+ var data = outputs[0].*::Data.*::*[0];
+ var builder = this.parseData[data.localName().toLowerCase()];
+ if (builder)
+ return builder.apply(this,[data]);
+ else
+ return null;
+ } else
+ return null;
+ },
+ /**
+ * Property: parseData
+ * Object containing methods to analyse data response.
+ */
+ parseData: {
+ /**
+ * Method: parseData.complexdata
+ * Given an Object representing the WPS complex data response.
+ *
+ * Parameters:
+ * node - {E4XElement} A WPS node.
+ *
+ * Returns:
+ * {Object} A WPS complex data response.
+ */
+ 'complexdata': function(node) {
+ var result = {value:node.toString()};
+ if (node.@mimeType.length()>0)
+ result.mimeType = node.@mimeType;
+ if (node.@encoding.length()>0)
+ result.encoding = node.@encoding;
+ if (node.@schema.length()>0)
+ result.schema = node.@schema;
+ return result;
+ },
+ /**
+ * Method: parseData.literaldata
+ * Given an Object representing the WPS literal data response.
+ *
+ * Parameters:
+ * node - {E4XElement} A WPS node.
+ *
+ * Returns:
+ * {Object} A WPS literal data response.
+ */
+ 'literaldata': function(node) {
+ var result = {value:node.toString()};
+ if (node.@dataType.length()>0)
+ result.dataType = node.@dataType;
+ if (node.@uom.length()>0)
+ result.uom = node.@uom;
+ return result;
+ },
+ /**
+ * Method: parseData.reference
+ * Given an Object representing the WPS reference response.
+ *
+ * Parameters:
+ * node - {E4XElement} A WPS node.
+ *
+ * Returns:
+ * {Object} A WPS reference response.
+ */
+ 'reference': function(node) {
+ var result = {type:'reference',value:node.*::href};
+ return result;
+ }
+ },
+ CLASS_NAME: 'ZOO.Format.WPS'
+});
+
+/**
+ * Class: ZOO.Feature
+ * Vector features use the ZOO.Geometry classes as geometry description.
+ * They have an 'attributes' property, which is the data object
+ */
+ZOO.Feature = ZOO.Class({
+ /**
+ * Property: fid
+ * {String}
+ */
+ fid: null,
+ /**
+ * Property: geometry
+ * {}
+ */
+ geometry: null,
+ /**
+ * Property: attributes
+ * {Object} This object holds arbitrary properties that describe the
+ * feature.
+ */
+ attributes: null,
+ /**
+ * Property: bounds
+ * {} The box bounding that feature's geometry, that
+ * property can be set by an object when
+ * deserializing the feature, so in most cases it represents an
+ * information set by the server.
+ */
+ bounds: null,
+ /**
+ * Constructor: ZOO.Feature
+ * Create a vector feature.
+ *
+ * Parameters:
+ * geometry - {} The geometry that this feature
+ * represents.
+ * attributes - {Object} An optional object that will be mapped to the
+ * property.
+ */
+ initialize: function(geometry, attributes) {
+ this.geometry = geometry ? geometry : null;
+ this.attributes = {};
+ if (attributes)
+ this.attributes = ZOO.extend(this.attributes,attributes);
+ },
+ /**
+ * Method: destroy
+ * nullify references to prevent circular references and memory leaks
+ */
+ destroy: function() {
+ this.geometry = null;
+ },
+ /**
+ * Method: clone
+ * Create a clone of this vector feature. Does not set any non-standard
+ * properties.
+ *
+ * Returns:
+ * {} An exact clone of this vector feature.
+ */
+ clone: function () {
+ return new ZOO.Feature(this.geometry ? this.geometry.clone() : null,
+ this.attributes);
+ },
+ /**
+ * Method: move
+ * Moves the feature and redraws it at its new location
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ */
+ move: function(x, y) {
+ if(!this.geometry.move)
+ return;
+
+ this.geometry.move(x,y);
+ return this.geometry;
+ },
+ CLASS_NAME: 'ZOO.Feature'
+});
+
+/**
+ * Class: ZOO.Geometry
+ * A Geometry is a description of a geographic object. Create an instance
+ * of this class with the constructor. This is a base class,
+ * typical geometry types are described by subclasses of this class.
+ */
+ZOO.Geometry = ZOO.Class({
+ /**
+ * Property: id
+ * {String} A unique identifier for this geometry.
+ */
+ id: null,
+ /**
+ * Property: parent
+ * {}This is set when a Geometry is added as component
+ * of another geometry
+ */
+ parent: null,
+ /**
+ * Property: bounds
+ * {} The bounds of this geometry
+ */
+ bounds: null,
+ /**
+ * Constructor: ZOO.Geometry
+ * Creates a geometry object.
+ */
+ initialize: function() {
+ //generate unique id
+ },
+ /**
+ * Method: destroy
+ * Destroy this geometry.
+ */
+ destroy: function() {
+ this.id = null;
+ this.bounds = null;
+ },
+ /**
+ * Method: clone
+ * Create a clone of this geometry. Does not set any non-standard
+ * properties of the cloned geometry.
+ *
+ * Returns:
+ * {} An exact clone of this geometry.
+ */
+ clone: function() {
+ return new ZOO.Geometry();
+ },
+ /**
+ * Method: extendBounds
+ * Extend the existing bounds to include the new bounds.
+ * If geometry's bounds is not yet set, then set a new Bounds.
+ *
+ * Parameters:
+ * newBounds - {}
+ */
+ extendBounds: function(newBounds){
+ var bounds = this.getBounds();
+ if (!bounds)
+ this.setBounds(newBounds);
+ else
+ this.bounds.extend(newBounds);
+ },
+ /**
+ * Set the bounds for this Geometry.
+ *
+ * Parameters:
+ * bounds - {}
+ */
+ setBounds: function(bounds) {
+ if (bounds)
+ this.bounds = bounds.clone();
+ },
+ /**
+ * Method: clearBounds
+ * Nullify this components bounds and that of its parent as well.
+ */
+ clearBounds: function() {
+ this.bounds = null;
+ if (this.parent)
+ this.parent.clearBounds();
+ },
+ /**
+ * Method: getBounds
+ * Get the bounds for this Geometry. If bounds is not set, it
+ * is calculated again, this makes queries faster.
+ *
+ * Returns:
+ * {}
+ */
+ getBounds: function() {
+ if (this.bounds == null) {
+ this.calculateBounds();
+ }
+ return this.bounds;
+ },
+ /**
+ * Method: calculateBounds
+ * Recalculate the bounds for the geometry.
+ */
+ calculateBounds: function() {
+ // This should be overridden by subclasses.
+ return this.bounds;
+ },
+ distanceTo: function(geometry, options) {
+ },
+ getVertices: function(nodes) {
+ },
+ getLength: function() {
+ return 0.0;
+ },
+ getArea: function() {
+ return 0.0;
+ },
+ getCentroid: function() {
+ return null;
+ },
+ /**
+ * Method: toString
+ * Returns the Well-Known Text representation of a geometry
+ *
+ * Returns:
+ * {String} Well-Known Text
+ */
+ toString: function() {
+ return ZOO.Format.WKT.prototype.write(
+ new ZOO.Feature(this)
+ );
+ },
+ CLASS_NAME: 'ZOO.Geometry'
+});
+/**
+ * Function: ZOO.Geometry.fromWKT
+ * Generate a geometry given a Well-Known Text string.
+ *
+ * Parameters:
+ * wkt - {String} A string representing the geometry in Well-Known Text.
+ *
+ * Returns:
+ * {} A geometry of the appropriate class.
+ */
+ZOO.Geometry.fromWKT = function(wkt) {
+ var format = arguments.callee.format;
+ if(!format) {
+ format = new ZOO.Format.WKT();
+ arguments.callee.format = format;
+ }
+ var geom;
+ var result = format.read(wkt);
+ if(result instanceof ZOO.Feature) {
+ geom = result.geometry;
+ } else if(result instanceof Array) {
+ var len = result.length;
+ var components = new Array(len);
+ for(var i=0; i= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
+ // intersect
+ if(!point) {
+ intersection = true;
+ } else {
+ // calculate the intersection point
+ var x = seg1.x1 + (along1 * x12_11);
+ var y = seg1.y1 + (along1 * y12_11);
+ intersection = new ZOO.Geometry.Point(x, y);
+ }
+ }
+ }
+ if(tolerance) {
+ var dist;
+ if(intersection) {
+ if(point) {
+ var segs = [seg1, seg2];
+ var seg, x, y;
+ // check segment endpoints for proximity to intersection
+ // set intersection to first endpoint within the tolerance
+ outer: for(var i=0; i<2; ++i) {
+ seg = segs[i];
+ for(var j=1; j<3; ++j) {
+ x = seg["x" + j];
+ y = seg["y" + j];
+ dist = Math.sqrt(
+ Math.pow(x - intersection.x, 2) +
+ Math.pow(y - intersection.y, 2)
+ );
+ if(dist < tolerance) {
+ intersection.x = x;
+ intersection.y = y;
+ break outer;
+ }
+ }
+ }
+ }
+ } else {
+ // no calculated intersection, but segments could be within
+ // the tolerance of one another
+ var segs = [seg1, seg2];
+ var source, target, x, y, p, result;
+ // check segment endpoints for proximity to intersection
+ // set intersection to first endpoint within the tolerance
+ outer: for(var i=0; i<2; ++i) {
+ source = segs[i];
+ target = segs[(i+1)%2];
+ for(var j=1; j<3; ++j) {
+ p = {x: source["x"+j], y: source["y"+j]};
+ result = ZOO.Geometry.distanceToSegment(p, target);
+ if(result.distance < tolerance) {
+ if(point) {
+ intersection = new ZOO.Geometry.Point(p.x, p.y);
+ } else {
+ intersection = true;
+ }
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ return intersection;
+};
+ZOO.Geometry.distanceToSegment = function(point, segment) {
+ var x0 = point.x;
+ var y0 = point.y;
+ var x1 = segment.x1;
+ var y1 = segment.y1;
+ var x2 = segment.x2;
+ var y2 = segment.y2;
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
+ (Math.pow(dx, 2) + Math.pow(dy, 2));
+ var x, y;
+ if(along <= 0.0) {
+ x = x1;
+ y = y1;
+ } else if(along >= 1.0) {
+ x = x2;
+ y = y2;
+ } else {
+ x = x1 + along * dx;
+ y = y1 + along * dy;
+ }
+ return {
+ distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
+ x: x, y: y
+ };
+};
+/**
+ * Class: ZOO.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different
+ * Geometries. These are stored in the local parameter (which
+ * can be passed as a parameter to the constructor).
+ *
+ * As new geometries are added to the collection, they are NOT cloned.
+ * When removing geometries, they need to be specified by reference (ie you
+ * have to pass in the *exact* geometry to be removed).
+ *
+ * The and functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the constructor.
+ *
+ * Inerhits from:
+ * -
+ */
+ZOO.Geometry.Collection = ZOO.Class(ZOO.Geometry, {
+ /**
+ * Property: components
+ * {Array()} The component parts of this geometry
+ */
+ components: null,
+ /**
+ * Property: componentTypes
+ * {Array(String)} An array of class names representing the types of
+ * components that the collection can include. A null value means the
+ * component types are not restricted.
+ */
+ componentTypes: null,
+ /**
+ * Constructor: ZOO.Geometry.Collection
+ * Creates a Geometry Collection -- a list of geoms.
+ *
+ * Parameters:
+ * components - {Array()} Optional array of geometries
+ *
+ */
+ initialize: function (components) {
+ ZOO.Geometry.prototype.initialize.apply(this, arguments);
+ this.components = [];
+ if (components != null) {
+ this.addComponents(components);
+ }
+ },
+ /**
+ * Method: destroy
+ * Destroy this geometry.
+ */
+ destroy: function () {
+ this.components.length = 0;
+ this.components = null;
+ },
+ /**
+ * Method: clone
+ * Clone this geometry.
+ *
+ * Returns:
+ * {} An exact clone of this collection
+ */
+ clone: function() {
+ var geometry = eval("new " + this.CLASS_NAME + "()");
+ for(var i=0, len=this.components.length; i 0) {
+ this.setBounds(this.components[0].getBounds());
+ for (var i=1, len=this.components.length; i)} An array of geometries to add
+ */
+ addComponents: function(components){
+ if(!(components instanceof Array))
+ components = [components];
+ for(var i=0, len=components.length; i} A geometry to add
+ * index - {int} Optional index into the array to insert the component
+ *
+ * Returns:
+ * {Boolean} The component geometry was successfully added
+ */
+ addComponent: function(component, index) {
+ var added = false;
+ if(component) {
+ if(this.componentTypes == null ||
+ (ZOO.indexOf(this.componentTypes,
+ component.CLASS_NAME) > -1)) {
+ if(index != null && (index < this.components.length)) {
+ var components1 = this.components.slice(0, index);
+ var components2 = this.components.slice(index,
+ this.components.length);
+ components1.push(component);
+ this.components = components1.concat(components2);
+ } else {
+ this.components.push(component);
+ }
+ component.parent = this;
+ this.clearBounds();
+ added = true;
+ }
+ }
+ return added;
+ },
+ /**
+ * Method: removeComponents
+ * Remove components from this geometry.
+ *
+ * Parameters:
+ * components - {Array()} The components to be removed
+ */
+ removeComponents: function(components) {
+ if(!(components instanceof Array))
+ components = [components];
+ for(var i=components.length-1; i>=0; --i) {
+ this.removeComponent(components[i]);
+ }
+ },
+ /**
+ * Method: removeComponent
+ * Remove a component from this geometry.
+ *
+ * Parameters:
+ * component - {}
+ */
+ removeComponent: function(component) {
+ ZOO.removeItem(this.components, component);
+ // clearBounds() so that it gets recalculated on the next call
+ // to this.getBounds();
+ this.clearBounds();
+ },
+ /**
+ * Method: getLength
+ * Calculate the length of this geometry
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = 0.0;
+ for (var i=0, len=this.components.length; i.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ var area = 0.0;
+ for (var i=0, len=this.components.length; i} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Reference:
+ * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+ * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+ *
+ * Returns:
+ * {float} The approximate geodesic area of the geometry in square meters.
+ */
+ getGeodesicArea: function(projection) {
+ var area = 0.0;
+ for(var i=0, len=this.components.length; i} The centroid of the collection
+ */
+ getCentroid: function() {
+ return this.components.length && this.components[0].getCentroid();
+ },
+ /**
+ * Method: getGeodesicLength
+ * Calculate the approximate length of the geometry were it projected onto
+ * the earth.
+ *
+ * Parameters:
+ * projection - {} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Returns:
+ * {Float} The appoximate geodesic length of the geometry in meters.
+ */
+ getGeodesicLength: function(projection) {
+ var length = 0.0;
+ for(var i=0, len=this.components.length; i} Center point for the rotation
+ */
+ rotate: function(angle, origin) {
+ for(var i=0, len=this.components.length; i} Point of origin for resizing
+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
+ *
+ * Returns:
+ * {ZOO.Geometry} - The current geometry.
+ */
+ resize: function(scale, origin, ratio) {
+ for(var i=0; i} The geometry to test.
+ *
+ * Returns:
+ * {Boolean} The supplied geometry is equivalent to this geometry.
+ */
+ equals: function(geometry) {
+ var equivalent = true;
+ if(!geometry || !geometry.CLASS_NAME ||
+ (this.CLASS_NAME != geometry.CLASS_NAME))
+ equivalent = false;
+ else if(!(geometry.components instanceof Array) ||
+ (geometry.components.length != this.components.length))
+ equivalent = false;
+ else
+ for(var i=0, len=this.components.length; i}
+ * dest - {}
+ *
+ * Returns:
+ * {}
+ */
+ transform: function(source, dest) {
+ if (source && dest) {
+ for (var i=0, len=this.components.length; i} Any type of geometry.
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this one.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ for(var i=0, len=this.components.length; i
+ */
+ZOO.Geometry.Point = ZOO.Class(ZOO.Geometry, {
+ /**
+ * Property: x
+ * {float}
+ */
+ x: null,
+ /**
+ * Property: y
+ * {float}
+ */
+ y: null,
+ /**
+ * Constructor: ZOO.Geometry.Point
+ * Construct a point geometry.
+ *
+ * Parameters:
+ * x - {float}
+ * y - {float}
+ *
+ */
+ initialize: function(x, y) {
+ ZOO.Geometry.prototype.initialize.apply(this, arguments);
+ this.x = parseFloat(x);
+ this.y = parseFloat(y);
+ },
+ /**
+ * Method: clone
+ *
+ * Returns:
+ * {} An exact clone of this ZOO.Geometry.Point
+ */
+ clone: function(obj) {
+ if (obj == null)
+ obj = new ZOO.Geometry.Point(this.x, this.y);
+ // catch any randomly tagged-on properties
+ // ZOO.Util.applyDefaults(obj, this);
+ return obj;
+ },
+ /**
+ * Method: calculateBounds
+ * Create a new Bounds based on the x/y
+ */
+ calculateBounds: function () {
+ this.bounds = new ZOO.Bounds(this.x, this.y,
+ this.x, this.y);
+ },
+ distanceTo: function(geometry, options) {
+ var edge = !(options && options.edge === false);
+ var details = edge && options && options.details;
+ var distance, x0, y0, x1, y1, result;
+ if(geometry instanceof ZOO.Geometry.Point) {
+ x0 = this.x;
+ y0 = this.y;
+ x1 = geometry.x;
+ y1 = geometry.y;
+ distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
+ result = !details ?
+ distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
+ } else {
+ result = geometry.distanceTo(this, options);
+ if(details) {
+ // switch coord order since this geom is target
+ result = {
+ x0: result.x1, y0: result.y1,
+ x1: result.x0, y1: result.y0,
+ distance: result.distance
+ };
+ }
+ }
+ return result;
+ },
+ /**
+ * Method: equals
+ * Determine whether another geometry is equivalent to this one. Geometries
+ * are considered equivalent if all components have the same coordinates.
+ *
+ * Parameters:
+ * geom - {} The geometry to test.
+ *
+ * Returns:
+ * {Boolean} The supplied geometry is equivalent to this geometry.
+ */
+ equals: function(geom) {
+ var equals = false;
+ if (geom != null)
+ equals = ((this.x == geom.x && this.y == geom.y) ||
+ (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
+ return equals;
+ },
+ /**
+ * Method: toShortString
+ *
+ * Returns:
+ * {String} Shortened String representation of Point object.
+ * (ex. "5, 42")
+ */
+ toShortString: function() {
+ return (this.x + ", " + this.y);
+ },
+ /**
+ * Method: move
+ * Moves a geometry by the given displacement along positive x and y axes.
+ * This modifies the position of the geometry and clears the cached
+ * bounds.
+ *
+ * Parameters:
+ * x - {Float} Distance to move geometry in positive x direction.
+ * y - {Float} Distance to move geometry in positive y direction.
+ */
+ move: function(x, y) {
+ this.x = this.x + x;
+ this.y = this.y + y;
+ this.clearBounds();
+ },
+ /**
+ * Method: rotate
+ * Rotate a point around another.
+ *
+ * Parameters:
+ * angle - {Float} Rotation angle in degrees (measured counterclockwise
+ * from the positive x-axis)
+ * origin - {} Center point for the rotation
+ */
+ rotate: function(angle, origin) {
+ angle *= Math.PI / 180;
+ var radius = this.distanceTo(origin);
+ var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
+ this.x = origin.x + (radius * Math.cos(theta));
+ this.y = origin.y + (radius * Math.sin(theta));
+ this.clearBounds();
+ },
+ /**
+ * Method: getCentroid
+ *
+ * Returns:
+ * {} The centroid of the collection
+ */
+ getCentroid: function() {
+ return new ZOO.Geometry.Point(this.x, this.y);
+ },
+ /**
+ * Method: resize
+ * Resize a point relative to some origin. For points, this has the effect
+ * of scaling a vector (from the origin to the point). This method is
+ * more useful on geometry collection subclasses.
+ *
+ * Parameters:
+ * scale - {Float} Ratio of the new distance from the origin to the old
+ * distance from the origin. A scale of 2 doubles the
+ * distance between the point and origin.
+ * origin - {} Point of origin for resizing
+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
+ *
+ * Returns:
+ * {ZOO.Geometry} - The current geometry.
+ */
+ resize: function(scale, origin, ratio) {
+ ratio = (ratio == undefined) ? 1 : ratio;
+ this.x = origin.x + (scale * ratio * (this.x - origin.x));
+ this.y = origin.y + (scale * (this.y - origin.y));
+ this.clearBounds();
+ return this;
+ },
+ /**
+ * Method: intersects
+ * Determine if the input geometry intersects this one.
+ *
+ * Parameters:
+ * geometry - {} Any type of geometry.
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this one.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ if(geometry.CLASS_NAME == "ZOO.Geometry.Point") {
+ intersect = this.equals(geometry);
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+ /**
+ * Method: transform
+ * Translate the x,y properties of the point from source to dest.
+ *
+ * Parameters:
+ * source - {}
+ * dest - {}
+ *
+ * Returns:
+ * {}
+ */
+ transform: function(source, dest) {
+ if ((source && dest)) {
+ ZOO.Projection.transform(
+ this, source, dest);
+ this.bounds = null;
+ }
+ return this;
+ },
+ /**
+ * Method: getVertices
+ * Return a list of all points in this geometry.
+ *
+ * Parameters:
+ * nodes - {Boolean} For lines, only return vertices that are
+ * endpoints. If false, for lines, only vertices that are not
+ * endpoints will be returned. If not provided, all vertices will
+ * be returned.
+ *
+ * Returns:
+ * {Array} A list of all vertices in the geometry.
+ */
+ getVertices: function(nodes) {
+ return [this];
+ },
+ CLASS_NAME: 'ZOO.Geometry.Point'
+});
+/**
+ * Class: ZOO.Geometry.Surface
+ * Surface geometry class.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Geometry.Surface = ZOO.Class(ZOO.Geometry, {
+ initialize: function() {
+ ZOO.Geometry.prototype.initialize.apply(this, arguments);
+ },
+ CLASS_NAME: "ZOO.Geometry.Surface"
+});
+/**
+ * Class: ZOO.Geometry.MultiPoint
+ * MultiPoint is a collection of Points. Create a new instance with the
+ * constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Geometry.MultiPoint = ZOO.Class(
+ ZOO.Geometry.Collection, {
+ /**
+ * Property: componentTypes
+ * {Array(String)} An array of class names representing the types of
+ * components that the collection can include. A null value means the
+ * component types are not restricted.
+ */
+ componentTypes: ["ZOO.Geometry.Point"],
+ /**
+ * Constructor: ZOO.Geometry.MultiPoint
+ * Create a new MultiPoint Geometry
+ *
+ * Parameters:
+ * components - {Array()}
+ *
+ * Returns:
+ * {}
+ */
+ initialize: function(components) {
+ ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+ },
+ /**
+ * Method: addPoint
+ * Wrapper for
+ *
+ * Parameters:
+ * point - {} Point to be added
+ * index - {Integer} Optional index
+ */
+ addPoint: function(point, index) {
+ this.addComponent(point, index);
+ },
+ /**
+ * Method: removePoint
+ * Wrapper for
+ *
+ * Parameters:
+ * point - {} Point to be removed
+ */
+ removePoint: function(point){
+ this.removeComponent(point);
+ },
+ CLASS_NAME: "ZOO.Geometry.MultiPoint"
+});
+/**
+ * Class: ZOO.Geometry.Curve
+ * A Curve is a MultiPoint, whose points are assumed to be connected. To
+ * this end, we provide a "getLength()" function, which iterates through
+ * the points, summing the distances between them.
+ *
+ * Inherits:
+ * -
+ */
+ZOO.Geometry.Curve = ZOO.Class(ZOO.Geometry.MultiPoint, {
+ /**
+ * Property: componentTypes
+ * {Array(String)} An array of class names representing the types of
+ * components that the collection can include. A null
+ * value means the component types are not restricted.
+ */
+ componentTypes: ["ZOO.Geometry.Point"],
+ /**
+ * Constructor: ZOO.Geometry.Curve
+ *
+ * Parameters:
+ * point - {Array()}
+ */
+ initialize: function(points) {
+ ZOO.Geometry.MultiPoint.prototype.initialize.apply(this,arguments);
+ },
+ /**
+ * Method: getLength
+ *
+ * Returns:
+ * {Float} The length of the curve
+ */
+ getLength: function() {
+ var length = 0.0;
+ if ( this.components && (this.components.length > 1)) {
+ for(var i=1, len=this.components.length; i} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Returns:
+ * {Float} The appoximate geodesic length of the geometry in meters.
+ */
+ getGeodesicLength: function(projection) {
+ var geom = this; // so we can work with a clone if needed
+ if(projection) {
+ var gg = new ZOO.Projection("EPSG:4326");
+ if(!gg.equals(projection)) {
+ geom = this.clone().transform(projection, gg);
+ }
+ }
+ var length = 0.0;
+ if(geom.components && (geom.components.length > 1)) {
+ var p1, p2;
+ for(var i=1, len=geom.components.length; i
+ */
+ZOO.Geometry.LineString = ZOO.Class(ZOO.Geometry.Curve, {
+ /**
+ * Constructor: ZOO.Geometry.LineString
+ * Create a new LineString geometry
+ *
+ * Parameters:
+ * points - {Array()} An array of points used to
+ * generate the linestring
+ *
+ */
+ initialize: function(points) {
+ ZOO.Geometry.Curve.prototype.initialize.apply(this, arguments);
+ },
+ /**
+ * Method: removeComponent
+ * Only allows removal of a point if there are three or more points in
+ * the linestring. (otherwise the result would be just a single point)
+ *
+ * Parameters:
+ * point - {} The point to be removed
+ */
+ removeComponent: function(point) {
+ if ( this.components && (this.components.length > 2))
+ ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+ },
+ /**
+ * Method: intersects
+ * Test for instersection between two geometries. This is a cheapo
+ * implementation of the Bently-Ottmann algorigithm. It doesn't
+ * really keep track of a sweep line data structure. It is closer
+ * to the brute force method, except that segments are sorted and
+ * potential intersections are only calculated when bounding boxes
+ * intersect.
+ *
+ * Parameters:
+ * geometry - {}
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this geometry.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ var type = geometry.CLASS_NAME;
+ if(type == "ZOO.Geometry.LineString" ||
+ type == "ZOO.Geometry.LinearRing" ||
+ type == "ZOO.Geometry.Point") {
+ var segs1 = this.getSortedSegments();
+ var segs2;
+ if(type == "ZOO.Geometry.Point")
+ segs2 = [{
+ x1: geometry.x, y1: geometry.y,
+ x2: geometry.x, y2: geometry.y
+ }];
+ else
+ segs2 = geometry.getSortedSegments();
+ var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
+ seg2, seg2y1, seg2y2;
+ // sweep right
+ outer: for(var i=0, len=segs1.length; i seg1x2)
+ break;
+ if(seg2.x2 < seg1x1)
+ continue;
+ seg2y1 = seg2.y1;
+ seg2y2 = seg2.y2;
+ if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2))
+ continue;
+ if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2))
+ continue;
+ if(ZOO.Geometry.segmentsIntersect(seg1, seg2)) {
+ intersect = true;
+ break outer;
+ }
+ }
+ }
+ } else {
+ intersect = geometry.intersects(this);
+ }
+ return intersect;
+ },
+ /**
+ * Method: getSortedSegments
+ *
+ * Returns:
+ * {Array} An array of segment objects. Segment objects have properties
+ * x1, y1, x2, and y2. The start point is represented by x1 and y1.
+ * The end point is represented by x2 and y2. Start and end are
+ * ordered so that x1 < x2.
+ */
+ getSortedSegments: function() {
+ var numSeg = this.components.length - 1;
+ var segments = new Array(numSeg);
+ for(var i=0; i 0) {
+ // sort intersections along segment
+ var xDir = seg.x1 < seg.x2 ? 1 : -1;
+ var yDir = seg.y1 < seg.y2 ? 1 : -1;
+ result = {
+ lines: lines,
+ points: intersections.sort(function(p1, p2) {
+ return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
+ })
+ };
+ }
+ return result;
+ },
+ /**
+ * Method: split
+ * Use this geometry (the source) to attempt to split a target geometry.
+ *
+ * Parameters:
+ * target - {} The target geometry.
+ * options - {Object} Properties of this object will be used to determine
+ * how the split is conducted.
+ *
+ * Valid options:
+ * mutual - {Boolean} Split the source geometry in addition to the target
+ * geometry. Default is false.
+ * edge - {Boolean} Allow splitting when only edges intersect. Default is
+ * true. If false, a vertex on the source must be within the tolerance
+ * distance of the intersection to be considered a split.
+ * tolerance - {Number} If a non-null value is provided, intersections
+ * within the tolerance distance of an existing vertex on the source
+ * will be assumed to occur at the vertex.
+ *
+ * Returns:
+ * {Array} A list of geometries (of this same type as the target) that
+ * result from splitting the target with the source geometry. The
+ * source and target geometry will remain unmodified. If no split
+ * results, null will be returned. If mutual is true and a split
+ * results, return will be an array of two arrays - the first will be
+ * all geometries that result from splitting the source geometry and
+ * the second will be all geometries that result from splitting the
+ * target geometry.
+ */
+ split: function(target, options) {
+ var results = null;
+ var mutual = options && options.mutual;
+ var sourceSplit, targetSplit, sourceParts, targetParts;
+ if(target instanceof ZOO.Geometry.LineString) {
+ var verts = this.getVertices();
+ var vert1, vert2, seg, splits, lines, point;
+ var points = [];
+ sourceParts = [];
+ for(var i=0, stop=verts.length-2; i<=stop; ++i) {
+ vert1 = verts[i];
+ vert2 = verts[i+1];
+ seg = {
+ x1: vert1.x, y1: vert1.y,
+ x2: vert2.x, y2: vert2.y
+ };
+ targetParts = targetParts || [target];
+ if(mutual)
+ points.push(vert1.clone());
+ for(var j=0; j 0) {
+ lines.unshift(j, 1);
+ Array.prototype.splice.apply(targetParts, lines);
+ j += lines.length - 2;
+ }
+ if(mutual) {
+ for(var k=0, len=splits.points.length; k 0 && points.length > 0) {
+ points.push(vert2.clone());
+ sourceParts.push(new ZOO.Geometry.LineString(points));
+ }
+ } else {
+ results = target.splitWith(this, options);
+ }
+ if(targetParts && targetParts.length > 1)
+ targetSplit = true;
+ else
+ targetParts = [];
+ if(sourceParts && sourceParts.length > 1)
+ sourceSplit = true;
+ else
+ sourceParts = [];
+ if(targetSplit || sourceSplit) {
+ if(mutual)
+ results = [sourceParts, targetParts];
+ else
+ results = targetParts;
+ }
+ return results;
+ },
+ /**
+ * Method: splitWith
+ * Split this geometry (the target) with the given geometry (the source).
+ *
+ * Parameters:
+ * geometry - {} A geometry used to split this
+ * geometry (the source).
+ * options - {Object} Properties of this object will be used to determine
+ * how the split is conducted.
+ *
+ * Valid options:
+ * mutual - {Boolean} Split the source geometry in addition to the target
+ * geometry. Default is false.
+ * edge - {Boolean} Allow splitting when only edges intersect. Default is
+ * true. If false, a vertex on the source must be within the tolerance
+ * distance of the intersection to be considered a split.
+ * tolerance - {Number} If a non-null value is provided, intersections
+ * within the tolerance distance of an existing vertex on the source
+ * will be assumed to occur at the vertex.
+ *
+ * Returns:
+ * {Array} A list of geometries (of this same type as the target) that
+ * result from splitting the target with the source geometry. The
+ * source and target geometry will remain unmodified. If no split
+ * results, null will be returned. If mutual is true and a split
+ * results, return will be an array of two arrays - the first will be
+ * all geometries that result from splitting the source geometry and
+ * the second will be all geometries that result from splitting the
+ * target geometry.
+ */
+ splitWith: function(geometry, options) {
+ return geometry.split(this, options);
+ },
+ /**
+ * Method: getVertices
+ * Return a list of all points in this geometry.
+ *
+ * Parameters:
+ * nodes - {Boolean} For lines, only return vertices that are
+ * endpoints. If false, for lines, only vertices that are not
+ * endpoints will be returned. If not provided, all vertices will
+ * be returned.
+ *
+ * Returns:
+ * {Array} A list of all vertices in the geometry.
+ */
+ getVertices: function(nodes) {
+ var vertices;
+ if(nodes === true)
+ vertices = [
+ this.components[0],
+ this.components[this.components.length-1]
+ ];
+ else if (nodes === false)
+ vertices = this.components.slice(1, this.components.length-1);
+ else
+ vertices = this.components.slice();
+ return vertices;
+ },
+ distanceTo: function(geometry, options) {
+ var edge = !(options && options.edge === false);
+ var details = edge && options && options.details;
+ var result, best = {};
+ var min = Number.POSITIVE_INFINITY;
+ if(geometry instanceof ZOO.Geometry.Point) {
+ var segs = this.getSortedSegments();
+ var x = geometry.x;
+ var y = geometry.y;
+ var seg;
+ for(var i=0, len=segs.length; i x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2)))
+ break;
+ }
+ }
+ if(details)
+ best = {
+ distance: best.distance,
+ x0: best.x, y0: best.y,
+ x1: x, y1: y
+ };
+ else
+ best = best.distance;
+ } else if(geometry instanceof ZOO.Geometry.LineString) {
+ var segs0 = this.getSortedSegments();
+ var segs1 = geometry.getSortedSegments();
+ var seg0, seg1, intersection, x0, y0;
+ var len1 = segs1.length;
+ var interOptions = {point: true};
+ outer: for(var i=0, len=segs0.length; i
+ */
+ZOO.Geometry.LinearRing = ZOO.Class(
+ ZOO.Geometry.LineString, {
+ /**
+ * Property: componentTypes
+ * {Array(String)} An array of class names representing the types of
+ * components that the collection can include. A null
+ * value means the component types are not restricted.
+ */
+ componentTypes: ["ZOO.Geometry.Point"],
+ /**
+ * Constructor: ZOO.Geometry.LinearRing
+ * Linear rings are constructed with an array of points. This array
+ * can represent a closed or open ring. If the ring is open (the last
+ * point does not equal the first point), the constructor will close
+ * the ring. If the ring is already closed (the last point does equal
+ * the first point), it will be left closed.
+ *
+ * Parameters:
+ * points - {Array()} points
+ */
+ initialize: function(points) {
+ ZOO.Geometry.LineString.prototype.initialize.apply(this,arguments);
+ },
+ /**
+ * Method: addComponent
+ * Adds a point to geometry components. If the point is to be added to
+ * the end of the components array and it is the same as the last point
+ * already in that array, the duplicate point is not added. This has
+ * the effect of closing the ring if it is not already closed, and
+ * doing the right thing if it is already closed. This behavior can
+ * be overridden by calling the method with a non-null index as the
+ * second argument.
+ *
+ * Parameter:
+ * point - {}
+ * index - {Integer} Index into the array to insert the component
+ *
+ * Returns:
+ * {Boolean} Was the Point successfully added?
+ */
+ addComponent: function(point, index) {
+ var added = false;
+ //remove last point
+ var lastPoint = this.components.pop();
+ // given an index, add the point
+ // without an index only add non-duplicate points
+ if(index != null || !point.equals(lastPoint))
+ added = ZOO.Geometry.Collection.prototype.addComponent.apply(this,arguments);
+ //append copy of first point
+ var firstPoint = this.components[0];
+ ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+ return added;
+ },
+ /**
+ * APIMethod: removeComponent
+ * Removes a point from geometry components.
+ *
+ * Parameters:
+ * point - {}
+ */
+ removeComponent: function(point) {
+ if (this.components.length > 4) {
+ //remove last point
+ this.components.pop();
+ //remove our point
+ ZOO.Geometry.Collection.prototype.removeComponent.apply(this,arguments);
+ //append copy of first point
+ var firstPoint = this.components[0];
+ ZOO.Geometry.Collection.prototype.addComponent.apply(this,[firstPoint]);
+ }
+ },
+ /**
+ * Method: move
+ * Moves a geometry by the given displacement along positive x and y axes.
+ * This modifies the position of the geometry and clears the cached
+ * bounds.
+ *
+ * Parameters:
+ * x - {Float} Distance to move geometry in positive x direction.
+ * y - {Float} Distance to move geometry in positive y direction.
+ */
+ move: function(x, y) {
+ for(var i = 0, len=this.components.length; i} Center point for the rotation
+ */
+ rotate: function(angle, origin) {
+ for(var i=0, len=this.components.length; i} Point of origin for resizing
+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
+ *
+ * Returns:
+ * {ZOO.Geometry} - The current geometry.
+ */
+ resize: function(scale, origin, ratio) {
+ for(var i=0, len=this.components.length; i}
+ * dest - {}
+ *
+ * Returns:
+ * {}
+ */
+ transform: function(source, dest) {
+ if (source && dest) {
+ for (var i=0, len=this.components.length; i} The centroid of the ring
+ */
+ getCentroid: function() {
+ if ( this.components && (this.components.length > 2)) {
+ var sumX = 0.0;
+ var sumY = 0.0;
+ for (var i = 0; i < this.components.length - 1; i++) {
+ var b = this.components[i];
+ var c = this.components[i+1];
+ sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
+ sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
+ }
+ var area = -1 * this.getArea();
+ var x = sumX / (6 * area);
+ var y = sumY / (6 * area);
+ }
+ return new ZOO.Geometry.Point(x, y);
+ },
+ /**
+ * Method: getArea
+ * Note - The area is positive if the ring is oriented CW, otherwise
+ * it will be negative.
+ *
+ * Returns:
+ * {Float} The signed area for a ring.
+ */
+ getArea: function() {
+ var area = 0.0;
+ if ( this.components && (this.components.length > 2)) {
+ var sum = 0.0;
+ for (var i=0, len=this.components.length; i} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Reference:
+ * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+ * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+ *
+ * Returns:
+ * {float} The approximate signed geodesic area of the polygon in square
+ * meters.
+ */
+ getGeodesicArea: function(projection) {
+ var ring = this; // so we can work with a clone if needed
+ if(projection) {
+ var gg = new ZOO.Projection("EPSG:4326");
+ if(!gg.equals(projection)) {
+ ring = this.clone().transform(projection, gg);
+ }
+ }
+ var area = 0.0;
+ var len = ring.components && ring.components.length;
+ if(len > 2) {
+ var p1, p2;
+ for(var i=0; i}
+ *
+ * Returns:
+ * {Boolean | Number} The point is inside the linear ring. Returns 1 if
+ * the point is coincident with an edge. Returns boolean otherwise.
+ */
+ containsPoint: function(point) {
+ var approx = OpenLayers.Number.limitSigDigs;
+ var digs = 14;
+ var px = approx(point.x, digs);
+ var py = approx(point.y, digs);
+ function getX(y, x1, y1, x2, y2) {
+ return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
+ }
+ var numSeg = this.components.length - 1;
+ var start, end, x1, y1, x2, y2, cx, cy;
+ var crosses = 0;
+ for(var i=0; i= x1 && px <= x2) || // right or vert
+ x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
+ // point on edge
+ crosses = -1;
+ break;
+ }
+ }
+ // ignore other horizontal edges
+ continue;
+ }
+ cx = approx(getX(py, x1, y1, x2, y2), digs);
+ if(cx == px) {
+ // point on line
+ if(y1 < y2 && (py >= y1 && py <= y2) || // upward
+ y1 > y2 && (py <= y1 && py >= y2)) { // downward
+ // point on edge
+ crosses = -1;
+ break;
+ }
+ }
+ if(cx <= px) {
+ // no crossing to the right
+ continue;
+ }
+ if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
+ // no crossing
+ continue;
+ }
+ if(y1 < y2 && (py >= y1 && py < y2) || // upward
+ y1 > y2 && (py < y1 && py >= y2)) { // downward
+ ++crosses;
+ }
+ }
+ var contained = (crosses == -1) ?
+ // on edge
+ 1 :
+ // even (out) or odd (in)
+ !!(crosses & 1);
+
+ return contained;
+ },
+ intersects: function(geometry) {
+ var intersect = false;
+ if(geometry.CLASS_NAME == "ZOO.Geometry.Point")
+ intersect = this.containsPoint(geometry);
+ else if(geometry.CLASS_NAME == "ZOO.Geometry.LineString")
+ intersect = geometry.intersects(this);
+ else if(geometry.CLASS_NAME == "ZOO.Geometry.LinearRing")
+ intersect = ZOO.Geometry.LineString.prototype.intersects.apply(
+ this, [geometry]
+ );
+ else
+ for(var i=0, len=geometry.components.length; i
+ * components.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Geometry.MultiLineString = ZOO.Class(
+ ZOO.Geometry.Collection, {
+ componentTypes: ["ZOO.Geometry.LineString"],
+ /**
+ * Constructor: ZOO.Geometry.MultiLineString
+ * Constructor for a MultiLineString Geometry.
+ *
+ * Parameters:
+ * components - {Array()}
+ *
+ */
+ initialize: function(components) {
+ ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+ },
+ split: function(geometry, options) {
+ var results = null;
+ var mutual = options && options.mutual;
+ var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
+ var sourceParts = [];
+ var targetParts = [geometry];
+ for(var i=0, len=this.components.length; i 1)
+ sourceSplit = true;
+ else
+ sourceParts = [];
+ if(targetParts && targetParts.length > 1)
+ targetSplit = true;
+ else
+ targetParts = [];
+ if(sourceSplit || targetSplit) {
+ if(mutual)
+ results = [sourceParts, targetParts];
+ else
+ results = targetParts;
+ }
+ return results;
+ },
+ splitWith: function(geometry, options) {
+ var results = null;
+ var mutual = options && options.mutual;
+ var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
+ if(geometry instanceof ZOO.Geometry.LineString) {
+ targetParts = [];
+ sourceParts = [geometry];
+ for(var i=0, len=this.components.length; i 1)
+ sourceSplit = true;
+ else
+ sourceParts = [];
+ if(targetParts && targetParts.length > 1)
+ targetSplit = true;
+ else
+ targetParts = [];
+ if(sourceSplit || targetSplit) {
+ if(mutual)
+ results = [sourceParts, targetParts];
+ else
+ results = targetParts;
+ }
+ return results;
+ },
+ CLASS_NAME: "ZOO.Geometry.MultiLineString"
+});
+/**
+ * Class: ZOO.Geometry.Polygon
+ * Polygon is a collection of .
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Geometry.Polygon = ZOO.Class(
+ ZOO.Geometry.Collection, {
+ componentTypes: ["ZOO.Geometry.LinearRing"],
+ /**
+ * Constructor: ZOO.Geometry.Polygon
+ * Constructor for a Polygon geometry.
+ * The first ring (this.component[0])is the outer bounds of the polygon and
+ * all subsequent rings (this.component[1-n]) are internal holes.
+ *
+ *
+ * Parameters:
+ * components - {Array()}
+ */
+ initialize: function(components) {
+ ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+ },
+ /**
+ * Method: getArea
+ * Calculated by subtracting the areas of the internal holes from the
+ * area of the outer hole.
+ *
+ * Returns:
+ * {float} The area of the geometry
+ */
+ getArea: function() {
+ var area = 0.0;
+ if ( this.components && (this.components.length > 0)) {
+ area += Math.abs(this.components[0].getArea());
+ for (var i=1, len=this.components.length; i} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Reference:
+ * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+ * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+ *
+ * Returns:
+ * {float} The approximate geodesic area of the polygon in square meters.
+ */
+ getGeodesicArea: function(projection) {
+ var area = 0.0;
+ if(this.components && (this.components.length > 0)) {
+ area += Math.abs(this.components[0].getGeodesicArea(projection));
+ for(var i=1, len=this.components.length; i}
+ *
+ * Returns:
+ * {Boolean | Number} The point is inside the polygon. Returns 1 if the
+ * point is on an edge. Returns boolean otherwise.
+ */
+ containsPoint: function(point) {
+ var numRings = this.components.length;
+ var contained = false;
+ if(numRings > 0) {
+ // check exterior ring - 1 means on edge, boolean otherwise
+ contained = this.components[0].containsPoint(point);
+ if(contained !== 1) {
+ if(contained && numRings > 1) {
+ // check interior rings
+ var hole;
+ for(var i=1; i} center of polygon.
+ * radius - {Float} distance to vertex, in map units.
+ * sides - {Integer} Number of sides. 20 approximates a circle.
+ * rotation - {Float} original angle of rotation, in degrees.
+ */
+ZOO.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {
+ var angle = Math.PI * ((1/sides) - (1/2));
+ if(rotation) {
+ angle += (rotation / 180) * Math.PI;
+ }
+ var rotatedAngle, x, y;
+ var points = [];
+ for(var i=0; i
+ * components. Create a new instance with the
+ * constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Geometry.MultiPolygon = ZOO.Class(
+ ZOO.Geometry.Collection, {
+ componentTypes: ["ZOO.Geometry.Polygon"],
+ /**
+ * Constructor: ZOO.Geometry.MultiPolygon
+ * Create a new MultiPolygon geometry
+ *
+ * Parameters:
+ * components - {Array()} An array of polygons
+ * used to generate the MultiPolygon
+ *
+ */
+ initialize: function(components) {
+ ZOO.Geometry.Collection.prototype.initialize.apply(this,arguments);
+ },
+ CLASS_NAME: "ZOO.Geometry.MultiPolygon"
+});
+/**
+ * Class: ZOO.Process
+ * Used to query OGC WPS process defined by its URL and its identifier.
+ * Usefull for chaining localhost process.
+ */
+ZOO.Process = ZOO.Class({
+ /**
+ * Property: schemaLocation
+ * {String} Schema location for a particular minor version.
+ */
+ schemaLocation: "http://www.opengis.net/wps/1.0.0/../wpsExecute_request.xsd",
+ /**
+ * Property: namespaces
+ * {Object} Mapping of namespace aliases to namespace URIs.
+ */
+ namespaces: {
+ ows: "http://www.opengis.net/ows/1.1",
+ wps: "http://www.opengis.net/wps/1.0.0",
+ xlink: "http://www.w3.org/1999/xlink",
+ xsi: "http://www.w3.org/2001/XMLSchema-instance",
+ },
+ /**
+ * Property: url
+ * {String} The OGC's Web PRocessing Service URL,
+ * default is http://localhost/zoo.
+ */
+ url: 'http://localhost/zoo',
+ /**
+ * Property: identifier
+ * {String} Process identifier in the OGC's Web Processing Service.
+ */
+ identifier: null,
+ /**
+ * Constructor: ZOO.Process
+ * Create a new Process
+ *
+ * Parameters:
+ * url - {String} The OGC's Web Processing Service URL.
+ * identifier - {String} The process identifier in the OGC's Web Processing Service.
+ *
+ */
+ initialize: function(url,identifier) {
+ this.url = url;
+ this.identifier = identifier;
+ },
+ /**
+ * Method: Execute
+ * Query the OGC's Web PRocessing Servcie to Execute the process.
+ *
+ * Parameters:
+ * inputs - {Object}
+ *
+ * Returns:
+ * {String} The OGC's Web processing Service XML response. The result
+ * needs to be interpreted.
+ */
+ Execute: function(inputs) {
+ if (this.identifier == null)
+ return null;
+ var body = new XML(''+this.identifier+''+this.buildDataInputsNode(inputs)+'');
+ body = body.toXMLString();
+ var response = ZOO.Request.Post(this.url,body,['Content-Type: text/xml; charset=UTF-8']);
+ return response;
+ },
+ /**
+ * Property: buildInput
+ * Object containing methods to build WPS inputs.
+ */
+ buildInput: {
+ /**
+ * Method: buildInput.complex
+ * Given an E4XElement representing the WPS complex data input.
+ *
+ * Parameters:
+ * identifier - {String} the input indetifier
+ * data - {Object} A WPS complex data input.
+ *
+ * Returns:
+ * {E4XElement} A WPS Input node.
+ */
+ 'complex': function(identifier,data) {
+ var input = new XML(''+identifier+''+data.value+'');
+ input.*::Data.*::ComplexData.@mimeType = data.mimetype ? data.mimetype : 'text/plain';
+ if (data.encoding)
+ input.*::Data.*::ComplexData.@encoding = data.encoding;
+ if (data.schema)
+ input.*::Data.*::ComplexData.@schema = data.schema;
+ input = input.toXMLString();
+ return input;
+ },
+ /**
+ * Method: buildInput.reference
+ * Given an E4XElement representing the WPS reference input.
+ *
+ * Parameters:
+ * identifier - {String} the input indetifier
+ * data - {Object} A WPS reference input.
+ *
+ * Returns:
+ * {E4XElement} A WPS Input node.
+ */
+ 'reference': function(identifier,data) {
+ return ''+identifier+'';
+ },
+ /**
+ * Method: buildInput.literal
+ * Given an E4XElement representing the WPS literal data input.
+ *
+ * Parameters:
+ * identifier - {String} the input indetifier
+ * data - {Object} A WPS literal data input.
+ *
+ * Returns:
+ * {E4XElement} The WPS Input node.
+ */
+ 'literal': function(identifier,data) {
+ var input = new XML(''+identifier+''+data.value+'');
+ if (data.type)
+ input.*::Data.*::LiteralData.@dataType = data.type;
+ if (data.uom)
+ input.*::Data.*::LiteralData.@uom = data.uom;
+ input = input.toXMLString();
+ return input;
+ }
+ },
+ /**
+ * Method: buildDataInputsNode
+ * Method to build the WPS DataInputs element.
+ *
+ * Parameters:
+ * inputs - {Object}
+ *
+ * Returns:
+ * {E4XElement} The WPS DataInputs node for Execute query.
+ */
+ buildDataInputsNode:function(inputs){
+ var data, builder, inputsArray=[];
+ for (var attr in inputs) {
+ data = inputs[attr];
+ if (data.mimetype || data.type == 'complex')
+ builder = this.buildInput['complex'];
+ else if (data.type == 'reference' || data.type == 'url')
+ builder = this.buildInput['reference'];
+ else
+ builder = this.buildInput['literal'];
+ inputsArray.push(builder.apply(this,[attr,data]));
+ }
+ return ''+inputsArray.join('\n')+'';
+ },
+ CLASS_NAME: "ZOO.Process"
+});
Index: /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js
===================================================================
--- /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js (revision 221)
+++ /tags/rel-1.2.0-rc1/zoo-api/js/ZOO-proj4js.js (revision 221)
@@ -0,0 +1,6054 @@
+/**
+ * Author : René-Luc D'Hont
+ *
+ * Copyright 2010 3liz SARL. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * Author: Mike Adair madairATdmsolutions.ca
+ * Richard Greenwood rich@greenwoodmap.com
+ * License: LGPL as per: http://www.gnu.org/copyleft/lesser.html
+ * $Id: Proj.js 2956 2007-07-09 12:17:52Z steven $
+ */
+
+/**
+ * Class: ZOO
+ */
+ZOO = {
+ /**
+ * Constant: SERVICE_ACCEPTED
+ * {Integer} used for
+ */
+ SERVICE_ACCEPTED: 0,
+ /**
+ * Constant: SERVICE_STARTED
+ * {Integer} used for
+ */
+ SERVICE_STARTED: 1,
+ /**
+ * Constant: SERVICE_PAUSED
+ * {Integer} used for
+ */
+ SERVICE_PAUSED: 2,
+ /**
+ * Constant: SERVICE_SUCCEEDED
+ * {Integer} used for
+ */
+ SERVICE_SUCCEEDED: 3,
+ /**
+ * Constant: SERVICE_FAILED
+ * {Integer} used for
+ */
+ SERVICE_FAILED: 4,
+ /**
+ * Function: removeItem
+ * Remove an object from an array. Iterates through the array
+ * to find the item, then removes it.
+ *
+ * Parameters:
+ * array - {Array}
+ * item - {Object}
+ *
+ * Return
+ * {Array} A reference to the array
+ */
+ removeItem: function(array, item) {
+ for(var i = array.length - 1; i >= 0; i--) {
+ if(array[i] == item) {
+ array.splice(i,1);
+ }
+ }
+ return array;
+ },
+ /**
+ * Function: indexOf
+ *
+ * Parameters:
+ * array - {Array}
+ * obj - {Object}
+ *
+ * Returns:
+ * {Integer} The index at, which the first object was found in the array.
+ * If not found, returns -1.
+ */
+ indexOf: function(array, obj) {
+ for(var i=0, len=array.length; i} (or any object with both .x, .y properties)
+ * p2 - {} (or any object with both .x, .y properties)
+ *
+ * Returns:
+ * {Float} The distance (in km) between the two input points as measured on an
+ * ellipsoid. Note that the input point objects must be in geographic
+ * coordinates (decimal degrees) and the return distance is in kilometers.
+ */
+ distVincenty: function(p1, p2) {
+ var a = 6378137, b = 6356752.3142, f = 1/298.257223563;
+ var L = ZOO.rad(p2.x - p1.y);
+ var U1 = Math.atan((1-f) * Math.tan(ZOO.rad(p1.y)));
+ var U2 = Math.atan((1-f) * Math.tan(ZOO.rad(p2.y)));
+ var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
+ var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
+ var lambda = L, lambdaP = 2*Math.PI;
+ var iterLimit = 20;
+ while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
+ var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
+ var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
+ (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
+ if (sinSigma==0) {
+ return 0; // co-incident points
+ }
+ var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
+ var sigma = Math.atan2(sinSigma, cosSigma);
+ var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
+ var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
+ var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
+ var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
+ lambdaP = lambda;
+ lambda = L + (1-C) * f * Math.sin(alpha) *
+ (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
+ }
+ if (iterLimit==0) {
+ return NaN; // formula failed to converge
+ }
+ var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
+ var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
+ var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
+ var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
+ B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
+ var s = b*A*(sigma-deltaSigma);
+ var d = s.toFixed(3)/1000; // round to 1mm precision
+ return d;
+ },
+ /**
+ * Function: Class
+ * Method used to create ZOO classes. Includes support for
+ * multiple inheritance.
+ */
+ Class: function() {
+ var Class = function() {
+ this.initialize.apply(this, arguments);
+ };
+ var extended = {};
+ var parent;
+ for(var i=0; i 0) {
+ var separator = (url.indexOf('?') > -1) ? '&' : '?';
+ url += separator + paramString;
+ }
+ return ZOORequest('GET',url);
+ },
+ /**
+ * Function: POST
+ * Send an HTTP POST request.
+ *
+ * Parameters:
+ * url - {String} The URL to request.
+ * body - {String} The request's body to send.
+ * headers - {Object} A key-value object of headers to push to
+ * the request's head
+ *
+ * Returns:
+ * {String} Request result.
+ */
+ Post: function(url,body,headers) {
+ if(!(headers instanceof Array)) {
+ var headersArray = [];
+ for (var name in headers) {
+ headersArray.push(name+': '+headers[name]);
+ }
+ headers = headersArray;
+ }
+ return ZOORequest('POST',url,body,headers);
+ }
+};
+
+/**
+ * Class: ZOO.Bounds
+ * Instances of this class represent bounding boxes. Data stored as left,
+ * bottom, right, top floats. All values are initialized to null,
+ * however, you should make sure you set them before using the bounds
+ * for anything.
+ */
+ZOO.Bounds = ZOO.Class({
+ /**
+ * Property: left
+ * {Number} Minimum horizontal coordinate.
+ */
+ left: null,
+ /**
+ * Property: bottom
+ * {Number} Minimum vertical coordinate.
+ */
+ bottom: null,
+ /**
+ * Property: right
+ * {Number} Maximum horizontal coordinate.
+ */
+ right: null,
+ /**
+ * Property: top
+ * {Number} Maximum vertical coordinate.
+ */
+ top: null,
+ /**
+ * Constructor: ZOO.Bounds
+ * Construct a new bounds object.
+ *
+ * Parameters:
+ * left - {Number} The left bounds of the box. Note that for width
+ * calculations, this is assumed to be less than the right value.
+ * bottom - {Number} The bottom bounds of the box. Note that for height
+ * calculations, this is assumed to be more than the top value.
+ * right - {Number} The right bounds.
+ * top - {Number} The top bounds.
+ */
+ initialize: function(left, bottom, right, top) {
+ if (left != null)
+ this.left = parseFloat(left);
+ if (bottom != null)
+ this.bottom = parseFloat(bottom);
+ if (right != null)
+ this.right = parseFloat(right);
+ if (top != null)
+ this.top = parseFloat(top);
+ },
+ /**
+ * Method: clone
+ * Create a cloned instance of this bounds.
+ *
+ * Returns:
+ * {} A fresh copy of the bounds
+ */
+ clone:function() {
+ return new ZOO.Bounds(this.left, this.bottom,
+ this.right, this.top);
+ },
+ /**
+ * Method: equals
+ * Test a two bounds for equivalence.
+ *
+ * Parameters:
+ * bounds - {}
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object has the same left,
+ * right, top, bottom components as this. Note that if bounds
+ * passed in is null, returns false.
+ */
+ equals:function(bounds) {
+ var equals = false;
+ if (bounds != null)
+ equals = ((this.left == bounds.left) &&
+ (this.right == bounds.right) &&
+ (this.top == bounds.top) &&
+ (this.bottom == bounds.bottom));
+ return equals;
+ },
+ /**
+ * Method: toString
+ *
+ * Returns:
+ * {String} String representation of bounds object.
+ * (ex."left-bottom=(5,42) right-top=(10,45)")
+ */
+ toString:function() {
+ return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+ + " right-top=(" + this.right + "," + this.top + ")" );
+ },
+ /**
+ * APIMethod: toArray
+ *
+ * Returns:
+ * {Array} array of left, bottom, right, top
+ */
+ toArray: function() {
+ return [this.left, this.bottom, this.right, this.top];
+ },
+ /**
+ * Method: toBBOX
+ *
+ * Parameters:
+ * decimal - {Integer} How many significant digits in the bbox coords?
+ * Default is 6
+ *
+ * Returns:
+ * {String} Simple String representation of bounds object.
+ * (ex. "5,42,10,45")
+ */
+ toBBOX:function(decimal) {
+ if (decimal== null)
+ decimal = 6;
+ var mult = Math.pow(10, decimal);
+ var bbox = Math.round(this.left * mult) / mult + "," +
+ Math.round(this.bottom * mult) / mult + "," +
+ Math.round(this.right * mult) / mult + "," +
+ Math.round(this.top * mult) / mult;
+ return bbox;
+ },
+ /**
+ * Method: toGeometry
+ * Create a new polygon geometry based on this bounds.
+ *
+ * Returns:
+ * {} A new polygon with the coordinates
+ * of this bounds.
+ */
+ toGeometry: function() {
+ return new ZOO.Geometry.Polygon([
+ new ZOO.Geometry.LinearRing([
+ new ZOO.Geometry.Point(this.left, this.bottom),
+ new ZOO.Geometry.Point(this.right, this.bottom),
+ new ZOO.Geometry.Point(this.right, this.top),
+ new ZOO.Geometry.Point(this.left, this.top)
+ ])
+ ]);
+ },
+ /**
+ * Method: getWidth
+ *
+ * Returns:
+ * {Float} The width of the bounds
+ */
+ getWidth:function() {
+ return (this.right - this.left);
+ },
+ /**
+ * Method: getHeight
+ *
+ * Returns:
+ * {Float} The height of the bounds (top minus bottom).
+ */
+ getHeight:function() {
+ return (this.top - this.bottom);
+ },
+ /**
+ * Method: add
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ *
+ * Returns:
+ * {} A new bounds whose coordinates are the same as
+ * this, but shifted by the passed-in x and y values.
+ */
+ add:function(x, y) {
+ if ( (x == null) || (y == null) )
+ return null;
+ return new ZOO.Bounds(this.left + x, this.bottom + y,
+ this.right + x, this.top + y);
+ },
+ /**
+ * Method: extend
+ * Extend the bounds to include the point, lonlat, or bounds specified.
+ * Note, this function assumes that left < right and bottom < top.
+ *
+ * Parameters:
+ * object - {Object} Can be Point, or Bounds
+ */
+ extend:function(object) {
+ var bounds = null;
+ if (object) {
+ // clear cached center location
+ switch(object.CLASS_NAME) {
+ case "ZOO.Geometry.Point":
+ bounds = new ZOO.Bounds(object.x, object.y,
+ object.x, object.y);
+ break;
+ case "ZOO.Bounds":
+ bounds = object;
+ break;
+ }
+ if (bounds) {
+ if ( (this.left == null) || (bounds.left < this.left))
+ this.left = bounds.left;
+ if ( (this.bottom == null) || (bounds.bottom < this.bottom) )
+ this.bottom = bounds.bottom;
+ if ( (this.right == null) || (bounds.right > this.right) )
+ this.right = bounds.right;
+ if ( (this.top == null) || (bounds.top > this.top) )
+ this.top = bounds.top;
+ }
+ }
+ },
+ /**
+ * APIMethod: contains
+ *
+ * Parameters:
+ * x - {Float}
+ * y - {Float}
+ * inclusive - {Boolean} Whether or not to include the border.
+ * Default is true.
+ *
+ * Returns:
+ * {Boolean} Whether or not the passed-in coordinates are within this
+ * bounds.
+ */
+ contains:function(x, y, inclusive) {
+ //set default
+ if (inclusive == null)
+ inclusive = true;
+ if (x == null || y == null)
+ return false;
+ x = parseFloat(x);
+ y = parseFloat(y);
+
+ var contains = false;
+ if (inclusive)
+ contains = ((x >= this.left) && (x <= this.right) &&
+ (y >= this.bottom) && (y <= this.top));
+ else
+ contains = ((x > this.left) && (x < this.right) &&
+ (y > this.bottom) && (y < this.top));
+ return contains;
+ },
+ /**
+ * Method: intersectsBounds
+ * Determine whether the target bounds intersects this bounds. Bounds are
+ * considered intersecting if any of their edges intersect or if one
+ * bounds contains the other.
+ *
+ * Parameters:
+ * bounds - {} The target bounds.
+ * inclusive - {Boolean} Treat coincident borders as intersecting. Default
+ * is true. If false, bounds that do not overlap but only touch at the
+ * border will not be considered as intersecting.
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object intersects this bounds.
+ */
+ intersectsBounds:function(bounds, inclusive) {
+ if (inclusive == null)
+ inclusive = true;
+ var intersects = false;
+ var mightTouch = (
+ this.left == bounds.right ||
+ this.right == bounds.left ||
+ this.top == bounds.bottom ||
+ this.bottom == bounds.top
+ );
+ if (inclusive || !mightTouch) {
+ var inBottom = (
+ ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
+ ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
+ );
+ var inTop = (
+ ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
+ ((this.top > bounds.bottom) && (this.top < bounds.top))
+ );
+ var inLeft = (
+ ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
+ ((this.left >= bounds.left) && (this.left <= bounds.right))
+ );
+ var inRight = (
+ ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
+ ((this.right >= bounds.left) && (this.right <= bounds.right))
+ );
+ intersects = ((inBottom || inTop) && (inLeft || inRight));
+ }
+ return intersects;
+ },
+ /**
+ * Method: containsBounds
+ * Determine whether the target bounds is contained within this bounds.
+ *
+ * bounds - {} The target bounds.
+ * partial - {Boolean} If any of the target corners is within this bounds
+ * consider the bounds contained. Default is false. If true, the
+ * entire target bounds must be contained within this bounds.
+ * inclusive - {Boolean} Treat shared edges as contained. Default is
+ * true.
+ *
+ * Returns:
+ * {Boolean} The passed-in bounds object is contained within this bounds.
+ */
+ containsBounds:function(bounds, partial, inclusive) {
+ if (partial == null)
+ partial = false;
+ if (inclusive == null)
+ inclusive = true;
+ var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
+ var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
+ var topLeft = this.contains(bounds.left, bounds.top, inclusive);
+ var topRight = this.contains(bounds.right, bounds.top, inclusive);
+ return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
+ : (bottomLeft && bottomRight && topLeft && topRight);
+ },
+ CLASS_NAME: 'ZOO.Bounds'
+});
+
+/**
+ * Class: ZOO.Projection
+ * Class for coordinate transforms between coordinate systems.
+ * Depends on the zoo-proj4js library. zoo-proj4js library
+ * is loaded by the ZOO Kernel with zoo-api.
+ */
+ZOO.Projection = ZOO.Class({
+ /**
+ * Property: proj
+ * {Object} Proj4js.Proj instance.
+ */
+ proj: null,
+ /**
+ * Property: projCode
+ * {String}
+ */
+ projCode: null,
+ /**
+ * Constructor: ZOO.Projection
+ * This class offers several methods for interacting with a wrapped
+ * zoo-pro4js projection object.
+ *
+ * Parameters:
+ * projCode - {String} A string identifying the Well Known Identifier for
+ * the projection.
+ * options - {Object} An optional object to set additional properties.
+ *
+ * Returns:
+ * {} A projection object.
+ */
+ initialize: function(projCode, options) {
+ ZOO.extend(this, options);
+ this.projCode = projCode;
+ if (Proj4js) {
+ this.proj = new Proj4js.Proj(projCode);
+ }
+ },
+ /**
+ * Method: getCode
+ * Get the string SRS code.
+ *
+ * Returns:
+ * {String} The SRS code.
+ */
+ getCode: function() {
+ return this.proj ? this.proj.srsCode : this.projCode;
+ },
+ /**
+ * Method: getUnits
+ * Get the units string for the projection -- returns null if
+ * zoo-proj4js is not available.
+ *
+ * Returns:
+ * {String} The units abbreviation.
+ */
+ getUnits: function() {
+ return this.proj ? this.proj.units : null;
+ },
+ /**
+ * Method: toString
+ * Convert projection to string (getCode wrapper).
+ *
+ * Returns:
+ * {String} The projection code.
+ */
+ toString: function() {
+ return this.getCode();
+ },
+ /**
+ * Method: equals
+ * Test equality of two projection instances. Determines equality based
+ * soley on the projection code.
+ *
+ * Returns:
+ * {Boolean} The two projections are equivalent.
+ */
+ equals: function(projection) {
+ if (projection && projection.getCode)
+ return this.getCode() == projection.getCode();
+ else
+ return false;
+ },
+ /* Method: destroy
+ * Destroy projection object.
+ */
+ destroy: function() {
+ this.proj = null;
+ this.projCode = null;
+ },
+ CLASS_NAME: 'ZOO.Projection'
+});
+/**
+ * Method: transform
+ * Transform a point coordinate from one projection to another. Note that
+ * the input point is transformed in place.
+ *
+ * Parameters:
+ * point - {{ZOO.Geometry.Point> | Object} An object with x and y
+ * properties representing coordinates in those dimensions.
+ * sourceProj - {ZOO.Projection} Source map coordinate system
+ * destProj - {ZOO.Projection} Destination map coordinate system
+ *
+ * Returns:
+ * point - {object} A transformed coordinate. The original point is modified.
+ */
+ZOO.Projection.transform = function(point, source, dest) {
+ if (source.proj && dest.proj)
+ point = Proj4js.transform(source.proj, dest.proj, point);
+ return point;
+};
+
+/**
+ * Class: ZOO.Format
+ * Base class for format reading/writing a variety of formats. Subclasses
+ * of ZOO.Format are expected to have read and write methods.
+ */
+ZOO.Format = ZOO.Class({
+ /**
+ * Property: options
+ * {Object} A reference to options passed to the constructor.
+ */
+ options:null,
+ /**
+ * Property: externalProjection
+ * {} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The externalProjection is the projection used by
+ * the content which is passed into read or which comes out of write.
+ * In order to reproject, a projection transformation function for the
+ * specified projections must be available. This support is provided
+ * via zoo-proj4js.
+ */
+ externalProjection: null,
+ /**
+ * Property: internalProjection
+ * {} When passed a externalProjection and
+ * internalProjection, the format will reproject the geometries it
+ * reads or writes. The internalProjection is the projection used by
+ * the geometries which are returned by read or which are passed into
+ * write. In order to reproject, a projection transformation function
+ * for the specified projections must be available. This support is
+ * provided via zoo-proj4js.
+ */
+ internalProjection: null,
+ /**
+ * Property: data
+ * {Object} When is true, this is the parsed string sent to
+ * .
+ */
+ data: null,
+ /**
+ * Property: keepData
+ * {Object} Maintain a reference () to the most recently read data.
+ * Default is false.
+ */
+ keepData: false,
+ /**
+ * Constructor: ZOO.Format
+ * Instances of this class are not useful. See one of the subclasses.
+ *
+ * Parameters:
+ * options - {Object} An optional object with properties to set on the
+ * format
+ *
+ * Valid options:
+ * keepData - {Boolean} If true, upon , the data property will be
+ * set to the parsed object (e.g. the json or xml object).
+ *
+ * Returns:
+ * An instance of ZOO.Format
+ */
+ initialize: function(options) {
+ ZOO.extend(this, options);
+ this.options = options;
+ },
+ /**
+ * Method: destroy
+ * Clean up.
+ */
+ destroy: function() {
+ },
+ /**
+ * Method: read
+ * Read data from a string, and return an object whose type depends on the
+ * subclass.
+ *
+ * Parameters:
+ * data - {string} Data to read/parse.
+ *
+ * Returns:
+ * Depends on the subclass
+ */
+ read: function(data) {
+ },
+ /**
+ * Method: write
+ * Accept an object, and return a string.
+ *
+ * Parameters:
+ * object - {Object} Object to be serialized
+ *
+ * Returns:
+ * {String} A string representation of the object.
+ */
+ write: function(data) {
+ },
+ CLASS_NAME: 'ZOO.Format'
+});
+/**
+ * Class: ZOO.Format.WKT
+ * Class for reading and writing Well-Known Text. Create a new instance
+ * with the constructor.
+ *
+ * Inherits from:
+ * -
+ */
+ZOO.Format.WKT = ZOO.Class(ZOO.Format, {
+ /**
+ * Constructor: ZOO.Format.WKT
+ * Create a new parser for WKT
+ *
+ * Parameters:
+ * options - {Object} An optional object whose properties will be set on
+ * this instance
+ *
+ * Returns:
+ * {} A new WKT parser.
+ */
+ initialize: function(options) {
+ this.regExes = {
+ 'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
+ 'spaces': /\s+/,
+ 'parenComma': /\)\s*,\s*\(/,
+ 'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
+ 'trimParens': /^\s*\(?(.*?)\)?\s*$/
+ };
+ ZOO.Format.prototype.initialize.apply(this, [options]);
+ },
+ /**
+ * Method: read
+ * Deserialize a WKT string and return a vector feature or an
+ * array of vector features. Supports WKT for POINT,
+ * MULTIPOINT, LINESTRING, MULTILINESTRING, POLYGON,
+ * MULTIPOLYGON, and GEOMETRYCOLLECTION.
+ *
+ * Parameters:
+ * wkt - {String} A WKT string
+ *
+ * Returns:
+ * {|Array} A feature or array of features for
+ * GEOMETRYCOLLECTION WKT.
+ */
+ read: function(wkt) {
+ var features, type, str;
+ var matches = this.regExes.typeStr.exec(wkt);
+ if(matches) {
+ type = matches[1].toLowerCase();
+ str = matches[2];
+ if(this.parse[type]) {
+ features = this.parse[type].apply(this, [str]);
+ }
+ if (this.internalProjection && this.externalProjection) {
+ if (features &&
+ features.CLASS_NAME == "ZOO.Feature") {
+ features.geometry.transform(this.externalProjection,
+ this.internalProjection);
+ } else if (features &&
+ type != "geometrycollection" &&
+ typeof features == "object") {
+ for (var i=0, len=features.length; i|Array} A feature or array of
+ * features
+ *
+ * Returns:
+ * {String} The WKT string representation of the input geometries
+ */
+ write: function(features) {
+ var collection, geometry, type, data, isCollection;
+ if(features.constructor == Array) {
+ collection = features;
+ isCollection = true;
+ } else {
+ collection = [features];
+ isCollection = false;
+ }
+ var pieces = [];
+ if(isCollection)
+ pieces.push('GEOMETRYCOLLECTION(');
+ for(var i=0, len=collection.length; i0)
+ pieces.push(',');
+ geometry = collection[i].geometry;
+ type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
+ if(!this.extract[type])
+ return null;
+ if (this.internalProjection && this.externalProjection) {
+ geometry = geometry.clone();
+ geometry.transform(this.internalProjection,
+ this.externalProjection);
+ }
+ data = this.extract[type].apply(this, [geometry]);
+ pieces.push(type.toUpperCase() + '(' + data + ')');
+ }
+ if(isCollection)
+ pieces.push(')');
+ return pieces.join('');
+ },
+ /**
+ * Property: extract
+ * Object with properties corresponding to the geometry types.
+ * Property values are functions that do the actual data extraction.
+ */
+ extract: {
+ /**
+ * Return a space delimited string of point coordinates.
+ * @param {} point
+ * @returns {String} A string of coordinates representing the point
+ */
+ 'point': function(point) {
+ return point.x + ' ' + point.y;
+ },
+ /**
+ * Return a comma delimited string of point coordinates from a multipoint.
+ * @param {} multipoint
+ * @returns {String} A string of point coordinate strings representing
+ * the multipoint
+ */
+ 'multipoint': function(multipoint) {
+ var array = [];
+ for(var i=0, len=multipoint.components.length; i} linestring
+ * @returns {String} A string of point coordinate strings representing
+ * the linestring
+ */
+ 'linestring': function(linestring) {
+ var array = [];
+ for(var i=0, len=linestring.components.length; i} multilinestring
+ * @returns {String} A string of of linestring strings representing
+ * the multilinestring
+ */
+ 'multilinestring': function(multilinestring) {
+ var array = [];
+ for(var i=0, len=multilinestring.components.length; i} polygon
+ * @returns {String} An array of linear ring arrays representing the polygon
+ */
+ 'polygon': function(polygon) {
+ var array = [];
+ for(var i=0, len=polygon.components.length; i} multipolygon
+ * @returns {Array} An array of polygon arrays representing
+ * the multipolygon
+ */
+ 'multipolygon': function(multipolygon) {
+ var array = [];
+ for(var i=0, len=multipolygon.components.length; i} A point feature
+ */
+ 'point': function(str) {
+ var coords = ZOO.String.trim(str).split(this.regExes.spaces);
+ return new ZOO.Feature(
+ new ZOO.Geometry.Point(coords[0], coords[1])
+ );
+ },
+ /**
+ * Method: parse.multipoint
+ * Return a multipoint feature given a multipoint WKT fragment.
+ *
+ * Parameters:
+ * str - {String} A WKT fragment representing the multipoint
+ *
+ * Returns:
+ * {} A multipoint feature
+ */
+ 'multipoint': function(str) {
+ var points = ZOO.String.trim(str).split(',');
+ var components = [];
+ for(var i=0, len=points.length; i