Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 05c48a334a
Fetching contributors…

Cannot retrieve contributors at this time

6577 lines (5751 sloc) 225.274 kB
/******************************************************************************
* $id: mapfile.c 7854 2008-08-14 19:22:48Z dmorissette $
*
* Project: MapServer
* Purpose: High level Map file parsing code.
* Author: Steve Lime and the MapServer team.
*
******************************************************************************
* Copyright (c) 1996-2005 Regents of the University of Minnesota.
*
* 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 of this Software or works derived from this 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.
****************************************************************************/
#define _GNU_SOURCE
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include "mapserver.h"
#include "mapfile.h"
#include "mapthread.h"
#include "maptime.h"
#ifdef USE_GDAL
# include "cpl_conv.h"
# include "gdal.h"
#endif
extern int msyylex(void);
extern void msyyrestart(FILE *);
extern int msyylex_destroy(void);
extern double msyynumber;
extern int msyylineno;
extern FILE *msyyin;
extern int msyysource;
extern int msyystate;
extern char *msyystring;
extern char *msyybasepath;
extern int msyyreturncomments;
extern char *msyystring_buffer;
extern char msyystring_icase;
extern int loadSymbol(symbolObj *s, char *symbolpath); /* in mapsymbol.c */
extern void writeSymbol(symbolObj *s, FILE *stream); /* in mapsymbol.c */
static int loadGrid( layerObj *pLayer );
static int loadStyle(styleObj *style);
static void writeStyle(FILE* stream, int indent, styleObj *style);
static int resolveSymbolNames(mapObj *map);
static int loadExpression(expressionObj *exp);
static void writeExpression(FILE *stream, int indent, const char *name, expressionObj *exp);
/*
** Symbol to string static arrays needed for writing map files.
** Must be kept in sync with enumerations and defines found in mapserver.h.
*/
/* static char *msUnits[9]={"INCHES", "FEET", "MILES", "METERS", "KILOMETERS", "DD", "PIXELS", "PERCENTAGES", "NAUTICALMILES"}; */
/* static char *msLayerTypes[9]={"POINT", "LINE", "POLYGON", "RASTER", "ANNOTATION", "QUERY", "CIRCLE", "TILEINDEX","CHART"}; */
char *msPositionsText[MS_POSITIONS_LENGTH] = {"UL", "LR", "UR", "LL", "CR", "CL", "UC", "LC", "CC", "AUTO", "XY", "FOLLOW"}; /* msLabelPositions[] also used in mapsymbols.c (not static) */
/* static char *msBitmapFontSizes[5]={"TINY", "SMALL", "MEDIUM", "LARGE", "GIANT"}; */
/* static char *msQueryMapStyles[4]={"NORMAL", "HILITE", "SELECTED", "INVERTED"}; */
/* static char *msStatus[4]={"OFF", "ON", "DEFAULT", "EMBED"}; */
/* static char *msOnOff[2]={"OFF", "ON"}; */
/* static char *msTrueFalse[2]={"FALSE", "TRUE"}; */
/* static char *msYesNo[2]={"NO", "YES"}; */
/* static char *msJoinType[2]={"ONE-TO-ONE", "ONE-TO-MANY"}; */
/* static char *msAlignValue[3]={"LEFT","CENTER","RIGHT"}; */
/*
** Validates a string (value) against a series of patterns. We support up to four to allow cascading from classObj to
** layerObj to webObj plus a legacy pattern like TEMPLATEPATTERN or qstring_validation_pattern.
*/
int msValidateParameter(char *value, char *pattern1, char *pattern2, char *pattern3, char *pattern4)
{
if(msEvalRegex(pattern1, value) == MS_TRUE) return MS_SUCCESS;
if(msEvalRegex(pattern2, value) == MS_TRUE) return MS_SUCCESS;
if(msEvalRegex(pattern3, value) == MS_TRUE) return MS_SUCCESS;
if(msEvalRegex(pattern4, value) == MS_TRUE) return MS_SUCCESS;
msSetError(MS_REGEXERR, "Parameter pattern validation failed." , "msValidateParameter()");
return(MS_FAILURE);
}
int msEvalRegex(char *e, char *s)
{
ms_regex_t re;
if(!e || !s) return(MS_FALSE);
if(ms_regcomp(&re, e, MS_REG_EXTENDED|MS_REG_NOSUB) != 0) {
msSetError(MS_REGEXERR, "Failed to compile expression (%s).", "msEvalRegex()", e);
return(MS_FALSE);
}
if(ms_regexec(&re, s, 0, NULL, 0) != 0) { /* no match */
ms_regfree(&re);
msSetError(MS_REGEXERR, "String failed expression test.", "msEvalRegex()");
return(MS_FALSE);
}
ms_regfree(&re);
return(MS_TRUE);
}
#ifdef USE_MSFREE
void msFree(void *p)
{
if(p) free(p);
}
#endif
/*
** Free memory allocated for a character array
*/
void msFreeCharArray(char **array, int num_items)
{
int i;
for(i=0; i<num_items; i++)
msFree(array[i]);
msFree(array);
}
/*
** Checks symbol from lexer against variable length list of
** legal symbols.
*/
int getSymbol(int n, ...)
{
int symbol;
va_list argp;
int i=0;
symbol = msyylex();
va_start(argp, n);
while(i<n) { /* check each symbol in the list */
if(symbol == va_arg(argp, int)) {
va_end(argp);
return(symbol);
}
i++;
}
va_end(argp);
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getSymbol()", msyystring_buffer, msyylineno);
return(-1);
}
/*
** Get a string or symbol as a string. Operates like getString(), but also
** supports symbols.
*/
static char *getToken(void)
{
msyylex();
return msStrdup(msyystring_buffer);
}
/*
** Load a string from the map file. A "string" is defined in lexer.l.
*/
int getString(char **s)
{
/* if (*s)
msSetError(MS_SYMERR, "Duplicate item (%s):(line %d)", "getString()", msyystring_buffer, msyylineno);
return(MS_FAILURE);
} else */
if(msyylex() == MS_STRING) {
if(*s) free(*s); /* avoid leak */
*s = msStrdup(msyystring_buffer);
return(MS_SUCCESS);
}
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getString()", msyystring_buffer, msyylineno);
return(MS_FAILURE);
}
/*
** Load a floating point number from the map file. (see lexer.l)
*/
int getDouble(double *d)
{
if(msyylex() == MS_NUMBER) {
*d = msyynumber;
return(0); /* success */
}
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getDouble()", msyystring_buffer, msyylineno);
return(-1);
}
/*
** Load a integer from the map file. (see lexer.l)
*/
int getInteger(int *i)
{
if(msyylex() == MS_NUMBER) {
*i = (int)msyynumber;
return(0); /* success */
}
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getInteger()", msyystring_buffer, msyylineno);
return(-1);
}
int getCharacter(char *c)
{
if(msyylex() == MS_STRING) {
*c = msyystring_buffer[0];
return(0);
}
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)", "getCharacter()", msyystring_buffer, msyylineno);
return(-1);
}
/*
** Try to load as an integer, then try as a named symbol.
** Part of work on bug 490.
*/
int getIntegerOrSymbol(int *i, int n, ...)
{
int symbol;
va_list argp;
int j=0;
symbol = msyylex();
if (symbol == MS_NUMBER) {
*i = (int)msyynumber;
return MS_SUCCESS; /* success */
}
va_start(argp, n);
while (j<n) { /* check each symbol in the list */
if(symbol == va_arg(argp, int)) {
va_end(argp);
*i = symbol;
return MS_SUCCESS;
}
j++;
}
va_end(argp);
msSetError(MS_SYMERR, "Parsing error near (%s):(line %d)",
"getIntegerOrSymbol()", msyystring_buffer, msyylineno);
return(-1);
}
/*
** msBuildPluginLibraryPath
**
** This function builds a path to be used dynamically to load plugin library.
*/
int msBuildPluginLibraryPath(char **dest, const char *lib_str, mapObj *map)
{
char szLibPath[MS_MAXPATHLEN] = { '\0' };
char szLibPathExt[MS_MAXPATHLEN] = { '\0' };
const char *plugin_dir = msLookupHashTable( &(map->configoptions), "MS_PLUGIN_DIR");
/* do nothing on windows, filename without .dll will be loaded by default*/
#if !defined(_WIN32)
if (lib_str) {
size_t len = strlen(lib_str);
if (3 < len && strcmp(lib_str + len-3, ".so")) {
strlcpy(szLibPathExt, lib_str, MS_MAXPATHLEN);
strlcat(szLibPathExt, ".so", MS_MAXPATHLEN);
lib_str = szLibPathExt;
}
}
#endif /* !defined(_WIN32) */
if (NULL == msBuildPath(szLibPath, plugin_dir, lib_str)) {
return MS_FAILURE;
}
*dest = msStrdup(szLibPath);
return MS_SUCCESS;
}
/*
** Returns the index of specified symbol or -1 if not found.
**
** If try_addimage_if_notfound==MS_TRUE then msAddImageSymbol() will be called
** to try to allocate the symbol as an image symbol.
*/
int msGetSymbolIndex(symbolSetObj *symbols, char *name, int try_addimage_if_notfound)
{
int i;
if(!symbols || !name) return(-1);
/* symbol 0 has no name */
for(i=1; i<symbols->numsymbols; i++) {
if(symbols->symbol[i]->name)
if(strcasecmp(symbols->symbol[i]->name, name) == 0) return(i);
}
if (try_addimage_if_notfound)
return(msAddImageSymbol(symbols, name)); /* make sure it's not a filename */
return(-1);
}
/*
** Return the index number for a given layer based on its name.
*/
int msGetLayerIndex(mapObj *map, char *name)
{
int i;
if(!name) return(-1);
for(i=0; i<map->numlayers; i++) {
if(!GET_LAYER(map, i)->name) /* skip it */
continue;
if(strcmp(name, GET_LAYER(map, i)->name) == 0)
return(i);
}
return(-1);
}
int msGetClassIndex(layerObj *layer, char *name)
{
int i;
if(!name) return(-1);
for(i=0; i<layer->numclasses; i++) {
if(!layer->class[i]->name) /* skip it */
continue;
if(strcmp(name, layer->class[i]->name) == 0)
return(i);
}
return(-1);
}
int loadColor(colorObj *color, attributeBindingObj *binding)
{
int symbol;
char hex[2];
if(binding) {
if((symbol = getSymbol(3, MS_NUMBER, MS_BINDING, MS_STRING)) == -1) return MS_FAILURE;
} else {
if((symbol = getSymbol(2, MS_NUMBER, MS_STRING)) == -1) return MS_FAILURE;
}
color->alpha=255;
if(symbol == MS_NUMBER) {
color->red = (int) msyynumber;
if(getInteger(&(color->green)) == -1) return MS_FAILURE;
if(getInteger(&(color->blue)) == -1) return MS_FAILURE;
} else if(symbol == MS_STRING) {
int len = strlen(msyystring_buffer);
if(msyystring_buffer[0] == '#' && (len == 7 || len == 9)) { /* got a hex color w/optional alpha */
hex[0] = msyystring_buffer[1];
hex[1] = msyystring_buffer[2];
color->red = msHexToInt(hex);
hex[0] = msyystring_buffer[3];
hex[1] = msyystring_buffer[4];
color->green = msHexToInt(hex);
hex[0] = msyystring_buffer[5];
hex[1] = msyystring_buffer[6];
color->blue = msHexToInt(hex);
if(len == 9) {
hex[0] = msyystring_buffer[7];
hex[1] = msyystring_buffer[8];
color->alpha = msHexToInt(hex);
}
} else {
/* TODO: consider named colors here */
msSetError(MS_SYMERR, "Invalid hex color (%s):(line %d)", "loadColor()", msyystring_buffer, msyylineno);
return MS_FAILURE;
}
} else {
binding->item = msStrdup(msyystring_buffer);
binding->index = -1;
}
return MS_SUCCESS;
}
#if ALPHACOLOR_ENABLED
int loadColorWithAlpha(colorObj *color)
{
char hex[2];
if(getInteger(&(color->red)) == -1) {
if(msyystring_buffer[0] == '#' && strlen(msyystring_buffer) == 7) { /* got a hex color */
hex[0] = msyystring_buffer[1];
hex[1] = msyystring_buffer[2];
color->red = msHexToInt(hex);
hex[0] = msyystring_buffer[3];
hex[1] = msyystring_buffer[4];
color->green = msHexToInt(hex);
hex[0] = msyystring_buffer[5];
hex[1] = msyystring_buffer[6];
color->blue = msHexToInt(hex);
color->alpha = 0;
return(MS_SUCCESS);
} else if(msyystring_buffer[0] == '#' && strlen(msyystring_buffer) == 9) { /* got a hex color with alpha */
hex[0] = msyystring_buffer[1];
hex[1] = msyystring_buffer[2];
color->red = msHexToInt(hex);
hex[0] = msyystring_buffer[3];
hex[1] = msyystring_buffer[4];
color->green = msHexToInt(hex);
hex[0] = msyystring_buffer[5];
hex[1] = msyystring_buffer[6];
color->blue = msHexToInt(hex);
hex[0] = msyystring_buffer[7];
hex[1] = msyystring_buffer[8];
color->alpha = msHexToInt(hex);
return(MS_SUCCESS);
}
return(MS_FAILURE);
}
if(getInteger(&(color->green)) == -1) return(MS_FAILURE);
if(getInteger(&(color->blue)) == -1) return(MS_FAILURE);
if(getInteger(&(color->alpha)) == -1) return(MS_FAILURE);
return(MS_SUCCESS);
}
#endif
/*
** Helper functions for writing mapfiles.
*/
static void writeLineFeed(FILE *stream)
{
fprintf(stream, "\n");
}
static void writeIndent(FILE *stream, int indent)
{
const char *str=" "; /* change this string to define the indent */
int i;
for(i=0; i<indent; i++) fprintf(stream, "%s", str);
}
static void writeBlockBegin(FILE *stream, int indent, const char *name)
{
writeIndent(stream, indent);
fprintf(stream, "%s\n", name);
}
static void writeBlockEnd(FILE *stream, int indent, const char *name)
{
writeIndent(stream, indent);
fprintf(stream, "END # %s\n", name);
}
static void writeKeyword(FILE *stream, int indent, const char *name, int value, int size, ...)
{
va_list argp;
int i, j=0;
const char *s;
va_start(argp, size);
while (j<size) { /* check each value/keyword mapping in the list, values with no match are ignored */
i = va_arg(argp, int);
s = va_arg(argp, const char *);
if(value == i) {
writeIndent(stream, ++indent);
fprintf(stream, "%s %s\n", name, s);
va_end(argp);
return;
}
j++;
}
va_end(argp);
}
static void writeDimension(FILE *stream, int indent, const char *name, int x, int y, char *bind_x, char *bind_y)
{
writeIndent(stream, ++indent);
if(bind_x) fprintf(stream, "%s [%s] ", name, bind_x);
else fprintf(stream, "%s %d ", name, x);
if(bind_y) fprintf(stream, "[%s]\n", bind_y);
else fprintf(stream, "%d\n", y);
}
static void writeExtent(FILE *stream, int indent, const char *name, rectObj extent)
{
if(!MS_VALID_EXTENT(extent)) return;
writeIndent(stream, ++indent);
fprintf(stream, "%s %.15g %.15g %.15g %.15g\n", name, extent.minx, extent.miny, extent.maxx, extent.maxy);
}
static void writeNumber(FILE *stream, int indent, const char *name, double defaultNumber, double number)
{
if(number == defaultNumber) return; /* don't output default */
writeIndent(stream, ++indent);
fprintf(stream, "%s %g\n", name, number);
}
static void writeCharacter(FILE *stream, int indent, const char *name, const char defaultCharacter, char character)
{
if(defaultCharacter == character) return;
writeIndent(stream, ++indent);
fprintf(stream, "%s '%c'\n", name, character);
}
static void writeString(FILE *stream, int indent, const char *name, const char *defaultString, char *string)
{
char *string_tmp;
if(!string) return;
if(defaultString && strcmp(string, defaultString) == 0) return;
writeIndent(stream, ++indent);
if(name) fprintf(stream, "%s ", name);
if ( (strchr(string, '\'') == NULL) && (strchr(string, '\"') == NULL))
fprintf(stream, "\"%s\"\n", string);
else if ( (strchr(string, '\"') != NULL) && (strchr(string, '\'') == NULL))
fprintf(stream, "'%s'\n", string);
else if ( (strchr(string, '\'') != NULL) && (strchr(string, '\"') == NULL))
fprintf(stream, "\"%s\"\n", string);
else {
string_tmp = msStringEscape(string);
fprintf(stream, "\"%s\"\n", string_tmp);
free(string_tmp);
}
}
static void writeNumberOrString(FILE *stream, int indent, const char *name, double defaultNumber, double number, char *string)
{
if(string)
writeString(stream, indent, name, NULL, string);
else
writeNumber(stream, indent, name, defaultNumber, number);
}
static void writeNumberOrKeyword(FILE *stream, int indent, const char *name, double defaultNumber, double number, int value, int size, ...)
{
va_list argp;
int i, j=0;
const char *s;
va_start(argp, size);
while (j<size) { /* check each value/keyword mapping in the list */
i = va_arg(argp, int);
s = va_arg(argp, const char *);
if(value == i) {
writeIndent(stream, ++indent);
fprintf(stream, "%s %s\n", name, s);
va_end(argp);
return;
}
j++;
}
va_end(argp);
writeNumber(stream, indent, name, defaultNumber, number);
}
static void writeNameValuePair(FILE *stream, int indent, const char *name, const char *value)
{
char *string_tmp;
if(!name || !value) return;
writeIndent(stream, ++indent);
if ( (strchr(name, '\'') == NULL) && (strchr(name, '\"') == NULL))
fprintf(stream, "\"%s\"\t", name);
else if ( (strchr(name, '\"') != NULL) && (strchr(name, '\'') == NULL))
fprintf(stream, "'%s'\t", name);
else if ( (strchr(name, '\'') != NULL) && (strchr(name, '\"') == NULL))
fprintf(stream, "\"%s\"\t", name);
else {
string_tmp = msStringEscape(name);
fprintf(stream, "\"%s\"\t", string_tmp);
free(string_tmp);
}
if ( (strchr(value, '\'') == NULL) && (strchr(value, '\"') == NULL))
fprintf(stream, "\"%s\"\n", value);
else if ( (strchr(value, '\"') != NULL) && (strchr(value, '\'') == NULL))
fprintf(stream, "'%s'\n", value);
else if ( (strchr(value, '\'') != NULL) && (strchr(value, '\"') == NULL))
fprintf(stream, "\"%s\"\n", value);
else {
string_tmp = msStringEscape(value);
fprintf(stream, "\"%s\"\n", string_tmp);
free(string_tmp);
}
}
static void writeAttributeBinding(FILE *stream, int indent, const char *name, attributeBindingObj *binding)
{
if(!binding || !binding->item) return;
writeIndent(stream, ++indent);
fprintf(stream, "%s [%s]\n", name, binding->item);
}
static void writeColor(FILE *stream, int indent, const char *name, colorObj *defaultColor, colorObj *color)
{
if (!defaultColor && !MS_VALID_COLOR(*color)) return;
else if(defaultColor && MS_COMPARE_COLOR(*defaultColor, *color)) return; /* if defaultColor has the same value than the color, return.*/
writeIndent(stream, ++indent);
#if ALPHACOLOR_ENABLED
fprintf(stream, "%s %d %d %d\n", name, color->red, color->green, color->blue, color->alpha);
#else
fprintf(stream, "%s %d %d %d\n", name, color->red, color->green, color->blue);
#endif
}
/* todo: deal with alpha's... */
static void writeColorRange(FILE *stream, int indent, const char *name, colorObj *mincolor, colorObj *maxcolor)
{
if(!MS_VALID_COLOR(*mincolor) || !MS_VALID_COLOR(*maxcolor)) return;
writeIndent(stream, ++indent);
fprintf(stream, "%s %d %d %d %d %d %d\n", name, mincolor->red, mincolor->green, mincolor->blue, maxcolor->red, maxcolor->green, maxcolor->blue);
}
/*
** Initialize, load and free a single join
*/
void initJoin(joinObj *join)
{
join->numitems = 0;
join->name = NULL; /* unique join name, used for variable substitution */
join->items = NULL; /* array to hold item names for the joined table */
join->values = NULL; /* arrays of strings to holds one record worth of data */
join->table = NULL;
join->joininfo = NULL;
join->from = NULL; /* join items */
join->to = NULL;
join->header = NULL;
join->template = NULL; /* only html type templates are supported */
join->footer = NULL;
join->type = MS_JOIN_ONE_TO_ONE;
join->connection = NULL;
join->connectiontype = MS_DB_XBASE;
}
void freeJoin(joinObj *join)
{
msFree(join->name);
msFree(join->table);
msFree(join->from);
msFree(join->to);
msFree(join->header);
msFree(join->template);
msFree(join->footer);
msFreeCharArray(join->items, join->numitems); /* these may have been free'd elsewhere */
msFreeCharArray(join->values, join->numitems);
join->numitems = 0;
msJoinClose(join);
msFree(join->connection);
}
int loadJoin(joinObj *join)
{
initJoin(join);
for(;;) {
switch(msyylex()) {
case(CONNECTION):
if(getString(&join->connection) == MS_FAILURE) return(-1);
break;
case(CONNECTIONTYPE):
if((join->connectiontype = getSymbol(5, MS_DB_XBASE, MS_DB_MYSQL, MS_DB_ORACLE, MS_DB_POSTGRES, MS_DB_CSV)) == -1) return(-1);
break;
case(EOF):
msSetError(MS_EOFERR, NULL, "loadJoin()");
return(-1);
case(END):
if((join->from == NULL) || (join->to == NULL) || (join->table == NULL)) {
msSetError(MS_EOFERR, "Join must define table, name, from and to properties.", "loadJoin()");
return(-1);
}
if((join->type == MS_MULTIPLE) && ((join->template == NULL) || (join->name == NULL))) {
msSetError(MS_EOFERR, "One-to-many joins must define template and name properties.", "loadJoin()");
return(-1);
}
return(0);
case(FOOTER):
if(getString(&join->footer) == MS_FAILURE) return(-1);
break;
case(FROM):
if(getString(&join->from) == MS_FAILURE) return(-1);
break;
case(HEADER):
if(getString(&join->header) == MS_FAILURE) return(-1);
break;
case(JOIN):
break; /* for string loads */
case(NAME):
if(getString(&join->name) == MS_FAILURE) return(-1);
break;
case(TABLE):
if(getString(&join->table) == MS_FAILURE) return(-1);
break;
case(TEMPLATE):
if(getString(&join->template) == MS_FAILURE) return(-1);
break;
case(TO):
if(getString(&join->to) == MS_FAILURE) return(-1);
break;
case(TYPE):
if((join->type = getSymbol(2, MS_JOIN_ONE_TO_ONE, MS_JOIN_ONE_TO_MANY)) == -1) return(-1);
break;
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadJoin()", msyystring_buffer, msyylineno);
return(-1);
}
} /* next token */
}
static void writeJoin(FILE *stream, int indent, joinObj *join)
{
indent++;
writeBlockBegin(stream, indent, "JOIN");
writeString(stream, indent, "FOOTER", NULL, join->footer);
writeString(stream, indent, "FROM", NULL, join->from);
writeString(stream, indent, "HEADER", NULL, join->header);
writeString(stream, indent, "NAME", NULL, join->name);
writeString(stream, indent, "TABLE", NULL, join->table);
writeString(stream, indent, "TEMPLATE", NULL, join->template);
writeString(stream, indent, "TO", NULL, join->to);
writeKeyword(stream, indent, "CONNECTIONTYPE", join->connectiontype, 3, MS_DB_CSV, "CSV", MS_DB_POSTGRES, "POSTRESQL", MS_DB_MYSQL, "MYSQL");
writeKeyword(stream, indent, "TYPE", join->type, 1, MS_JOIN_ONE_TO_MANY, "ONE-TO-MANY");
writeBlockEnd(stream, indent, "JOIN");
}
/* inserts a feature at the end of the list, can create a new list */
featureListNodeObjPtr insertFeatureList(featureListNodeObjPtr *list, shapeObj *shape)
{
featureListNodeObjPtr node;
node = (featureListNodeObjPtr) malloc(sizeof(featureListNodeObj));
MS_CHECK_ALLOC(node, sizeof(featureListNodeObj), NULL);
msInitShape(&(node->shape));
if(msCopyShape(shape, &(node->shape)) == -1) return(NULL);
/* AJS - alans@wunderground.com O(n^2) -> O(n) conversion, keep a pointer to the end */
/* set the tailifhead to NULL, since it is only set for the head of the list */
node->tailifhead = NULL;
node->next = NULL;
/* if we are at the head of the list, we need to set the list to node, before pointing tailifhead somewhere */
if(*list == NULL) {
*list=node;
} else {
if((*list)->tailifhead!=NULL) /* this should never be NULL, but just in case */
(*list)->tailifhead->next=node; /* put the node at the end of the list */
}
/* repoint the head of the list to the end - our new element
this causes a loop if we are at the head, be careful not to
walk in a loop */
(*list)->tailifhead = node;
return(node); /* a pointer to last object in the list */
}
void freeFeatureList(featureListNodeObjPtr list)
{
featureListNodeObjPtr listNext = NULL;
while (list!=NULL) {
listNext = list->next;
msFreeShape(&(list->shape));
msFree(list);
list = listNext;
}
}
/* lineObj = multipointObj */
static int loadFeaturePoints(lineObj *points)
{
int buffer_size=0;
points->point = (pointObj *)malloc(sizeof(pointObj)*MS_FEATUREINITSIZE);
MS_CHECK_ALLOC(points->point, sizeof(pointObj)*MS_FEATUREINITSIZE, MS_FAILURE);
points->numpoints = 0;
buffer_size = MS_FEATUREINITSIZE;
for(;;) {
switch(msyylex()) {
case(EOF):
msSetError(MS_EOFERR, NULL, "loadFeaturePoints()");
return(MS_FAILURE);
case(END):
return(MS_SUCCESS);
case(MS_NUMBER):
if(points->numpoints == buffer_size) { /* just add it to the end */
points->point = (pointObj *) realloc(points->point, sizeof(pointObj)*(buffer_size+MS_FEATUREINCREMENT));
MS_CHECK_ALLOC(points->point, sizeof(pointObj)*(buffer_size+MS_FEATUREINCREMENT), MS_FAILURE);
buffer_size+=MS_FEATUREINCREMENT;
}
points->point[points->numpoints].x = atof(msyystring_buffer);
if(getDouble(&(points->point[points->numpoints].y)) == -1) return(MS_FAILURE);
points->numpoints++;
break;
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadFeaturePoints()", msyystring_buffer, msyylineno );
return(MS_FAILURE);
}
}
}
static int loadFeature(layerObj *player, int type)
{
int status=MS_SUCCESS;
featureListNodeObjPtr *list = &(player->features);
featureListNodeObjPtr node;
multipointObj points= {0,NULL};
shapeObj *shape=NULL;
shape = (shapeObj *) malloc(sizeof(shapeObj));
MS_CHECK_ALLOC(shape, sizeof(shapeObj), MS_FAILURE);
msInitShape(shape);
shape->type = type;
for(;;) {
switch(msyylex()) {
case(EOF):
msSetError(MS_EOFERR, NULL, "loadFeature()");
return(MS_FAILURE);
case(END):
if(player->features != NULL && player->features->tailifhead != NULL)
shape->index = player->features->tailifhead->shape.index + 1;
else
shape->index = 0;
if((node = insertFeatureList(list, shape)) == NULL)
status = MS_FAILURE;
msFreeShape(shape); /* clean up */
msFree(shape);
return(status);
case(FEATURE):
break; /* for string loads */
case(POINTS):
if(loadFeaturePoints(&points) == MS_FAILURE) return(MS_FAILURE); /* no clean up necessary, just return */
status = msAddLine(shape, &points);
msFree(points.point); /* clean up */
points.numpoints = 0;
if(status == MS_FAILURE) return(MS_FAILURE);
break;
case(ITEMS): {
char *string=NULL;
if(getString(&string) == MS_FAILURE) return(MS_FAILURE);
if (string) {
if(shape->values) msFreeCharArray(shape->values, shape->numvalues);
shape->values = msStringSplit(string, ';', &shape->numvalues);
msFree(string); /* clean up */
}
break;
}
case(TEXT):
if(getString(&shape->text) == MS_FAILURE) return(MS_FAILURE);
break;
case(WKT): {
char *string=NULL;
/* todo, what do we do with multiple WKT property occurances? */
if(getString(&string) == MS_FAILURE) return(MS_FAILURE);
if((shape = msShapeFromWKT(string)) == NULL)
status = MS_FAILURE;
msFree(string); /* clean up */
if(status == MS_FAILURE) return(MS_FAILURE);
break;
}
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadfeature()", msyystring_buffer, msyylineno);
return(MS_FAILURE);
}
} /* next token */
}
static void writeFeature(FILE *stream, int indent, shapeObj *feature)
{
int i,j;
indent++;
writeBlockBegin(stream, indent, "FEATURE");
indent++;
for(i=0; i<feature->numlines; i++) {
writeBlockBegin(stream, indent, "POINTS");
for(j=0; j<feature->line[i].numpoints; j++) {
writeIndent(stream, indent);
fprintf(stream, "%.15g %.15g\n", feature->line[i].point[j].x, feature->line[i].point[j].y);
}
writeBlockEnd(stream, indent, "POINTS");
}
indent--;
if (feature->numvalues) {
writeIndent(stream, indent);
fprintf(stream, "ITEMS \"");
for (i=0; i<feature->numvalues; i++) {
if (i == 0)
fprintf(stream, "%s", feature->values[i]);
else
fprintf(stream, ";%s", feature->values[i]);
}
fprintf(stream, "\"\n");
}
writeString(stream, indent, "TEXT", NULL, feature->text);
writeBlockEnd(stream, indent, "FEATURE");
}
void initGrid( graticuleObj *pGraticule )
{
memset( pGraticule, 0, sizeof( graticuleObj ) );
}
static int loadGrid( layerObj *pLayer )
{
for(;;) {
switch(msyylex()) {
case(EOF):
msSetError(MS_EOFERR, NULL, "loadGrid()");
return(-1);
case(END):
return(0);
case(GRID):
break; /* for string loads */
case( LABELFORMAT ):
if(getString(&((graticuleObj *)pLayer->layerinfo)->labelformat) == MS_FAILURE) {
if(strcasecmp(msyystring_buffer, "DD") == 0) /* DD triggers a symbol to be returned instead of a string so check for this special case */
((graticuleObj *)pLayer->layerinfo)->labelformat = msStrdup("DD");
else
return(-1);
}
break;
case( MINARCS ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->minarcs) == -1)
return(-1);
break;
case( MAXARCS ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->maxarcs) == -1)
return(-1);
break;
case( MININTERVAL ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->minincrement) == -1)
return(-1);
break;
case( MAXINTERVAL ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->maxincrement) == -1)
return(-1);
break;
case( MINSUBDIVIDE ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->minsubdivides) == -1)
return(-1);
break;
case( MAXSUBDIVIDE ):
if(getDouble(&((graticuleObj *)pLayer->layerinfo)->maxsubdivides) == -1)
return(-1);
break;
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadGrid()", msyystring_buffer, msyylineno);
return(-1);
}
}
}
static void writeGrid(FILE *stream, int indent, graticuleObj *pGraticule)
{
if(!pGraticule) return;
indent++;
writeBlockBegin(stream, indent, "GRID");
writeString(stream, indent, "LABELFORMAT", NULL, pGraticule->labelformat);
writeNumber(stream, indent, "MAXARCS", 0, pGraticule->maxarcs);
writeNumber(stream, indent, "MAXSUBDIVIDE", 0, pGraticule->maxsubdivides);
writeNumber(stream, indent, "MAXINTERVAL", 0, pGraticule->maxincrement);
writeNumber(stream, indent, "MINARCS", 0, pGraticule->minarcs);
writeNumber(stream, indent, "MININTERVAL", 0, pGraticule->minincrement);
writeNumber(stream, indent, "MINSUBDIVIDE", 0, pGraticule->minsubdivides);
writeBlockEnd(stream, indent, "GRID");
}
/*
** Initialize, load and free a projectionObj structure
*/
int msInitProjection(projectionObj *p)
{
p->gt.need_geotransform = MS_FALSE;
p->numargs = 0;
p->args = NULL;
p->wellknownprojection = wkp_none;
#ifdef USE_PROJ
p->proj = NULL;
p->args = (char **)malloc(MS_MAXPROJARGS*sizeof(char *));
MS_CHECK_ALLOC(p->args, MS_MAXPROJARGS*sizeof(char *), -1);
#if PJ_VERSION >= 480
p->proj_ctx = NULL;
#endif
#endif
return(0);
}
void msFreeProjection(projectionObj *p)
{
#ifdef USE_PROJ
if(p->proj) {
pj_free(p->proj);
p->proj = NULL;
}
#if PJ_VERSION >= 480
if(p->proj_ctx) {
pj_ctx_free(p->proj_ctx);
p->proj_ctx = NULL;
}
#endif
msFreeCharArray(p->args, p->numargs);
p->args = NULL;
p->numargs = 0;
#endif
}
/*
** Handle OGC WMS/WFS AUTO projection in the format:
** "AUTO:proj_id,units_id,lon0,lat0"
*/
#ifdef USE_PROJ
static int _msProcessAutoProjection(projectionObj *p)
{
char **args;
int numargs, nProjId, nUnitsId, nZone;
double dLat0, dLon0;
const char *pszUnits = "m";
char szProjBuf[512]="";
/* WMS/WFS AUTO projection: "AUTO:proj_id,units_id,lon0,lat0" */
args = msStringSplit(p->args[0], ',', &numargs);
if (numargs != 4 ||
(strncasecmp(args[0], "AUTO:", 5) != 0 &&
strncasecmp(args[0], "AUTO2:", 6) != 0)) {
msSetError(MS_PROJERR,
"WMS/WFS AUTO/AUTO2 PROJECTION must be in the format "
"'AUTO:proj_id,units_id,lon0,lat0' or 'AUTO2:crs_id,factor,lon0,lat0'(got '%s').\n",
"_msProcessAutoProjection()", p->args[0]);
return -1;
}
if (strncasecmp(args[0], "AUTO:", 5)==0)
nProjId = atoi(args[0]+5);
else
nProjId = atoi(args[0]+6);
nUnitsId = atoi(args[1]);
dLon0 = atof(args[2]);
dLat0 = atof(args[3]);
/*There is no unit parameter for AUTO2. The 2nd parameter is
factor. Set the units to always be meter*/
if (strncasecmp(args[0], "AUTO2:", 6) == 0)
nUnitsId = 9001;
msFreeCharArray(args, numargs);
/* Handle EPSG Units. Only meters for now. */
switch(nUnitsId) {
case 9001: /* Meters */
pszUnits = "m";
break;
default:
msSetError(MS_PROJERR,
"WMS/WFS AUTO PROJECTION: EPSG Units %d not supported.\n",
"_msProcessAutoProjection()", nUnitsId);
return -1;
}
/* Build PROJ4 definition.
* This is based on the definitions found in annex E of the WMS 1.1.1
* spec and online at http://www.digitalearth.org/wmt/auto.html
* The conversion from the original WKT definitions to PROJ4 format was
* done using the MapScript setWKTProjection() function (based on OGR).
*/
switch(nProjId) {
case 42001: /** WGS 84 / Auto UTM **/
nZone = (int) floor( (dLon0 + 180.0) / 6.0 ) + 1;
sprintf( szProjBuf,
"+proj=tmerc+lat_0=0+lon_0=%.16g+k=0.999600+x_0=500000"
"+y_0=%.16g+ellps=WGS84+datum=WGS84+units=%s",
-183.0 + nZone * 6.0,
(dLat0 >= 0.0) ? 0.0 : 10000000.0,
pszUnits);
break;
case 42002: /** WGS 84 / Auto Tr. Mercator **/
sprintf( szProjBuf,
"+proj=tmerc+lat_0=0+lon_0=%.16g+k=0.999600+x_0=500000"
"+y_0=%.16g+ellps=WGS84+datum=WGS84+units=%s",
dLon0,
(dLat0 >= 0.0) ? 0.0 : 10000000.0,
pszUnits);
break;
case 42003: /** WGS 84 / Auto Orthographic **/
sprintf( szProjBuf,
"+proj=ortho+lon_0=%.16g+lat_0=%.16g+x_0=0+y_0=0"
"+ellps=WGS84+datum=WGS84+units=%s",
dLon0, dLat0, pszUnits );
break;
case 42004: /** WGS 84 / Auto Equirectangular **/
/* Note that we have to pass lon_0 as lon_ts for this one to */
/* work. Either a PROJ4 bug or a PROJ4 documentation issue. */
sprintf( szProjBuf,
"+proj=eqc+lon_ts=%.16g+lat_ts=%.16g+x_0=0+y_0=0"
"+ellps=WGS84+datum=WGS84+units=%s",
dLon0, dLat0, pszUnits);
break;
case 42005: /** WGS 84 / Auto Mollweide **/
sprintf( szProjBuf,
"+proj=moll+lon_0=%.16g+x_0=0+y_0=0+ellps=WGS84"
"+datum=WGS84+units=%s",
dLon0, pszUnits);
break;
default:
msSetError(MS_PROJERR,
"WMS/WFS AUTO PROJECTION %d not supported.\n",
"_msProcessAutoProjection()", nProjId);
return -1;
}
/* msDebug("%s = %s\n", p->args[0], szProjBuf); */
/* OK, pass the definition to pj_init() */
args = msStringSplit(szProjBuf, '+', &numargs);
msAcquireLock( TLOCK_PROJ );
if( !(p->proj = pj_init(numargs, args)) ) {
int *pj_errno_ref = pj_get_errno_ref();
msReleaseLock( TLOCK_PROJ );
msSetError(MS_PROJERR, "proj error \"%s\" for \"%s\"",
"msProcessProjection()", pj_strerrno(*pj_errno_ref), szProjBuf) ;
return(-1);
}
msReleaseLock( TLOCK_PROJ );
msFreeCharArray(args, numargs);
return(0);
}
#endif /* USE_PROJ */
int msProcessProjection(projectionObj *p)
{
#ifdef USE_PROJ
assert( p->proj == NULL );
if( strcasecmp(p->args[0],"GEOGRAPHIC") == 0 ) {
msSetError(MS_PROJERR,
"PROJECTION 'GEOGRAPHIC' no longer supported.\n"
"Provide explicit definition.\n"
"ie. proj=latlong\n"
" ellps=clrk66\n",
"msProcessProjection()");
return(-1);
}
if (strcasecmp(p->args[0], "AUTO") == 0) {
p->proj = NULL;
return 0;
}
if (strncasecmp(p->args[0], "AUTO:", 5) == 0 ||
strncasecmp(p->args[0], "AUTO2:", 6) == 0) {
/* WMS/WFS AUTO projection: "AUTO:proj_id,units_id,lon0,lat0" */
/*WMS 1.3.0: AUTO2:auto_crs_id,factor,lon0,lat0*/
return _msProcessAutoProjection(p);
}
msAcquireLock( TLOCK_PROJ );
#if PJ_VERSION < 480
if( !(p->proj = pj_init(p->numargs, p->args)) ) {
#else
p->proj_ctx = pj_ctx_alloc();
if( !(p->proj=pj_init_ctx(p->proj_ctx, p->numargs, p->args)) ) {
#endif
int *pj_errno_ref = pj_get_errno_ref();
msReleaseLock( TLOCK_PROJ );
if(p->numargs>1) {
msSetError(MS_PROJERR, "proj error \"%s\" for \"%s:%s\"",
"msProcessProjection()", pj_strerrno(*pj_errno_ref), p->args[0],p->args[1]) ;
} else {
msSetError(MS_PROJERR, "proj error \"%s\" for \"%s\"",
"msProcessProjection()", pj_strerrno(*pj_errno_ref), p->args[0]) ;
}
return(-1);
}
msReleaseLock( TLOCK_PROJ );
#ifdef USE_PROJ_FASTPATHS
if(strcasestr(p->args[0],"epsg:4326")) {
p->wellknownprojection = wkp_lonlat;
} else if(strcasestr(p->args[0],"epsg:3857")) {
p->wellknownprojection = wkp_gmerc;
} else {
p->wellknownprojection = wkp_none;
}
#endif
return(0);
#else
msSetError(MS_PROJERR, "Projection support is not available.",
"msProcessProjection()");
return(-1);
#endif
}
static int loadProjection(projectionObj *p)
{
#ifdef USE_PROJ
int i=0;
#endif
p->gt.need_geotransform = MS_FALSE;
#ifdef USE_PROJ
if ( p->proj != NULL ) {
msSetError(MS_MISCERR, "Projection is already initialized. Multiple projection definitions are not allowed in this object. (line %d)",
"loadProjection()", msyylineno);
return(-1);
}
for(;;) {
switch(msyylex()) {
case(EOF):
msSetError(MS_EOFERR, NULL, "loadProjection()");
return(-1);
case(END):
if( i == 1 && strstr(p->args[0],"+") != NULL ) {
char *one_line_def = p->args[0];
int result;
p->args[0] = NULL;
result = msLoadProjectionString( p, one_line_def );
free( one_line_def );
return result;
} else {
p->numargs = i;
if(p->numargs != 0)
return msProcessProjection(p);
else
return 0;
}
break;
case(MS_STRING):
case(MS_AUTO):
p->args[i] = msStrdup(msyystring_buffer);
p->automatic = MS_TRUE;
i++;
break;
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadProjection()",
msyystring_buffer, msyylineno);
return(-1);
}
} /* next token */
#else
msSetError(MS_PROJERR, "Projection support is not available.", "loadProjection()");
return(-1);
#endif
}
/************************************************************************/
/* msLoadProjectionStringEPSG */
/* */
/* Checks fro EPSG type projection and set the axes for a */
/* certain code ranges. */
/* Use for now in WMS 1.3.0 */
/************************************************************************/
int msLoadProjectionStringEPSG(projectionObj *p, const char *value)
{
#ifdef USE_PROJ
if(p) msFreeProjection(p);
p->gt.need_geotransform = MS_FALSE;
#ifdef USE_PROJ_FASTPATHS
if(strcasestr(value,"epsg:4326")) {
p->wellknownprojection = wkp_lonlat;
} else if(strcasestr(value,"epsg:3857")) {
p->wellknownprojection = wkp_gmerc;
} else {
p->wellknownprojection = wkp_none;
}
#endif
if (strncasecmp(value, "EPSG:", 5) == 0) {
size_t buffer_size = 10 + strlen(value+5) + 1;
char *init_string = (char*)msSmallMalloc(buffer_size);
/* translate into PROJ.4 format. */
snprintf(init_string, buffer_size, "init=epsg:%s", value+5 );
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = init_string;
p->numargs = 1;
if( msIsAxisInverted(atoi(value+5))) {
p->args[1] = msStrdup("+epsgaxis=ne");
p->numargs = 2;
}
return msProcessProjection( p );
}
return msLoadProjectionString(p, value);
#else
msSetError(MS_PROJERR, "Projection support is not available.",
"msLoadProjectionStringEPSG()");
return(-1);
#endif
}
int msLoadProjectionString(projectionObj *p, const char *value)
{
p->gt.need_geotransform = MS_FALSE;
#ifdef USE_PROJ
if(p) msFreeProjection(p);
/*
* Handle new style definitions, the same as they would be given to
* the proj program.
* eg.
* "+proj=utm +zone=11 +ellps=WGS84"
*/
if( value[0] == '+' ) {
char *trimmed;
int i, i_out=0;
trimmed = msStrdup(value+1);
for( i = 1; value[i] != '\0'; i++ ) {
if( !isspace( value[i] ) )
trimmed[i_out++] = value[i];
}
trimmed[i_out] = '\0';
p->args = msStringSplit(trimmed,'+', &p->numargs);
free( trimmed );
} else if (strncasecmp(value, "AUTO:", 5) == 0 ||
strncasecmp(value, "AUTO2:", 6) == 0) {
/* WMS/WFS AUTO projection: "AUTO:proj_id,units_id,lon0,lat0" */
/* WMS 1.3.0 projection: "AUTO2:auto_crs_id,factor,lon0,lat0"*/
/* Keep the projection defn into a single token for writeProjection() */
/* to work fine. */
p->args = (char**)msSmallMalloc(sizeof(char*));
p->args[0] = msStrdup(value);
p->numargs = 1;
} else if (strncasecmp(value, "EPSG:", 5) == 0) {
size_t buffer_size = 10 + strlen(value+5) + 1;
char *init_string = (char*)msSmallMalloc(buffer_size);
/* translate into PROJ.4 format. */
snprintf( init_string, buffer_size, "init=epsg:%s", value+5);
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = init_string;
p->numargs = 1;
} else if (strncasecmp(value, "urn:ogc:def:crs:EPSG:",21) == 0) {
/* this is very preliminary urn support ... expand later */
size_t buffer_size = 0;
char *init_string = NULL;
const char *code;
code = value + 21;
while( *code != ':' && *code != '\0' )
code++;
if( *code == ':' )
code++;
buffer_size = 10 + strlen(code) + 1;
init_string = (char*)msSmallMalloc(buffer_size);
/* translate into PROJ.4 format. */
snprintf( init_string, buffer_size, "init=epsg:%s", code );
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = init_string;
p->numargs = 1;
if( msIsAxisInverted(atoi(code))) {
p->args[1] = msStrdup("+epsgaxis=ne");
p->numargs = 2;
}
} else if (strncasecmp(value, "urn:x-ogc:def:crs:EPSG:",23) == 0) {
/*this case is to account for OGC CITE tests where x-ogc was used
before the ogc name became an official NID. Note also we also account
for the fact that a space for the version of the espg is not used with CITE tests.
(Syntax used could be urn:ogc:def:objectType:authority:code)*/
size_t buffer_size = 0;
char *init_string = NULL;
const char *code;
if (value[23] == ':')
code = value + 23;
else
code = value + 22;
while( *code != ':' && *code != '\0' )
code++;
if( *code == ':' )
code++;
buffer_size = 10 + strlen(code) + 1;
init_string = (char*)msSmallMalloc(buffer_size);
/* translate into PROJ.4 format. */
snprintf( init_string, buffer_size, "init=epsg:%s", code );
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = init_string;
p->numargs = 1;
if( msIsAxisInverted(atoi(code))) {
p->args[1] = msStrdup("+epsgaxis=ne");
p->numargs = 2;
}
} else if (strncasecmp(value, "urn:ogc:def:crs:OGC:",20) == 0 ) {
/* this is very preliminary urn support ... expand later */
char init_string[100];
const char *id;
id = value + 20;
while( *id != ':' && *id == '\0' )
id++;
if( *id == ':' )
id++;
init_string[0] = '\0';
if( strcasecmp(id,"CRS84") == 0 )
strncpy( init_string, "init=epsg:4326", sizeof(init_string) );
else if( strcasecmp(id,"CRS83") == 0 )
strncpy( init_string, "init=epsg:4269", sizeof(init_string) );
else if( strcasecmp(id,"CRS27") == 0 )
strncpy( init_string, "init=epsg:4267", sizeof(init_string) );
else {
msSetError( MS_PROJERR,
"Unrecognised OGC CRS def '%s'.",
"msLoadProjectionString()",
value );
return -1;
}
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = msStrdup(init_string);
p->numargs = 1;
}
/* URI projection support */
else if (EQUALN("http://www.opengis.net/def/crs/EPSG/", value, 36)) {
/* this is very preliminary urn support ... expand later */
char init_string[100];
const char *code;
code = value + 36;
while( *code != '/' && *code != '\0' )
code++;
if( *code == '/' )
code++;
/* translate into PROJ.4 format. */
snprintf( init_string, sizeof(init_string), "init=epsg:%s", code );
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = msStrdup(init_string);
p->numargs = 1;
if( msIsAxisInverted(atoi(code))) {
p->args[1] = msStrdup("+epsgaxis=ne");
p->numargs = 2;
}
} else if (EQUALN("http://www.opengis.net/def/crs/OGC/", value, 35) ) {
char init_string[100];
const char *id;
id = value + 35;
while( *id != '/' && *id == '\0' )
id++;
if( *id == '/' )
id++;
init_string[0] = '\0';
if( strcasecmp(id,"CRS84") == 0 )
strncpy( init_string, "init=epsg:4326", sizeof(init_string) );
else if( strcasecmp(id,"CRS83") == 0 )
strncpy( init_string, "init=epsg:4269", sizeof(init_string) );
else if( strcasecmp(id,"CRS27") == 0 )
strncpy( init_string, "init=epsg:4267", sizeof(init_string) );
else {
msSetError( MS_PROJERR,
"Unrecognised OGC CRS def '%s'.",
"msLoadProjectionString()",
value );
return -1;
}
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = msStrdup(init_string);
p->numargs = 1;
} else if (strncasecmp(value, "CRS:",4) == 0 ) {
char init_string[100];
init_string[0] = '\0';
if (atoi(value+4) == 84)
strncpy( init_string, "init=epsg:4326", sizeof(init_string) );
else if (atoi(value+4) == 83)
strncpy( init_string, "init=epsg:4269", sizeof(init_string) );
else if (atoi(value+4) == 27)
strncpy( init_string, "init=epsg:4267", sizeof(init_string) );
else {
msSetError( MS_PROJERR,
"Unrecognised OGC CRS def '%s'.",
"msLoadProjectionString()",
value );
return -1;
}
p->args = (char**)msSmallMalloc(sizeof(char*) * 2);
p->args[0] = msStrdup(init_string);
p->numargs = 1;
}
/*
* Handle old style comma delimited. eg. "proj=utm,zone=11,ellps=WGS84".
*/
else {
p->args = msStringSplit(value,',', &p->numargs);
}
return msProcessProjection( p );
#else
msSetError(MS_PROJERR, "Projection support is not available.",
"msLoadProjectionString()");
return(-1);
#endif
}
static void writeProjection(FILE *stream, int indent, projectionObj *p)
{
#ifdef USE_PROJ
int i;
if(!p || p->numargs <= 0) return;
indent++;
writeBlockBegin(stream, indent, "PROJECTION");
for(i=0; i<p->numargs; i++)
writeString(stream, indent, NULL, NULL, p->args[i]);
writeBlockEnd(stream, indent, "PROJECTION");
#endif
}
void initLeader(labelLeaderObj *leader)
{
leader->gridstep = 5;
leader->maxdistance = 0;
/* Set maxstyles = 0, styles[] will be allocated as needed on first call
* to msGrowLabelLeaderStyles()
*/
leader->numstyles = leader->maxstyles = 0;
leader->styles = NULL;
}
/*
** Initialize, load and free a labelObj structure
*/
void initLabel(labelObj *label)
{
int i;
MS_REFCNT_INIT(label);
label->antialias = -1; /* off */
label->align = MS_ALIGN_LEFT;
MS_INIT_COLOR(label->color, 0,0,0,255);
MS_INIT_COLOR(label->outlinecolor, -1,-1,-1,255); /* don't use it */
label->outlinewidth=1;
MS_INIT_COLOR(label->shadowcolor, -1,-1,-1,255); /* don't use it */
label->shadowsizex = label->shadowsizey = 1;
label->font = NULL;
label->type = MS_BITMAP;
label->size = MS_MEDIUM;
label->position = MS_CC;
label->angle = 0;
label->anglemode = MS_NONE;
label->minsize = MS_MINFONTSIZE;
label->maxsize = MS_MAXFONTSIZE;
label->buffer = 0;
label->offsetx = label->offsety = 0;
label->minscaledenom=-1;
label->maxscaledenom=-1;
label->minfeaturesize = -1; /* no limit */
label->autominfeaturesize = MS_FALSE;
label->mindistance = -1; /* no limit */
label->repeatdistance = 0; /* no repeat */
label->maxoverlapangle = 22.5; /* default max overlap angle */
label->partials = MS_TRUE;
label->wrap = '\0';
label->maxlength = 0;
label->minlength = 0;
label->space_size_10=0.0;
label->encoding = NULL;
label->force = MS_OFF;
label->priority = MS_DEFAULT_LABEL_PRIORITY;
/* Set maxstyles = 0, styles[] will be allocated as needed on first call
* to msGrowLabelStyles()
*/
label->numstyles = label->maxstyles = 0;
label->styles = NULL;
label->numbindings = 0;
for(i=0; i<MS_LABEL_BINDING_LENGTH; i++) {
label->bindings[i].item = NULL;
label->bindings[i].index = -1;
}
label->status = MS_ON;
initExpression(&(label->expression));
initExpression(&(label->text));
label->annotext = NULL;
label->annopoly = NULL;
initLeader(&(label->leader));
return;
}
static int freeLabelLeader(labelLeaderObj *leader)
{
int i;
for(i=0; i<leader->numstyles; i++) {
msFree(leader->styles[i]);
}
msFree(leader->styles);
return MS_SUCCESS;
}
int freeLabel(labelObj *label)
{
int i;
if( MS_REFCNT_DECR_IS_NOT_ZERO(label) ) {
return MS_FAILURE;
}
msFree(label->font);
msFree(label->encoding);
for(i=0; i<label->numstyles; i++) { /* each style */
if(label->styles[i]!=NULL) {
if(freeStyle(label->styles[i]) == MS_SUCCESS) {
msFree(label->styles[i]);
}
}
}
msFree(label->styles);
for(i=0; i<MS_LABEL_BINDING_LENGTH; i++)
msFree(label->bindings[i].item);
freeExpression(&(label->expression));
freeExpression(&(label->text));
/* free book keeping vars associated with the label cache */
msFree(label->annotext);
if(label->annopoly) {
msFreeShape(label->annopoly);
msFree(label->annopoly);
}
freeLabelLeader(&(label->leader));
return MS_SUCCESS;
}
static int loadLeader(labelLeaderObj *leader)
{
for(;;) {
switch(msyylex()) {
case(END):
return(0);
break;
case(EOF):
msSetError(MS_EOFERR, NULL, "loadLeader()");
return(-1);
case GRIDSTEP:
if(getInteger(&(leader->gridstep)) == -1) return(-1);
break;
case MAXDISTANCE:
if(getInteger(&(leader->maxdistance)) == -1) return(-1);
break;
case STYLE:
if(msGrowLeaderStyles(leader) == NULL)
return(-1);
initStyle(leader->styles[leader->numstyles]);
if(loadStyle(leader->styles[leader->numstyles]) != MS_SUCCESS) return(-1);
leader->numstyles++;
break;
default:
if(strlen(msyystring_buffer) > 0) {
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadLeader()", msyystring_buffer, msyylineno);
return(-1);
} else {
return(0); /* end of a string, not an error */
}
}
}
}
static int loadLabel(labelObj *label)
{
int symbol;
for(;;) {
switch(msyylex()) {
case(ANGLE):
if((symbol = getSymbol(5, MS_NUMBER,MS_AUTO,MS_AUTO2,MS_FOLLOW,MS_BINDING)) == -1)
return(-1);
if(symbol == MS_NUMBER)
label->angle = msyynumber;
else if(symbol == MS_BINDING) {
if (label->bindings[MS_LABEL_BINDING_ANGLE].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_ANGLE].item);
label->bindings[MS_LABEL_BINDING_ANGLE].item = msStrdup(msyystring_buffer);
label->numbindings++;
} else if ( symbol == MS_FOLLOW ) {
label->anglemode = MS_FOLLOW;
} else if ( symbol == MS_AUTO2 ) {
label->anglemode = MS_AUTO2;
} else
label->anglemode = MS_AUTO;
break;
case(ALIGN):
if((label->align = getSymbol(3, MS_ALIGN_LEFT,MS_ALIGN_CENTER,MS_ALIGN_RIGHT)) == -1) return(-1);
break;
case(ANTIALIAS):
if((label->antialias = getSymbol(2, MS_TRUE,MS_FALSE)) == -1)
return(-1);
break;
case(BUFFER):
if(getInteger(&(label->buffer)) == -1) return(-1);
break;
case(COLOR):
if(loadColor(&(label->color), &(label->bindings[MS_LABEL_BINDING_COLOR])) != MS_SUCCESS) return(-1);
if(label->bindings[MS_LABEL_BINDING_COLOR].item) label->numbindings++;
break;
case(ENCODING):
if((getString(&label->encoding)) == MS_FAILURE) return(-1);
break;
case(END):
/* sanity check */
if(label->anglemode == MS_FOLLOW && label->type == MS_BITMAP) {
msSetError(MS_MISCERR,"Follow labels not supported with bitmap fonts.", "loadLabel()");
return -1;
}
return(0);
break;
case(EOF):
msSetError(MS_EOFERR, NULL, "loadLabel()");
return(-1);
case(EXPRESSION):
if(loadExpression(&(label->expression)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */
if(msyysource == MS_URL_TOKENS) {
msSetError(MS_MISCERR, "URL-based EXPRESSION configuration not supported." , "loadLabel()");
freeExpression(&(label->expression));
return(-1);
}
break;
case(FONT):
if((symbol = getSymbol(2, MS_STRING, MS_BINDING)) == -1)
return(-1);
if(symbol == MS_STRING) {
if (label->font != NULL)
msFree(label->font);
label->font = msStrdup(msyystring_buffer);
} else {
if (label->bindings[MS_LABEL_BINDING_FONT].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_FONT].item);
label->bindings[MS_LABEL_BINDING_FONT].item = msStrdup(msyystring_buffer);
label->numbindings++;
}
break;
case(FORCE):
switch(msyylex()) {
case MS_ON:
label->force = MS_ON;
break;
case MS_OFF:
label->force = MS_OFF;
break;
case GROUP:
label->force = MS_LABEL_FORCE_GROUP;
break;
default:
msSetError(MS_MISCERR, "Invalid FORCE, must be ON,OFF,or GROUP" , "loadLabel()");
return(-1);
}
break;
case(LABEL):
break; /* for string loads */
case(LEADER):
msSetError(MS_MISCERR, "LABEL LEADER not implemented. LEADER goes at the CLASS level." , "loadLabel()");
return(-1);
if(loadLeader(&(label->leader)) == -1) return(-1);
break;
case(MAXSIZE):
if(getDouble(&(label->maxsize)) == -1) return(-1);
break;
case(MAXSCALEDENOM):
if(getDouble(&(label->maxscaledenom)) == -1) return(-1);
break;
case(MAXLENGTH):
if(getInteger(&(label->maxlength)) == -1) return(-1);
break;
case(MINLENGTH):
if(getInteger(&(label->minlength)) == -1) return(-1);
break;
case(MINDISTANCE):
if(getInteger(&(label->mindistance)) == -1) return(-1);
break;
case(REPEATDISTANCE):
if(getInteger(&(label->repeatdistance)) == -1) return(-1);
break;
case(MAXOVERLAPANGLE):
if(getDouble(&(label->maxoverlapangle)) == -1) return(-1);
break;
case(MINFEATURESIZE):
if((symbol = getSymbol(2, MS_NUMBER,MS_AUTO)) == -1) return(-1);
if(symbol == MS_NUMBER)
label->minfeaturesize = (int)msyynumber;
else
label->autominfeaturesize = MS_TRUE;
break;
case(MINSCALEDENOM):
if(getDouble(&(label->minscaledenom)) == -1) return(-1);
break;
case(MINSIZE):
if(getDouble(&(label->minsize)) == -1) return(-1);
break;
case(OFFSET):
if(getInteger(&(label->offsetx)) == -1) return(-1);
if(getInteger(&(label->offsety)) == -1) return(-1);
break;
case(OUTLINECOLOR):
if(loadColor(&(label->outlinecolor), &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR])) != MS_SUCCESS) return(-1);
if(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item) label->numbindings++;
break;
case(OUTLINEWIDTH):
if(getInteger(&(label->outlinewidth)) == -1) return(-1);
break;
case(PARTIALS):
if((label->partials = getSymbol(2, MS_TRUE,MS_FALSE)) == -1) return(-1);
break;
case(POSITION):
if((label->position = getSymbol(11, MS_UL,MS_UC,MS_UR,MS_CL,MS_CC,MS_CR,MS_LL,MS_LC,MS_LR,MS_AUTO,MS_BINDING)) == -1)
return(-1);
if(label->position == MS_BINDING) {
if(label->bindings[MS_LABEL_BINDING_POSITION].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_POSITION].item);
label->bindings[MS_LABEL_BINDING_POSITION].item = strdup(msyystring_buffer);
label->numbindings++;
}
break;
case(PRIORITY):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1);
if(symbol == MS_NUMBER) {
label->priority = (int) msyynumber;
if(label->priority < 1 || label->priority > MS_MAX_LABEL_PRIORITY) {
msSetError(MS_MISCERR, "Invalid PRIORITY, must be an integer between 1 and %d." , "loadLabel()", MS_MAX_LABEL_PRIORITY);
return(-1);
}
} else {
if (label->bindings[MS_LABEL_BINDING_PRIORITY].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_PRIORITY].item);
label->bindings[MS_LABEL_BINDING_PRIORITY].item = msStrdup(msyystring_buffer);
label->numbindings++;
}
break;
case(SHADOWCOLOR):
if(loadColor(&(label->shadowcolor), NULL) != MS_SUCCESS) return(-1);
break;
case(SHADOWSIZE):
/* if(getInteger(&(label->shadowsizex)) == -1) return(-1); */
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1);
if(symbol == MS_NUMBER) {
label->shadowsizex = (int) msyynumber;
} else {
if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item);
label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item = msStrdup(msyystring_buffer);
label->numbindings++;
}
/* if(getInteger(&(label->shadowsizey)) == -1) return(-1); */
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(-1);
if(symbol == MS_NUMBER) {
label->shadowsizey = (int) msyynumber;
} else {
if (label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item != NULL)
msFree(label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item = msStrdup(msyystring_buffer);
label->numbindings++;
}
break;
case(SIZE):
if(label->bindings[MS_LABEL_BINDING_SIZE].item) {
msFree(label->bindings[MS_LABEL_BINDING_SIZE].item);
label->bindings[MS_LABEL_BINDING_SIZE].item = NULL;
label->numbindings--;
}
if((symbol = getSymbol(7, MS_NUMBER,MS_BINDING,MS_TINY,MS_SMALL,MS_MEDIUM,MS_LARGE,MS_GIANT)) == -1)
return(-1);
if(symbol == MS_NUMBER) {
label->size = (double) msyynumber;
} else if(symbol == MS_BINDING) {
label->bindings[MS_LABEL_BINDING_SIZE].item = msStrdup(msyystring_buffer);
label->numbindings++;
} else
label->size = symbol;
break;
case(STYLE):
if(msGrowLabelStyles(label) == NULL)
return(-1);
initStyle(label->styles[label->numstyles]);
if(loadStyle(label->styles[label->numstyles]) != MS_SUCCESS) return(-1);
if(label->styles[label->numstyles]->_geomtransform.type == MS_GEOMTRANSFORM_NONE)
label->styles[label->numstyles]->_geomtransform.type = MS_GEOMTRANSFORM_LABELPOINT; /* set a default, a marker? */
label->numstyles++;
break;
case(TEXT):
if(loadExpression(&(label->text)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */
if(msyysource == MS_URL_TOKENS) {
msSetError(MS_MISCERR, "URL-based TEXT configuration not supported for labels." , "loadLabel()");
freeExpression(&(label->text));
return(-1);
}
if((label->text.type != MS_STRING) && (label->text.type != MS_EXPRESSION)) {
msSetError(MS_MISCERR, "Text expressions support constant or tagged replacement strings." , "loadLabel()");
return(-1);
}
break;
case(TYPE):
if((label->type = getSymbol(2, MS_TRUETYPE,MS_BITMAP)) == -1) return(-1);
break;
case(WRAP):
if(getCharacter(&(label->wrap)) == -1) return(-1);
break;
default:
if(strlen(msyystring_buffer) > 0) {
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadLabel()", msyystring_buffer, msyylineno);
return(-1);
} else {
return(0); /* end of a string, not an error */
}
}
} /* next token */
}
int msUpdateLabelFromString(labelObj *label, char *string)
{
if(!label || !string) return MS_FAILURE;
msAcquireLock( TLOCK_PARSER );
msyystate = MS_TOKENIZE_STRING;
msyystring = string;
msyylex(); /* sets things up, but doesn't process any tokens */
msyylineno = 1; /* start at line 1 */
if(loadLabel(label) == -1) {
msReleaseLock( TLOCK_PARSER );
return MS_FAILURE; /* parse error */;
}
msReleaseLock( TLOCK_PARSER );
msyylex_destroy();
return MS_SUCCESS;
}
static void writeLeader(FILE *stream, int indent, labelLeaderObj *leader)
{
int i;
if(leader->maxdistance == 0 && leader->numstyles == 0) {
return;
}
indent++;
writeBlockBegin(stream, indent, "LEADER");
writeNumber(stream, indent, "MAXDISTANCE", 0, leader->maxdistance);
writeNumber(stream, indent, "GRIDSTEP", 5, leader->gridstep);
for(i=0; i<leader->numstyles; i++)
writeStyle(stream, indent, leader->styles[i]);
writeBlockEnd(stream, indent, "LEADER");
}
static void writeLabel(FILE *stream, int indent, labelObj *label)
{
int i;
colorObj c;
if(label->size == -1) return; /* there is no default label anymore */
indent++;
writeBlockBegin(stream, indent, "LABEL");
/*
** a few attributes are bitmap or truetype only
*/
if(label->type == MS_BITMAP) {
writeKeyword(stream, indent, "SIZE", (int)label->size, 5, MS_TINY, "TINY", MS_SMALL, "SMALL", MS_MEDIUM, "MEDIUM", MS_LARGE, "LARGE", MS_GIANT, "GIANT");
} else {
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_ANGLE].item)
writeAttributeBinding(stream, indent, "ANGLE", &(label->bindings[MS_LABEL_BINDING_ANGLE]));
else writeNumberOrKeyword(stream, indent, "ANGLE", 0, label->angle, label->anglemode, 3, MS_FOLLOW, "FOLLOW", MS_AUTO, "AUTO", MS_AUTO2, "AUTO2");
writeKeyword(stream, indent, "ANTIALIAS", label->antialias, 1, MS_TRUE, "TRUE");
writeExpression(stream, indent, "EXPRESSION", &(label->expression));
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_FONT].item)
writeAttributeBinding(stream, indent, "FONT", &(label->bindings[MS_LABEL_BINDING_FONT]));
else writeString(stream, indent, "FONT", NULL, label->font);
writeNumber(stream, indent, "MAXSIZE", MS_MAXFONTSIZE, label->maxsize);
writeNumber(stream, indent, "MINSIZE", MS_MINFONTSIZE, label->minsize);
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_SIZE].item)
writeAttributeBinding(stream, indent, "SIZE", &(label->bindings[MS_LABEL_BINDING_SIZE]));
else writeNumber(stream, indent, "SIZE", -1, label->size);
}
writeKeyword(stream, indent, "ALIGN", label->align, MS_ALIGN_CENTER, "CENTER", MS_ALIGN_RIGHT, "RIGHT");
writeNumber(stream, indent, "BUFFER", 0, label->buffer);
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_COLOR].item)
writeAttributeBinding(stream, indent, "COLOR", &(label->bindings[MS_LABEL_BINDING_COLOR]));
else {
MS_INIT_COLOR(c,0,0,0,255);
writeColor(stream, indent, "COLOR", &c, &(label->color));
}
writeString(stream, indent, "ENCODING", NULL, label->encoding);
writeLeader(stream,indent,&(label->leader));
writeKeyword(stream, indent, "FORCE", label->force, 2, MS_TRUE, "TRUE", MS_LABEL_FORCE_GROUP, "GROUP");
writeNumber(stream, indent, "MAXLENGTH", 0, label->maxlength);
writeNumber(stream, indent, "MAXSCALEDENOM", -1, label->maxscaledenom);
writeNumber(stream, indent, "MINDISTANCE", -1, label->mindistance);
writeNumberOrKeyword(stream, indent, "MINFEATURESIZE", -1, label->minfeaturesize, 1, label->autominfeaturesize, MS_TRUE, "AUTO");
writeNumber(stream, indent, "MINLENGTH", 0, label->minlength);
writeNumber(stream, indent, "MINSCALEDENOM", -1, label->minscaledenom);
writeDimension(stream, indent, "OFFSET", label->offsetx, label->offsety, NULL, NULL);
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_OUTLINECOLOR].item)
writeAttributeBinding(stream, indent, "OUTLINECOLOR", &(label->bindings[MS_LABEL_BINDING_OUTLINECOLOR]));
else writeColor(stream, indent, "OUTLINECOLOR", NULL, &(label->outlinecolor));
writeNumber(stream, indent, "OUTLINEWIDTH", 1, label->outlinewidth);
writeKeyword(stream, indent, "PARTIALS", label->partials, 1, MS_FALSE, "FALSE");
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_POSITION].item)
writeAttributeBinding(stream, indent, "POSITION", &(label->bindings[MS_LABEL_BINDING_POSITION]));
else writeKeyword(stream, indent, "POSITION", label->position, 10, MS_UL, "UL", MS_UC, "UC", MS_UR, "UR", MS_CL, "CL", MS_CC, "CC", MS_CR, "CR", MS_LL, "LL", MS_LC, "LC", MS_LR, "LR", MS_AUTO, "AUTO");
if(label->numbindings > 0 && label->bindings[MS_LABEL_BINDING_PRIORITY].item)
writeAttributeBinding(stream, indent, "PRIORITY", &(label->bindings[MS_LABEL_BINDING_PRIORITY]));
else writeNumber(stream, indent, "PRIORITY", MS_DEFAULT_LABEL_PRIORITY, label->priority);
writeNumber(stream, indent, "REPEATDISTANCE", 0, label->repeatdistance);
writeColor(stream, indent, "SHADOWCOLOR", NULL, &(label->shadowcolor));
writeDimension(stream, indent, "SHADOWSIZE", label->shadowsizex, label->shadowsizey, label->bindings[MS_LABEL_BINDING_SHADOWSIZEX].item, label->bindings[MS_LABEL_BINDING_SHADOWSIZEY].item);
writeNumber(stream, indent, "MAXOVERLAPANGLE", 22.5, label->maxoverlapangle);
for(i=0; i<label->numstyles; i++)
writeStyle(stream, indent, label->styles[i]);
writeExpression(stream, indent, "TEXT", &(label->text));
writeKeyword(stream, indent, "TYPE", label->type, 2, MS_BITMAP, "BITMAP", MS_TRUETYPE, "TRUETYPE");
writeCharacter(stream, indent, "WRAP", '\0', label->wrap);
writeBlockEnd(stream, indent, "LABEL");
}
void initExpression(expressionObj *exp)
{
exp->type = MS_STRING;
exp->string = NULL;
exp->compiled = MS_FALSE;
exp->flags = 0;
exp->tokens = exp->curtoken = NULL;
}
void freeExpressionTokens(expressionObj *exp)
{
tokenListNodeObjPtr node = NULL;
tokenListNodeObjPtr nextNode = NULL;
if(!exp) return;
if(exp->tokens) {
node = exp->tokens;
while (node != NULL) {
nextNode = node->next;
switch(node->token) {
case MS_TOKEN_BINDING_DOUBLE:
case MS_TOKEN_BINDING_INTEGER:
case MS_TOKEN_BINDING_STRING:
case MS_TOKEN_BINDING_TIME:
msFree(node->tokenval.bindval.item);
break;
case MS_TOKEN_LITERAL_TIME:
/* anything to do? */
break;
case MS_TOKEN_LITERAL_STRING:
msFree(node->tokenval.strval);
break;
case MS_TOKEN_LITERAL_SHAPE:
msFreeShape(node->tokenval.shpval);
free(node->tokenval.shpval);
break;
}
msFree(node);
node = nextNode;
}
exp->tokens = exp->curtoken = NULL;
}
}
void freeExpression(expressionObj *exp)
{
if(!exp) return;
msFree(exp->string);
if((exp->type == MS_REGEX) && exp->compiled) ms_regfree(&(exp->regex));
freeExpressionTokens(exp);
initExpression(exp); /* re-initialize */
}
int loadExpression(expressionObj *exp)
{
/* TODO: should we fall freeExpression if exp->string != NULL? We do some checking to avoid a leak but is it enough... */
msyystring_icase = MS_TRUE;
if((exp->type = getSymbol(5, MS_STRING,MS_EXPRESSION,MS_REGEX,MS_ISTRING,MS_IREGEX)) == -1) return(-1);
if (exp->string != NULL)
msFree(exp->string);
exp->string = msStrdup(msyystring_buffer);
if(exp->type == MS_ISTRING) {
exp->flags = exp->flags | MS_EXP_INSENSITIVE;
exp->type = MS_STRING;
} else if(exp->type == MS_IREGEX) {
exp->flags = exp->flags | MS_EXP_INSENSITIVE;
exp->type = MS_REGEX;
}
return(0);
}
/* ---------------------------------------------------------------------------
msLoadExpressionString and loadExpressionString
msLoadExpressionString wraps call to loadExpressionString with mutex
acquisition and release. This function should be used everywhere outside
the mapfile loading phase of an application. loadExpressionString does
not check for a mutex! It should be used only within code that has
properly acquired a mutex.
See bug 339 for more details -- SG.
------------------------------------------------------------------------ */
int msLoadExpressionString(expressionObj *exp, char *value)
{
int retval = MS_FAILURE;
msAcquireLock( TLOCK_PARSER );
retval = loadExpressionString( exp, value );
msReleaseLock( TLOCK_PARSER );
return retval;
}
int loadExpressionString(expressionObj *exp, char *value)
{
msyystate = MS_TOKENIZE_STRING;
msyystring = value;
msyylex(); /* sets things up but processes no tokens */
freeExpression(exp); /* we're totally replacing the old expression so free (which re-inits) to start over */
msyystring_icase = MS_TRUE;
if((exp->type = getSymbol(4, MS_EXPRESSION,MS_REGEX,MS_IREGEX,MS_ISTRING)) != -1) {
exp->string = msStrdup(msyystring_buffer);
if(exp->type == MS_ISTRING) {
exp->type = MS_STRING;
exp->flags = exp->flags | MS_EXP_INSENSITIVE;
} else if(exp->type == MS_IREGEX) {
exp->type = MS_REGEX;
exp->flags = exp->flags | MS_EXP_INSENSITIVE;
}
} else {
msResetErrorList(); /* failure above is not really an error since we'll consider anything not matching (like an unquoted number) as a STRING) */
exp->type = MS_STRING;
exp->string = msStrdup(msyystring_buffer);
}
return(0);
}
/* msGetExpressionString()
*
* Returns the string representation of this expression, including delimiters
* and any flags (e.g. i = case-insensitive).
*
* Returns a newly allocated buffer that should be freed by the caller or NULL.
*/
char *msGetExpressionString(expressionObj *exp)
{
if(exp->string) {
char *exprstring;
size_t buffer_size;
const char *case_insensitive = "";
if(exp->flags & MS_EXP_INSENSITIVE)
case_insensitive = "i";
/* Alloc buffer big enough for string + 2 delimiters + 'i' + \0 */
buffer_size = strlen(exp->string)+4;
exprstring = (char*)msSmallMalloc(buffer_size);
switch(exp->type) {
case(MS_REGEX):
snprintf(exprstring, buffer_size, "/%s/%s", exp->string, case_insensitive);
return exprstring;
case(MS_STRING):
snprintf(exprstring, buffer_size, "\"%s\"%s", exp->string, case_insensitive);
return exprstring;
case(MS_EXPRESSION):
snprintf(exprstring, buffer_size, "(%s)", exp->string);
return exprstring;
default:
/* We should never get to here really! */
free(exprstring);
return NULL;
}
}
return NULL;
}
static void writeExpression(FILE *stream, int indent, const char *name, expressionObj *exp)
{
char *string_tmp;
if(!exp || !exp->string) return;
writeIndent(stream, ++indent);
switch(exp->type) {
case(MS_REGEX):
fprintf(stream, "%s /%s/", name, exp->string);
break;
case(MS_STRING):
if ( (strchr(exp->string, '\'') == NULL) && (strchr(exp->string, '\"') == NULL))
fprintf(stream, "%s \"%s\"", name, exp->string);
else if ( (strchr(exp->string, '\"') != NULL) && (strchr(exp->string, '\'') == NULL))
fprintf(stream, "%s \'%s\'", name, exp->string);
else if ( (strchr(exp->string, '\'') != NULL) && (strchr(exp->string, '\"') == NULL))
fprintf(stream, "%s \"%s\"", name, exp->string);
else {
string_tmp = msStringEscape(exp->string);
fprintf(stream, "%s \"%s\"", name, string_tmp);
free(string_tmp);
}
break;
case(MS_EXPRESSION):
fprintf(stream, "%s (%s)", name, exp->string);
break;
}
if((exp->type == MS_STRING || exp->type == MS_REGEX) && (exp->flags & MS_EXP_INSENSITIVE))
fprintf(stream, "i");
writeLineFeed(stream);
}
int loadHashTable(hashTableObj *ptable)
{
char *key=NULL, *data=NULL;
if (!ptable) ptable = msCreateHashTable();
for(;;) {
switch(msyylex()) {
case(EOF):
msSetError(MS_EOFERR, NULL, "loadHashTable()");
return(MS_FAILURE);
case(END):
return(MS_SUCCESS);
case(MS_STRING):
key = msStrdup(msyystring_buffer); /* the key is *always* a string */
if(getString(&data) == MS_FAILURE) return(MS_FAILURE);
msInsertHashTable(ptable, key, data);
free(key);
free(data);
data=NULL;
break;
default:
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadHashTable()", msyystring_buffer, msyylineno );
return(MS_FAILURE);
}
}
return(MS_SUCCESS);
}
static void writeHashTable(FILE *stream, int indent, const char *title, hashTableObj *table)
{
struct hashObj *tp;
int i;
if(!table) return;
if(msHashIsEmpty(table)) return;
indent++;
writeBlockBegin(stream, indent, title);
for (i=0; i<MS_HASHSIZE; i++) {
if (table->items[i] != NULL) {
for (tp=table->items[i]; tp!=NULL; tp=tp->next)
writeNameValuePair(stream, indent, tp->key, tp->data);
}
}
writeBlockEnd(stream, indent, title);
}
static void writeHashTableInline(FILE *stream, int indent, char *name, hashTableObj* table)
{
struct hashObj *tp = NULL;
int i;
if(!table) return;
if(msHashIsEmpty(table)) return;
++indent;
for (i=0; i<MS_HASHSIZE; ++i) {
if (table->items[i] != NULL) {
for (tp=table->items[i]; tp!=NULL; tp=tp->next) {
writeIndent(stream, indent);
fprintf(stream, "%s \"%s\" \"%s\"\n", name, tp->key, tp->data);
}
}
}
}
/*
** Initialize, load and free a cluster object
*/
void initCluster(clusterObj *cluster)
{
cluster->maxdistance = 10;
cluster->buffer = 0;
cluster->region = NULL;
initExpression(&(cluster->group));
initExpression(&(cluster->filter));
}
void freeCluster(clusterObj *cluster)
{
msFree(cluster->region);
freeExpression(&(cluster->group));
freeExpression(&(cluster->filter));
}
int loadCluster(clusterObj *cluster)
{
for(;;) {
switch(msyylex()) {
case(CLUSTER):
break; /* for string loads */
case(MAXDISTANCE):
if(getDouble(&(cluster->maxdistance)) == -1) return(-1);
break;
case(BUFFER):
if(getDouble(&(cluster->buffer)) == -1) return(-1);
break;
case(REGION):
if(getString(&cluster->region) == MS_FAILURE) return(-1);
break;
case(END):
return(0);
break;
case(GROUP):
if(loadExpression(&(cluster->group)) == -1) return(-1);
break;
case(FILTER):
if(loadExpression(&(cluster->filter)) == -1) return(-1);
break;
default:
if(strlen(msyystring_buffer) > 0) {
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadCluster()", msyystring_buffer, msyylineno);
return(-1);
} else {
return(0); /* end of a string, not an error */
}
}
}
return(MS_SUCCESS);
}
int msUpdateClusterFromString(clusterObj *cluster, char *string)
{
if(!cluster || !string) return MS_FAILURE;
msAcquireLock( TLOCK_PARSER );
msyystate = MS_TOKENIZE_STRING;
msyystring = string;
msyylex(); /* sets things up, but doesn't process any tokens */
msyylineno = 1; /* start at line 1 */
if(loadCluster(cluster) == -1) {
msReleaseLock( TLOCK_PARSER );
return MS_FAILURE; /* parse error */;
}
msReleaseLock( TLOCK_PARSER );
msyylex_destroy();
return MS_SUCCESS;
}
static void writeCluster(FILE *stream, int indent, clusterObj *cluster)
{
if (cluster->maxdistance == 10 &&
cluster->buffer == 0.0 &&
cluster->region == NULL &&
cluster->group.string == NULL &&
cluster->filter.string == NULL)
return; /* Nothing to write */
indent++;
writeBlockBegin(stream, indent, "CLUSTER");
writeNumber(stream, indent, "MAXDISTANCE", 10, cluster->maxdistance);
writeNumber(stream, indent, "BUFFER", 0, cluster->buffer);
writeString(stream, indent, "REGION", NULL, cluster->region);
writeExpression(stream, indent, "GROUP", &(cluster->group));
writeExpression(stream, indent, "FILTER", &(cluster->filter));
writeBlockEnd(stream, indent, "CLUSTER");
}
/*
** Initialize, load and free a single style
*/
int initStyle(styleObj *style)
{
int i;
MS_REFCNT_INIT(style);
MS_INIT_COLOR(style->color, -1,-1,-1,255); /* must explictly set colors */
MS_INIT_COLOR(style->backgroundcolor, -1,-1,-1,255);
MS_INIT_COLOR(style->outlinecolor, -1,-1,-1,255);
/* New Color Range fields*/
MS_INIT_COLOR(style->mincolor, -1,-1,-1,255);
MS_INIT_COLOR(style->maxcolor, -1,-1,-1,255);
style->minvalue = 0.0;
style->maxvalue = 1.0;
style->rangeitem = NULL;
/* End Color Range fields*/
style->symbol = 0; /* there is always a default symbol*/
style->symbolname = NULL;
style->size = -1; /* in SIZEUNITS (layerObj) */
style->minsize = MS_MINSYMBOLSIZE;
style->maxsize = MS_MAXSYMBOLSIZE;
style->width = 1; /* in pixels */
style->outlinewidth = 0; /* in pixels */
style->minwidth = MS_MINSYMBOLWIDTH;
style->maxwidth = MS_MAXSYMBOLWIDTH;
style->minscaledenom=style->maxscaledenom = -1.0;
style->offsetx = style->offsety = 0; /* no offset */
style->polaroffsetpixel = style->polaroffsetangle = 0; /* no polar offset */
style->antialias = MS_FALSE;
style->angle = 0;
style->autoangle= MS_FALSE;
style->opacity = 100; /* fully opaque */
initExpression(&(style->_geomtransform));
style->_geomtransform.type = MS_GEOMTRANSFORM_NONE;
style->patternlength = 0; /* solid line */
style->gap = 0;
style->initialgap = -1;
style->position = MS_CC;
style->linecap = MS_CJC_DEFAULT_CAPS;
style->linejoin = MS_CJC_DEFAULT_JOINS;
style->linejoinmaxsize = MS_CJC_DEFAULT_JOIN_MAXSIZE;
style->numbindings = 0;
for(i=0; i<MS_STYLE_BINDING_LENGTH; i++) {
style->bindings[i].item = NULL;
style->bindings[i].index = -1;
}
return MS_SUCCESS;
}
int loadStyle(styleObj *style)
{
int symbol;
for(;;) {
switch(msyylex()) {
/* New Color Range fields*/
case (COLORRANGE):
/*These are both in one line now*/
if(loadColor(&(style->mincolor), NULL) != MS_SUCCESS) return(MS_FAILURE);
if(loadColor(&(style->maxcolor), NULL) != MS_SUCCESS) return(MS_FAILURE);
break;
case(DATARANGE):
/*These are both in one line now*/
if(getDouble(&(style->minvalue)) == -1) return(-1);
if(getDouble(&(style->maxvalue)) == -1) return(-1);
break;
case(RANGEITEM):
if(getString(&style->rangeitem) == MS_FAILURE) return(-1);
break;
/* End Range fields*/
case(ANGLE):
if((symbol = getSymbol(3, MS_NUMBER,MS_BINDING,MS_AUTO)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->angle = (double) msyynumber;
else if(symbol==MS_BINDING) {
if (style->bindings[MS_STYLE_BINDING_ANGLE].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_ANGLE].item);
style->bindings[MS_STYLE_BINDING_ANGLE].item = msStrdup(msyystring_buffer);
style->numbindings++;
} else {
style->autoangle=MS_TRUE;
}
break;
case(ANTIALIAS):
if((style->antialias = getSymbol(2, MS_TRUE,MS_FALSE)) == -1)
return(MS_FAILURE);
break;
case(BACKGROUNDCOLOR):
if(loadColor(&(style->backgroundcolor), NULL) != MS_SUCCESS) return(MS_FAILURE);
break;
case(COLOR):
if(loadColor(&(style->color), &(style->bindings[MS_STYLE_BINDING_COLOR])) != MS_SUCCESS) return(MS_FAILURE);
if(style->bindings[MS_STYLE_BINDING_COLOR].item) style->numbindings++;
break;
case(EOF):
msSetError(MS_EOFERR, NULL, "loadStyle()");
return(MS_FAILURE); /* missing END (probably) */
case(END): {
int alpha;
/* apply opacity as the alpha channel color(s) */
if(style->opacity < 100) {
alpha = MS_NINT(style->opacity*2.55);
style->color.alpha = alpha;
style->outlinecolor.alpha = alpha;
style->backgroundcolor.alpha = alpha;
style->mincolor.alpha = alpha;
style->maxcolor.alpha = alpha;
}
return(MS_SUCCESS);
}
break;
case(GAP):
if((getDouble(&style->gap)) == -1) return(MS_FAILURE);
break;
case(INITIALGAP):
if((getDouble(&style->initialgap)) == -1) return(MS_FAILURE);
if(style->initialgap < 0) {
msSetError(MS_MISCERR, "INITIALGAP requires a positive values", "loadStyle()");
return(MS_FAILURE);
}
break;
case(MAXSCALEDENOM):
if(getDouble(&(style->maxscaledenom)) == -1) return(MS_FAILURE);
break;
case(MINSCALEDENOM):
if(getDouble(&(style->minscaledenom)) == -1) return(MS_FAILURE);
break;
case(GEOMTRANSFORM): {
int s;
if((s = getSymbol(2, MS_STRING, MS_EXPRESSION)) == -1) return(MS_FAILURE);
if(s == MS_STRING)
msStyleSetGeomTransform(style, msyystring_buffer);
else {
/* handle expression case here for the moment */
msFree(style->_geomtransform.string);
style->_geomtransform.string = msStrdup(msyystring_buffer);
style->_geomtransform.type = MS_GEOMTRANSFORM_EXPRESSION;
}
}
break;
case(LINECAP):
if((style->linecap = getSymbol(4,MS_CJC_BUTT, MS_CJC_ROUND, MS_CJC_SQUARE, MS_CJC_TRIANGLE)) == -1) return(MS_FAILURE);
break;
case(LINEJOIN):
if((style->linejoin = getSymbol(4,MS_CJC_NONE, MS_CJC_ROUND, MS_CJC_MITER, MS_CJC_BEVEL)) == -1) return(MS_FAILURE);
break;
case(LINEJOINMAXSIZE):
if((getDouble(&style->linejoinmaxsize)) == -1) return(MS_FAILURE);
break;
case(MAXSIZE):
if(getDouble(&(style->maxsize)) == -1) return(MS_FAILURE);
break;
case(MINSIZE):
if(getDouble(&(style->minsize)) == -1) return(MS_FAILURE);
break;
case(MAXWIDTH):
if(getDouble(&(style->maxwidth)) == -1) return(MS_FAILURE);
break;
case(MINWIDTH):
if(getDouble(&(style->minwidth)) == -1) return(MS_FAILURE);
break;
case(OFFSET):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->offsetx = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_OFFSET_X].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_OFFSET_X].item);
style->bindings[MS_STYLE_BINDING_OFFSET_X].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->offsety = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_OFFSET_Y].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
style->bindings[MS_STYLE_BINDING_OFFSET_Y].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(OPACITY):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->opacity = MS_MAX(MS_MIN((int) msyynumber, 100), 0); /* force opacity to between 0 and 100 */
else {
if (style->bindings[MS_STYLE_BINDING_OPACITY].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_OPACITY].item);
style->bindings[MS_STYLE_BINDING_OPACITY].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(OUTLINECOLOR):
if(loadColor(&(style->outlinecolor), &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR])) != MS_SUCCESS) return(MS_FAILURE);
if(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item) style->numbindings++;
break;
case(PATTERN): {
int done = MS_FALSE;
for(;;) { /* read till the next END */
switch(msyylex()) {
case(END):
if(style->patternlength < 2) {
msSetError(MS_SYMERR, "Not enough pattern elements. A minimum of 2 are required", "loadStyle()");
return(MS_FAILURE);
}
done = MS_TRUE;
break;
case(MS_NUMBER): /* read the pattern values */
if(style->patternlength == MS_MAXPATTERNLENGTH) {
msSetError(MS_SYMERR, "Pattern too long.", "loadStyle()");
return(-1);
}
style->pattern[style->patternlength] = atof(msyystring_buffer);
style->patternlength++;
break;
default:
msSetError(MS_TYPEERR, "Parsing error near (%s):(line %d)", "loadStyle()", msyystring_buffer, msyylineno);
return(-1);
}
if(done == MS_TRUE)
break;
}
break;
}
case(POSITION):
/* if((s->position = getSymbol(3, MS_UC,MS_CC,MS_LC)) == -1) */
/* return(-1); */
if((style->position = getSymbol(9, MS_UL,MS_UC,MS_UR,MS_CL,MS_CC,MS_CR,MS_LL,MS_LC,MS_LR)) == -1)
return(-1);
break;
case(OUTLINEWIDTH):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER) {
style->outlinewidth = (double) msyynumber;
if(style->outlinewidth < 0) {
msSetError(MS_MISCERR, "Invalid OUTLINEWIDTH, must be greater than 0" , "loadStyle()");
return(MS_FAILURE);
}
} else {
if (style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item);
style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(SIZE):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->size = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_SIZE].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_SIZE].item);
style->bindings[MS_STYLE_BINDING_SIZE].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(STYLE):
break; /* for string loads */
case(SYMBOL):
if((symbol = getSymbol(3, MS_NUMBER,MS_STRING,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER) {
if (style->symbolname != NULL) {
msFree(style->symbolname);
style->symbolname = NULL;
}
style->symbol = (int) msyynumber;
} else if(symbol == MS_STRING) {
if (style->symbolname != NULL)
msFree(style->symbolname);
style->symbolname = msStrdup(msyystring_buffer);
} else {
if (style->bindings[MS_STYLE_BINDING_SYMBOL].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_SYMBOL].item);
style->bindings[MS_STYLE_BINDING_SYMBOL].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(WIDTH):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->width = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_WIDTH].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_WIDTH].item);
style->bindings[MS_STYLE_BINDING_WIDTH].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
case(POLAROFFSET):
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->polaroffsetpixel = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item);
style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
if((symbol = getSymbol(2, MS_NUMBER,MS_BINDING)) == -1) return(MS_FAILURE);
if(symbol == MS_NUMBER)
style->polaroffsetangle = (double) msyynumber;
else {
if (style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item != NULL)
msFree(style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item = msStrdup(msyystring_buffer);
style->numbindings++;
}
break;
default:
if(strlen(msyystring_buffer) > 0) {
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadStyle()", msyystring_buffer, msyylineno);
return(MS_FAILURE);
} else {
return(MS_SUCCESS); /* end of a string, not an error */
}
}
}
}
int msUpdateStyleFromString(styleObj *style, char *string, int url_string)
{
if(!style || !string) return MS_FAILURE;
msAcquireLock( TLOCK_PARSER );
if(url_string)
msyystate = MS_TOKENIZE_URL_STRING;
else
msyystate = MS_TOKENIZE_STRING;
msyystring = string;
msyylex(); /* sets things up, but doesn't process any tokens */
msyylineno = 1; /* start at line 1 */
if(loadStyle(style) == -1) {
msReleaseLock( TLOCK_PARSER );
return MS_FAILURE; /* parse error */;
}
msReleaseLock( TLOCK_PARSER );
msyylex_destroy();
return MS_SUCCESS;
}
int freeStyle(styleObj *style)
{
int i;
if( MS_REFCNT_DECR_IS_NOT_ZERO(style) ) {
return MS_FAILURE;
}
msFree(style->symbolname);
freeExpression(&style->_geomtransform);
msFree(style->rangeitem);
for(i=0; i<MS_STYLE_BINDING_LENGTH; i++)
msFree(style->bindings[i].item);
return MS_SUCCESS;
}
void writeStyle(FILE *stream, int indent, styleObj *style)
{
indent++;
writeBlockBegin(stream, indent, "STYLE");
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_ANGLE].item)
writeAttributeBinding(stream, indent, "ANGLE", &(style->bindings[MS_STYLE_BINDING_ANGLE]));
else writeNumberOrKeyword(stream, indent, "ANGLE", 360, style->angle, style->autoangle, 1, MS_TRUE, "AUTO");
writeKeyword(stream, indent, "ANTIALIAS", style->antialias, 1, MS_TRUE, "TRUE");
writeColor(stream, indent, "BACKGROUNDCOLOR", NULL, &(style->backgroundcolor));
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_COLOR].item)
writeAttributeBinding(stream, indent, "COLOR", &(style->bindings[MS_STYLE_BINDING_COLOR]));
else writeColor(stream, indent, "COLOR", NULL, &(style->color));
writeNumber(stream, indent, "GAP", 0, style->gap);
writeNumber(stream, indent, "INITIALGAP", -1, style->initialgap);
if(style->_geomtransform.type != MS_GEOMTRANSFORM_NONE) {
writeKeyword(stream, indent, "GEOMTRANSFORM", style->_geomtransform.type, 6,
MS_GEOMTRANSFORM_BBOX, "\"bbox\"",
MS_GEOMTRANSFORM_END, "\"end\"",
MS_GEOMTRANSFORM_LABELPOINT, "\"labelpnt\"",
MS_GEOMTRANSFORM_LABELPOLY, "\"labelpoly\"",
MS_GEOMTRANSFORM_START, "\"start\"",
MS_GEOMTRANSFORM_VERTICES, "\"vertices\""
);
}
if(style->linecap != MS_CJC_DEFAULT_CAPS) {
writeKeyword(stream,indent,"LINECAP",(int)style->linecap,5,
MS_CJC_NONE,"NONE",
MS_CJC_ROUND, "ROUND",
MS_CJC_SQUARE, "SQUARE",
MS_CJC_BUTT, "BUTT",
MS_CJC_TRIANGLE, "TRIANGLE");
}
if(style->linejoin != MS_CJC_DEFAULT_JOINS) {
writeKeyword(stream,indent,"LINEJOIN",(int)style->linejoin,5,
MS_CJC_NONE,"NONE",
MS_CJC_ROUND, "ROUND",
MS_CJC_BEVEL, "BEVEL",
MS_CJC_MITER, "MITER");
}
writeNumber(stream, indent, "LINEJOINMAXSIZE", MS_CJC_DEFAULT_JOIN_MAXSIZE , style->linejoinmaxsize);
writeNumber(stream, indent, "MAXSCALEDENOM", -1, style->maxscaledenom);
writeNumber(stream, indent, "MAXSIZE", MS_MAXSYMBOLSIZE, style->maxsize);
writeNumber(stream, indent, "MAXWIDTH", MS_MAXSYMBOLWIDTH, style->maxwidth);
writeNumber(stream, indent, "MINSCALEDENOM", -1, style->minscaledenom);
writeNumber(stream, indent, "MINSIZE", MS_MINSYMBOLSIZE, style->minsize);
writeNumber(stream, indent, "MINWIDTH", MS_MINSYMBOLWIDTH, style->minwidth);
writeDimension(stream, indent, "OFFSET", style->offsetx, style->offsety, style->bindings[MS_STYLE_BINDING_OFFSET_X].item, style->bindings[MS_STYLE_BINDING_OFFSET_Y].item);
writeDimension(stream, indent, "POLAROFFSET", style->polaroffsetpixel, style->polaroffsetangle,
style->bindings[MS_STYLE_BINDING_POLAROFFSET_PIXEL].item, style->bindings[MS_STYLE_BINDING_POLAROFFSET_ANGLE].item);
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_OPACITY].item)
writeAttributeBinding(stream, indent, "OPACITY", &(style->bindings[MS_STYLE_BINDING_OPACITY]));
else writeNumber(stream, indent, "OPACITY", 100, style->opacity);
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_OUTLINECOLOR].item)
writeAttributeBinding(stream, indent, "OUTLINECOLOR", &(style->bindings[MS_STYLE_BINDING_OUTLINECOLOR]));
else writeColor(stream, indent, "OUTLINECOLOR", NULL, &(style->outlinecolor));
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH].item)
writeAttributeBinding(stream, indent, "OUTLINEWIDTH", &(style->bindings[MS_STYLE_BINDING_OUTLINEWIDTH]));
else writeNumber(stream, indent, "OUTLINEWIDTH", 0, style->outlinewidth);
/* PATTERN */
if(style->patternlength != 0) {
int i;
indent++;
writeBlockBegin(stream,indent,"PATTERN");
for(i=0; i<style->patternlength; i++)
fprintf(stream, " %.2f", style->pattern[i]);
fprintf(stream,"\n");
writeBlockEnd(stream,indent,"PATTERN");
indent--;
}
if(style->position != MS_CC) {
writeKeyword(stream, indent, "POSITION", style->position, 9,
MS_UL, "UL", MS_UC, "UC", MS_UR, "UR", MS_CL, "CL",
MS_CC, "CC", MS_CR, "CR", MS_LL, "LL", MS_LC, "LC",
MS_LR, "LR");
}
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SIZE].item)
writeAttributeBinding(stream, indent, "SIZE", &(style->bindings[MS_STYLE_BINDING_SIZE]));
else writeNumber(stream, indent, "SIZE", -1, style->size);
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_SYMBOL].item)
writeAttributeBinding(stream, indent, "SYMBOL", &(style->bindings[MS_STYLE_BINDING_SYMBOL]));
else writeNumberOrString(stream, indent, "SYMBOL", 0, style->symbol, style->symbolname);
if(style->numbindings > 0 && style->bindings[MS_STYLE_BINDING_WIDTH].item)
writeAttributeBinding(stream, indent, "WIDTH", &(style->bindings[MS_STYLE_BINDING_WIDTH]));
else writeNumber(stream, indent, "WIDTH", 1, style->width);
if(style->rangeitem) {
writeString(stream, indent, "RANGEITEM", NULL, style->rangeitem);
writeColorRange(stream, indent, "COLORRANGE", &(style->mincolor), &(style->maxcolor));
writeDimension(stream, indent, "DATARANGE", style->minvalue, style->maxvalue, NULL, NULL);
}
writeBlockEnd(stream, indent, "STYLE");
}
/*
** Initialize, load and free a single class
*/
int initClass(classObj *class)
{
class->status = MS_ON;
class->debug = MS_OFF;
MS_REFCNT_INIT(class);
initExpression(&(class->expression));
class->name = NULL;
class->title = NULL;
initExpression(&(class->text));
class->template = NULL;
class->type = -1;
initHashTable(&(class->metadata));
initHashTable(&(class->validation));
class->maxscaledenom = class->minscaledenom = -1.0;
class->minfeaturesize = -1; /* no limit */
/* Set maxstyles = 0, styles[] will be allocated as needed on first call
* to msGrowClassStyles()
*/
class->numstyles = class->maxstyles = 0;
class->styles = NULL;
class->numlabels = class->maxlabels = 0;
class->labels = NULL;
class->keyimage = NULL;
class->group = NULL;
initLeader(&(class->leader));
return(0);
}
int freeClass(classObj *class)
{
int i;
if( MS_REFCNT_DECR_IS_NOT_ZERO(class) ) {
return MS_FAILURE;
}
freeExpression(&(class->expression));
freeExpression(&(class->text));
msFree(class->name);
msFree(class->title);
msFree(class->template);
msFree(class->group);
if (&(class->metadata)) msFreeHashItems(&(class->metadata));
if (&(class->validation)) msFreeHashItems(&(class->validation));
for(i=0; i<class->numstyles; i++) { /* each style */
if(class->styles[i]!=NULL) {
if(freeStyle(class->styles[i]) == MS_SUCCESS) {
msFree(class->styles[i]);
}
}
}
msFree(class->styles);
for(i=0; i<class->numlabels; i++) { /* each label */
if(class->labels[i]!=NULL) {
if(freeLabel(class->labels[i]) == MS_SUCCESS) {
msFree(class->labels[i]);
}
}
}
msFree(class->labels);
msFree(class->keyimage);
freeLabelLeader(&(class->leader));
return MS_SUCCESS;
}
/*
** Ensure there is at least one free entry in the sttyles array of this
** classObj. Grow the allocated styles array if necessary and allocate
** a new style for styles[numstyles] if there is not already one,
** setting its contents to all zero bytes (i.e. does not call initStyle()
** on it).
**
** This function is safe to use for the initial allocation of the styles[]
** array as well (i.e. when maxstyles==0 and styles==NULL)
**
** Returns a reference to the new styleObj on success, NULL on error.
*/
styleObj *msGrowClassStyles( classObj *class )
{
/* Do we need to increase the size of styles[] by MS_STYLE_ALLOCSIZE?
*/
if (class->numstyles == class->maxstyles) {
styleObj **newStylePtr;
int i, newsize;
newsize = class->maxstyles + MS_STYLE_ALLOCSIZE;
/* Alloc/realloc styles */
newStylePtr = (styleObj**)realloc(class->styles, newsize*sizeof(styleObj*));
MS_CHECK_ALLOC(newStylePtr, newsize*sizeof(styleObj*), NULL);
class->styles = newStylePtr;
class->maxstyles = newsize;
for(i=class->numstyles; i<class->maxstyles; i++) {
class->styles[i] = NULL;
}
}
if (class->styles[class->numstyles]==NULL) {
class->styles[class->numstyles]=(styleObj*)calloc(1,sizeof(styleObj));
MS_CHECK_ALLOC(class->styles[class->numstyles], sizeof(styleObj), NULL);
}
return class->styles[class->numstyles];
}
/* exactly the same as for a classObj */
styleObj *msGrowLabelStyles( labelObj *label )
{
/* Do we need to increase the size of styles[] by MS_STYLE_ALLOCSIZE?
*/
if (label->numstyles == label->maxstyles) {
styleObj **newStylePtr;
int i, newsize;
newsize = label->maxstyles + MS_STYLE_ALLOCSIZE;
/* Alloc/realloc styles */
newStylePtr = (styleObj**)realloc(label->styles, newsize*sizeof(styleObj*));
MS_CHECK_ALLOC(newStylePtr, newsize*sizeof(styleObj*), NULL);
label->styles = newStylePtr;
label->maxstyles = newsize;
for(i=label->numstyles; i<label->maxstyles; i++) {
label->styles[i] = NULL;
}
}
if (label->styles[label->numstyles]==NULL) {
label->styles[label->numstyles]=(styleObj*)calloc(1,sizeof(styleObj));
MS_CHECK_ALLOC(label->styles[label->numstyles], sizeof(styleObj), NULL);
}
return label->styles[label->numstyles];
}
/* exactly the same as for a labelLeaderObj, needs refactoring */
styleObj *msGrowLeaderStyles( labelLeaderObj *leader )
{
/* Do we need to increase the size of styles[] by MS_STYLE_ALLOCSIZE?
*/
if (leader->numstyles == leader->maxstyles) {
styleObj **newStylePtr;
int i, newsize;
newsize = leader->maxstyles + MS_STYLE_ALLOCSIZE;
/* Alloc/realloc styles */
newStylePtr = (styleObj**)realloc(leader->styles,
newsize*sizeof(styleObj*));
MS_CHECK_ALLOC(newStylePtr, newsize*sizeof(styleObj*), NULL);
leader->styles = newStylePtr;
leader->maxstyles = newsize;
for(i=leader->numstyles; i<leader->maxstyles; i++) {
leader->styles[i] = NULL;
}
}
if (leader->styles[leader->numstyles]==NULL) {
leader->styles[leader->numstyles]=(styleObj*)calloc(1,sizeof(styleObj));
MS_CHECK_ALLOC(leader->styles[leader->numstyles], sizeof(styleObj), NULL);
}
return leader->styles[leader->numstyles];
}
/* msMaybeAllocateClassStyle()
**
** Ensure that requested style index exists and has been initialized.
**
** Returns MS_SUCCESS/MS_FAILURE.
*/
int msMaybeAllocateClassStyle(classObj* c, int idx)
{
if (c==NULL) return MS_FAILURE;
if ( idx < 0 ) {
msSetError(MS_MISCERR, "Invalid style index: %d", "msMaybeAllocateClassStyle()", idx);
return MS_FAILURE;
}
/* Alloc empty styles as needed up to idx.
* Nothing to do if requested style already exists
*/
while(c->numstyles <= idx) {
if (msGrowClassStyles(c) == NULL)
return MS_FAILURE;
if ( initStyle(c->styles[c->numstyles]) == MS_FAILURE ) {
msSetError(MS_MISCERR, "Failed to init new styleObj",
"msMaybeAllocateClassStyle()");
return(MS_FAILURE);
}
c->numstyles++;
}
return MS_SUCCESS;
}
/*
* Reset style info in the class to defaults
* the only members we don't touch are name, expression, and join/query stuff
* This is used with STYLEITEM before overwriting the contents of a class.
*/
void resetClassStyle(classObj *class)
{
int i;
/* reset labels */
for(i=0; i<class->numlabels; i++) {
if(class->styles[i] != NULL) {
if(freeLabel(class->labels[i]) == MS_SUCCESS ) {
msFree(class->labels[i]);
}
class->labels[i] = NULL;
}
}
class->numlabels = 0;
freeExpression(&(class->text));
initExpression(&(class->text));
/* reset styles */
for(i=0; i<class->numstyles; i++) {
if(class->styles[i] != NULL) {
if( freeStyle(class->styles[i]) == MS_SUCCESS ) {
msFree(class->styles[i]);
}
class->styles[i] = NULL;
}
}
class->numstyles = 0;
class->type = -1;
class->layer = NULL;
}
labelObj *msGrowClassLabels( classObj *class )
{
/* Do we need to increase the size of labels[] by MS_LABEL_ALLOCSIZE?
*/
if (class->numlabels == class->maxlabels) {
labelObj **newLabelPtr;
int i, newsize;
newsize = class->maxlabels + MS_LABEL_ALLOCSIZE;
/* Alloc/realloc labels */
newLabelPtr = (labelObj**)realloc(class->labels, newsize*sizeof(labelObj*));
MS_CHECK_ALLOC(newLabelPtr, newsize*sizeof(labelObj*), NULL);
class->labels = newLabelPtr;
class->maxlabels = newsize;
for(i=class->numlabels; i<class->maxlabels; i++) {
class->labels[i] = NULL;
}
}
if (class->labels[class->numlabels]==NULL) {
class->labels[class->numlabels]=(labelObj*)calloc(1,sizeof(labelObj));
MS_CHECK_ALLOC(class->labels[class->numlabels], sizeof(labelObj), NULL);
}
return class->labels[class->numlabels];
}
int loadClass(classObj *class, layerObj *layer)
{
int state;
mapObj *map=NULL;
class->layer = (layerObj *) layer;
if(layer && layer->map) map = layer->map;
for(;;) {
switch(msyylex()) {
case(CLASS):
break; /* for string loads */
case(DEBUG):
if((class->debug = getSymbol(3, MS_ON,MS_OFF, MS_NUMBER)) == -1) return(-1);
if(class->debug == MS_NUMBER) class->debug = (int) msyynumber;
break;
case(EOF):
msSetError(MS_EOFERR, NULL, "loadClass()");
return(-1);
case(END):
return(0);
break;
case(EXPRESSION):
if(loadExpression(&(class->expression)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->expression.string, msLookupHashTable(&(class->validation), "expression"), msLookupHashTable(&(layer->validation), "expression"), msLookupHashTable(&(map->web.validation), "expression"), NULL) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based EXPRESSION configuration failed pattern validation." , "loadClass()");
freeExpression(&(class->expression));
return(-1);
}
}
break;
case(GROUP):
if(getString(&class->group) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->group, msLookupHashTable(&(class->validation), "group"), msLookupHashTable(&(layer->validation), "group"), msLookupHashTable(&(map->web.validation), "group"), NULL) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based GROUP configuration failed pattern validation." , "loadClass()");
msFree(class->group);
class->group=NULL;
return(-1);
}
}
break;
case(KEYIMAGE):
if(getString(&class->keyimage) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->keyimage, msLookupHashTable(&(class->validation), "keyimage"), msLookupHashTable(&(layer->validation), "keyimage"), msLookupHashTable(&(map->web.validation), "keyimage"), NULL) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based KEYIMAGE configuration failed pattern validation." , "loadClass()");
msFree(class->keyimage);
class->keyimage=NULL;
return(-1);
}
}
break;
case(LABEL):
if(msGrowClassLabels(class) == NULL) return(-1);
initLabel(class->labels[class->numlabels]);
class->labels[class->numlabels]->size = MS_MEDIUM; /* only set a default if the LABEL section is present */
if(loadLabel(class->labels[class->numlabels]) == -1) return(-1);
class->numlabels++;
break;
case(LEADER):
if(loadLeader(&(class->leader)) == -1) return(-1);
break;
case(MAXSCALE):
case(MAXSCALEDENOM):
if(getDouble(&(class->maxscaledenom)) == -1) return(-1);
break;
case(METADATA):
if(loadHashTable(&(class->metadata)) != MS_SUCCESS) return(-1);
break;
case(MINSCALE):
case(MINSCALEDENOM):
if(getDouble(&(class->minscaledenom)) == -1) return(-1);
break;
case(MINFEATURESIZE):
if(getInteger(&(class->minfeaturesize)) == -1) return(-1);
break;
case(NAME):
if(getString(&class->name) == MS_FAILURE) return(-1);
break;
case(STATUS):
if((class->status = getSymbol(2, MS_ON,MS_OFF)) == -1) return(-1);
break;
case(STYLE):
if(msGrowClassStyles(class) == NULL)
return(-1);
initStyle(class->styles[class->numstyles]);
if(loadStyle(class->styles[class->numstyles]) != MS_SUCCESS) return(-1);
class->numstyles++;
break;
case(TEMPLATE):
if(getString(&class->template) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->template, msLookupHashTable(&(class->validation), "template"), msLookupHashTable(&(layer->validation), "template"), msLookupHashTable(&(map->web.validation), "template"), map->templatepattern) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based TEMPLATE configuration failed pattern validation." , "loadClass()");
msFree(class->template);
class->template=NULL;
return(-1);
}
}
break;
case(TEXT):
if(loadExpression(&(class->text)) == -1) return(-1); /* loadExpression() cleans up previously allocated expression */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->text.string, msLookupHashTable(&(class->validation), "text"), msLookupHashTable(&(layer->validation), "text"), msLookupHashTable(&(map->web.validation), "text"), NULL) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based TEXT configuration failed pattern validation." , "loadClass()");
freeExpression(&(class->text));
return(-1);
}
}
if((class->text.type != MS_STRING) && (class->text.type != MS_EXPRESSION)) {
msSetError(MS_MISCERR, "Text expressions support constant or tagged replacement strings." , "loadClass()");
return(-1);
}
break;
case(TITLE):
if(getString(&class->title) == MS_FAILURE) return(-1); /* getString() cleans up previously allocated string */
if(msyysource == MS_URL_TOKENS) {
if(msValidateParameter(class->title, msLookupHashTable(&(class->validation), "title"), msLookupHashTable(&(layer->validation), "title"), msLookupHashTable(&(map->web.validation), "title"), NULL) != MS_SUCCESS) {
msSetError(MS_MISCERR, "URL-based TITLE configuration failed pattern validation." , "loadClass()");
msFree(class->title);
class->title=NULL;
return(-1);
}
}
break;
case(TYPE):
if((class->type = getSymbol(6, MS_LAYER_POINT,MS_LAYER_LINE,MS_LAYER_RASTER,MS_LAYER_POLYGON,MS_LAYER_ANNOTATION,MS_LAYER_CIRCLE)) == -1) return(-1);
break;
/*
** for backwards compatability, these are shortcuts for style 0
*/
case(BACKGROUNDCOLOR):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(loadColor(&(class->styles[0]->backgroundcolor), NULL) != MS_SUCCESS) return(-1);
break;
case(COLOR):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(loadColor(&(class->styles[0]->color), NULL) != MS_SUCCESS) return(-1);
class->numstyles = 1; /* must *always* set a color or outlinecolor */
break;
case(MAXSIZE):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(getDouble(&(class->styles[0]->maxsize)) == -1) return(-1);
break;
case(MINSIZE):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(getDouble(&(class->styles[0]->minsize)) == -1) return(-1);
break;
case(OUTLINECOLOR):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(loadColor(&(class->styles[0]->outlinecolor), NULL) != MS_SUCCESS) return(-1);
class->numstyles = 1; /* must *always* set a color, symbol or outlinecolor */
break;
case(SIZE):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if(getDouble(&(class->styles[0]->size)) == -1) return(-1);
break;
case(SYMBOL):
if (msMaybeAllocateClassStyle(class, 0)) return MS_FAILURE;
if((state = getSymbol(2, MS_NUMBER,MS_STRING)) == -1) return(-1);
if(state == MS_NUMBER)
class->styles[0]->symbol = (int) msyynumber;
else {
if (class->styles[0]->symbolname != NULL)
msFree(class->styles[0]->symbolname);
class->styles[0]->symbolname = msStrdup(msyystring_buffer);
class->numstyles = 1;
}
break;
/*
** for backwards compatability, these are shortcuts for style 1
*/
case(OVERLAYBACKGROUNDCOLOR):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(loadColor(&(class->styles[1]->backgroundcolor), NULL) != MS_SUCCESS) return(-1);
break;
case(OVERLAYCOLOR):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(loadColor(&(class->styles[1]->color), NULL) != MS_SUCCESS) return(-1);
class->numstyles = 2; /* must *always* set a color, symbol or outlinecolor */
break;
case(OVERLAYMAXSIZE):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(getDouble(&(class->styles[1]->maxsize)) == -1) return(-1);
break;
case(OVERLAYMINSIZE):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(getDouble(&(class->styles[1]->minsize)) == -1) return(-1);
break;
case(OVERLAYOUTLINECOLOR):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(loadColor(&(class->styles[1]->outlinecolor), NULL) != MS_SUCCESS) return(-1);
class->numstyles = 2; /* must *always* set a color, symbol or outlinecolor */
break;
case(OVERLAYSIZE):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if(getDouble(&(class->styles[1]->size)) == -1) return(-1);
break;
case(OVERLAYSYMBOL):
if (msMaybeAllocateClassStyle(class, 1)) return MS_FAILURE;
if((state = getSymbol(2, MS_NUMBER,MS_STRING)) == -1) return(-1);
if(state == MS_NUMBER)
class->styles[1]->symbol = (int) msyynumber;
else {
if (class->styles[1]->symbolname != NULL)
msFree(class->styles[1]->symbolname);
class->styles[1]->symbolname = msStrdup(msyystring_buffer);
}
class->numstyles = 2;
break;
case(VALIDATION):
if(loadHashTable(&(class->validation)) != MS_SUCCESS) return(-1);
break;
default:
if(strlen(msyystring_buffer) > 0) {
msSetError(MS_IDENTERR, "Parsing error near (%s):(line %d)", "loadClass()", msyystring_buffer, msyylineno);
return(-1);
} else {
return(0); /* end of a string, not an error */
}
}
}
}
static int classResolveSymbolNames(classObj *class)
{
int i,j;
/* step through styles and labels to resolve symbol names */
/* class styles */
for(i=0; i<class->numstyles; i++) {
if(class->styles[i]->symbolname) {
if((class->styles[i]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->styles[i]->symbolname, MS_TRUE)) == -1) {
msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class, style %d of layer %s.", "classResolveSymbolNames()", class->styles[i]->symbolname, i, class->layer->name);
return MS_FAILURE;
}
}
}
/* label styles */
for(i=0; i<class->numlabels; i++) {
for(j=0; j<class->labels[i]->numstyles; j++) {
if(class->labels[i]->styles[j]->symbolname) {
if((class->labels[i]->styles[j]->symbol = msGetSymbolIndex(&(class->layer->map->symbolset), class->labels[i]->styles[j]->symbolname, MS_TRUE)) == -1) {
msSetError(MS_MISCERR, "Undefined symbol \"%s\" in class, label style %d of layer %s.", "classResolveSymbolNames()", class->labels[i]->styles[j]->symbolname, j, class->layer->name);
return MS_FAILURE;
}
}
}
}
return MS_SUCCESS;
}
int msUpdateClassFromString(classObj *class, char *string, int url_string)
{
if(!class || !string) return MS_FAILURE;
msAcquireLock( TLOCK_PARSER );
if(url_string)
msyystate = MS_TOKENIZE_URL_STRING;
else
msyystate = MS_TOKENIZE_STRING;
msyystring = string;
msyylex(); /* sets things up, but doesn't process any tokens */
msyylineno = 1; /* start at line 1 */
if(loadClass(class, class->layer) == -1) {
msReleaseLock( TLOCK_PARSER );
return MS_FAILURE; /* parse error */;
}
msReleaseLock( TLOCK_PARSER );
msyylex_destroy();
if(classResolveSymbolNames(class) != MS_SUCCESS) return MS_FAILURE;
return MS_SUCCESS;
}
static void writeClass(FILE *stream, int indent, classObj *class)
{
int i;
if(class->status == MS_DELETE) return;
indent++;
writeBlockBegin(stream, indent, "CLASS");
writeString(stream, indent, "NAME", NULL, class->name);
writeString(stream, indent, "GROUP", NULL, class->group);
writeNumber(stream, indent, "DEBUG", 0, class->debug);
writeExpression(stream, indent, "EXPRESSION", &(class->expression));
writeString(stream, indent, "KEYIMAGE", NULL, class->keyimage);
for(i=0; i<class->numlabels; i++) writeLabel(stream, indent, class->labels[i]);
writeLeader(stream,indent,&(class->leader));
writeNumber(stream, indent, "MAXSCALEDENOM", -1, class->maxscaledenom);
writeHashTable(stream, indent, "METADATA", &(class->metadata));
writeNumber(stream, indent, "MINSCALEDENOM", -1, class->minscaledenom);
writeNumber(stream, indent, "MINFEATURESIZE", -1, class->minfeaturesize);
writeKeyword(stream, indent, "STATUS", class->status, 1, MS_OFF, "OFF");
for(i=0; i<class->numstyles; i++) writeStyle(stream, indent, class->styles[i]);
writeString(stream, indent, "TEMPLATE", NULL, class->template);
writeExpression(stream, indent, "TEXT", &(class->text));
writeString(stream, indent, "TITLE", NULL, class->title);
writeBlockEnd(stream, indent, "CLASS");
}
/*
** Initialize, load and free a single layer structure
*/
int initLayer(layerObj *layer, mapObj *map)
{
if (layer==