Opened 18 years ago

Last modified 13 years ago

#1949 assigned defect

mapserver WMS time support in postgis

Reported by: dnadeau@… Owned by: pramsey
Priority: high Milestone: FUTURE
Component: WMS Server Version: 5.0
Severity: normal Keywords:
Cc:

Description

As requested by Assefa I enter a bug for this issue.

Here what it says in the WMS 1.1.1 doc.

"If a request does includes an imprecise dimensional value, and nearest value
behavior has been declared, then the server shall compute and send the nearest
available value. The value shall be rounded, not merely truncated."

Right now, mapserver is using date_trunc function to allow the user to specify
months, days, hours or minutes.  I think this could be enhanced to get the
nearest time value in Postgres.  For example,  NASA GES has 3 hourly data.  If
the user  specify 2006-09-28T02:00  a blank image is return, the user needs to
be exact 2006-09-28T03:00.  Same thing with 10-day data,  2006-09-03 will 
return a  blank image.  User have to hit the right date 2006-08-10, 2006-08-20,
2006-08-31, 2006-09-10,.... 

Example
Tropical Rain data 3B42_V6_10_DAY:
http://g0hep12u.ecs.nasa.gov/mapserv-bin/wms_ogc?SERVICE=WMS&VERSION=1.1.1&REQUEST=Getmap&layers=bluemarble,TRMM_3B42_V6_10_DAY&BBOX=-180,-50,180,50&TIME=2005-08-20

blank image
http://g0hep12u.ecs.nasa.gov/mapserv-bin/wms_ogc?SERVICE=WMS&VERSION=1.1.1&REQUEST=Getmap&layers=bluemarble,TRMM_3B42_V6_10_DAY&BBOX=-180,-50,180,50&TIME=2005-08-20T04:00

Change History (5)

comment:1 by dnadeau@…, 18 years ago

Cc: Steve.Lime@… added
Here is the code.  

I had to bypass the lexer for my sql queries.  The lexer would troncate the
queries.  

As well, I had to copy some code from the msPOSTGISLayerParseData to retrieve
the table name.  I could not call the funtion directly for the connection is not
open when LayerSetTimeFilter is called.


The philosophy is the same, only the SQL queries have changed.   The queries use
the closest time found in the database.  The buffer needs to be much bigger...

int msPOSTGISLayerSetTimeFilter(layerObj *lp, 
                                const char *timestring, 
                                const char *timefield)
{
    char *tmpstimestring = NULL;
    char *timeresolution = NULL;
    int timesresol = -1;
    char **atimes, **tokens = NULL;
    int numtimes=0,i=0,ntmp=0,nlength=0;
    char buffer[65535];
    char szTimeRequest[256];
    char szTimeRequest1[256];
    char szTimeRequest2[256];
    char    *table_name = NULL;

/* -------------------------------------------------------------------- */
/*      This query will select the closest time to the time             */
/*      specified by the user.                                          */
/* -------------------------------------------------------------------- */
    char *pszDiscreteTimeSQLRequest = "%s=(select %s from %s "
	"where abs(extract(epoch from %s) - "
	"extract(epoch from '%s'::TIMESTAMP)) in  "
	"(select min(abs(extract(epoch from %s) - "
	"extract(epoch from '%s'::TIMESTAMP))) from %s) limit 1) ";

    char *pszBetweenTimeSQLRequest = "%s in (select %s from %s "
	"where (extract(epoch from %s) - "
	"extract(epoch from '%s'::TIMESTAMP) > 0) "
	"and (extract(epoch from %s) - "
	"extract(epoch from '%s'::TIMESTAMP) < 0)";

/* -------------------------------------------------------------------- */
/*     We need the table name so I copied that code from ParseData      */
/* -------------------------------------------------------------------- */
    /* Scan for the table or sub-select clause */
    char *data=NULL;
    char *pos_scn=NULL;
    char *pos_opt=NULL;
    char *pos_srid=NULL;
    char *pos_urid=NULL;
    data=lp->data;
    
    pos_srid = strstrIgnoreCase(data, " using SRID=");
    pos_urid = strstrIgnoreCase(data, " using unique ");
    
    if ( !pos_srid && !pos_urid ) { 
	pos_opt = pos_urid; 
    }
    else if ( pos_srid && pos_urid )  { 
	pos_opt = (pos_srid > pos_urid)? pos_urid : pos_srid;
    }
    else { pos_opt = (pos_srid > pos_urid) ? pos_srid : pos_urid; }
    
    pos_scn = strstrIgnoreCase(data, " from ");
    
    if(!pos_scn) {
	msSetError(MS_QUERYERR, DATA_ERROR_MESSAGE, "msPOSTGISLayerParseData()", "Error
parsing POSTGIS data variable.  Must contain 'geometry_column from table_name'
or 'geom from (subselect) as foo' (couldnt find ' from ').  More help: \n\n", data);
	
	return MS_FAILURE;
    }
    
    /* Copy out the table name or sub-select clause */
    if(!pos_opt) {
	pos_opt = pos_scn + strlen(pos_scn);
    }
    table_name = (char *) malloc((pos_opt - (pos_scn + 6)) + 1);
    strncpy(table_name, pos_scn + 6, pos_opt - (pos_scn + 6));
    (table_name)[pos_opt - (pos_scn + 6)] = 0;
    
    if(strlen(table_name) < 1 ) {
	msSetError(MS_QUERYERR, DATA_ERROR_MESSAGE, "msPOSTGISLayerParseData()", "Error
parsing POSTGIS data variable.  Must contain 'geometry_column from table_name'
or 'geom from (subselect) as foo' (couldnt find a geometry_column or
table/subselect).  More help: \n\n", data);
	
	return MS_FAILURE;
    }



    buffer[0] = '\0';

    if (!lp || !timestring || !timefield)
      return MS_FALSE;

/* -------------------------------------------------------------------- */
/*      Discrete time                                                   */
/* -------------------------------------------------------------------- */
    if (strstr(timestring, ",") == NULL && 
        strstr(timestring, "/") == NULL) /* discrete time */
      tmpstimestring = strdup(timestring);
    else
    {
        atimes = split (timestring, ',', &numtimes);
        if (atimes == NULL || numtimes < 1)
          return MS_FALSE;

        if (numtimes >= 1)
        {
            tokens = split(atimes[0],  '/', &ntmp);
            if (ntmp == 2) /* ranges */
            {
                tmpstimestring = strdup(tokens[0]);
                msFreeCharArray(tokens, ntmp);
            }
            else if (ntmp == 1) /* multiple times */
            {
                tmpstimestring = strdup(atimes[0]);
            }
        }
        msFreeCharArray(atimes, numtimes);
    }
    if (!tmpstimestring)
      return MS_FALSE;
        
    timesresol = msTimeGetResolution((const char*)tmpstimestring);
    if (timesresol < 0)
      return MS_FALSE;

    free(tmpstimestring);

    switch (timesresol)
    {
        case (TIME_RESOLUTION_SECOND):
          timeresolution = strdup("second");
          break;

        case (TIME_RESOLUTION_MINUTE):
          timeresolution = strdup("minute");
          break;

        case (TIME_RESOLUTION_HOUR):
          timeresolution = strdup("hour");
          break;

        case (TIME_RESOLUTION_DAY):
          timeresolution = strdup("day");
          break;

        case (TIME_RESOLUTION_MONTH):
          timeresolution = strdup("month");
          break;

        case (TIME_RESOLUTION_YEAR):
          timeresolution = strdup("year");
          break;

        default:
          break;
    }

    if (!timeresolution)
      return MS_FALSE;

    strcpy( szTimeRequest, timestring );

    /* where date_trunc('month', _cwctstamp) = '2004-08-01' */
    if (strstr(timestring, ",") == NULL && 
        strstr(timestring, "/") == NULL) /* discrete time */
    {
        if(lp->filteritem) free(lp->filteritem);
        lp->filteritem = strdup(timefield);
        if (&lp->filter)
          freeExpression(&lp->filter);
        
        /* make sure that the timestring is complete and acceptable */
        /* to the date_trunc function : */
        /* - if the resolution is year (2004) or month (2004-01),  */
        /* a complete string for time would be 2004-01-01 */
        /* - if the resolluion is hour or minute (2004-01-01 15), a  */
        /* complete time is 2004-01-01 15:00:00 */
        if (strcasecmp(timeresolution, "year")==0)
        {
            nlength = strlen(timestring);
            if (timestring[nlength-1] != '-')
              strcat(szTimeRequest,"-01-01");
            else
              strcat(szTimeRequest,"01-01");
        }            
        else if (strcasecmp(timeresolution, "month")==0)
        {
            nlength = strlen(timestring);
            if (timestring[nlength-1] != '-')
              strcat(szTimeRequest,"-01");
            else
              strcat(szTimeRequest,"01");
        }            
        else if (strcasecmp(timeresolution, "hour")==0)
        {
            nlength = strlen(timestring);
            if (timestring[nlength-1] != ':')
              strcat(szTimeRequest,":00:00");
            else
              strcat(szTimeRequest,"00:00");
        }            
        else if (strcasecmp(timeresolution, "minute")==0)
        {
            nlength = strlen(timestring);
            if (timestring[nlength-1] != ':')
              strcat(szTimeRequest,":00");
            else
              strcat(szTimeRequest,"00");
        }            

	
        sprintf(buffer, pszDiscreteTimeSQLRequest,
		timefield,
                timefield, table_name, timefield, szTimeRequest,
                timefield, szTimeRequest,table_name );

        /* loadExpressionString(&lp->filter, (char *)timestring); */
        /* loadExpressionString(&lp->filter, buffer); */
	lp->filter.type = MS_STRING;
	lp->filter.string = strdup(buffer); 
	msDebug("%s\n",lp->filter.string);
        free(timeresolution);
        return MS_TRUE;
    }
    


    atimes = split (timestring, ',', &numtimes);
    if (atimes == NULL || numtimes < 1)
      return MS_FALSE;

    if (numtimes >= 1)
    {
        /* check to see if we have ranges by parsing the first entry */
        tokens = split(atimes[0],  '/', &ntmp);

        if (ntmp == 2) /* ranges */
        {
            msFreeCharArray(tokens, ntmp);
            for (i=0; i<numtimes; i++)
            {
                tokens = split(atimes[i],  '/', &ntmp);
		msDebug("atimes[%d]=%s\n",i,atimes[i]);
                if (ntmp == 2)
                {
		    char tmpbuffer[2048];
                    if (strlen(buffer) > 0)
			strcat(buffer, " OR ");
		    else
			strcat( buffer, "(");

                    strcpy(szTimeRequest1, tokens[0]);
                    strcpy(szTimeRequest2, tokens[1]);

                    /* - if the resolution is year (2004) or month (2004-01),  */
                    /* a complete string for time would be 2004-01-01 */
                    /* - if the resolluion is hour or minute (2004-01-01 15), a  */
                    /* complete time is 2004-01-01 15:00:00 */
                    if (strcasecmp(timeresolution, "year")==0)
                    {

/* -------------------------------------------------------------------- */
/*      First date                                                      */
/* -------------------------------------------------------------------- */
                        nlength = strlen(tokens[0]);
                        if (tokens[0][nlength-1] != '-')
                          strcat(szTimeRequest1,"-01-01");
                        else
                          strcat(szTimeRequest1,"01-01");

/* -------------------------------------------------------------------- */
/*      Second date                                                     */
/* -------------------------------------------------------------------- */
                        nlength = strlen(tokens[1]);
                        if (tokens[1][nlength-1] != '-')
                          strcat(szTimeRequest2,"-01-01");
                        else
                          strcat(szTimeRequest2,"01-01");

                    }            

                    else if (strcasecmp(timeresolution, "month")==0)
                    {
                        nlength = strlen(tokens[0]);
                        if (tokens[0][nlength-1] != '-')
                          strcat(szTimeRequest1,"-01");
                        else
                          strcat(szTimeRequest1,"01");

                        nlength = strlen(tokens[1]);
                        if (tokens[1][nlength-1] != '-')
                          strcat(szTimeRequest2,"-01");
                        else
                          strcat(szTimeRequest2,"01");

                    }            
                    else if (strcasecmp(timeresolution, "hour")==0)
                    {
                        nlength = strlen(tokens[0]);
                        if (tokens[0][nlength-1] != ':')
                          strcat(szTimeRequest1,":00:00");
                        else
                          strcat(szTimeRequest1,"00:00");

                        nlength = strlen(tokens[1]);
                        if (tokens[1][nlength-1] != ':')
                          strcat(szTimeRequest2,":00:00");
                        else
                          strcat(szTimeRequest2,"00:00");

                    }            
                    else if (strcasecmp(timeresolution, "minute")==0)
                    {
                        nlength = strlen(tokens[0]);
                        if (tokens[0][nlength-1] != ':')
                          strcat(szTimeRequest1,":00");
                        else
                          strcat(szTimeRequest1,"00");

                        nlength = strlen(tokens[1]);
                        if (tokens[1][nlength-1] != ':')
                          strcat(szTimeRequest2,":00");
                        else
                          strcat(szTimeRequest2,"00");
                    }            



/* -------------------------------------------------------------------- */
/*      Build time filter requested                                     */
/* -------------------------------------------------------------------- */
		    sprintf( tmpbuffer, pszBetweenTimeSQLRequest,
			     timefield, timefield, 
			     table_name, 
			     timefield, 
			     szTimeRequest1,
			     timefield, 
			     szTimeRequest2,
			     table_name );
		    strcat( buffer, tmpbuffer);
		    strcat(buffer, ")");

                }
                 
                msFreeCharArray(tokens, ntmp);
            }
            if (strlen(buffer) > 0)
		strcat(buffer, ")");
	    msDebug("%s\n",buffer);
		    

        }
        else if (ntmp == 1) /* multiple times */
        {
	    char tmpbuffer[65535];
            msFreeCharArray(tokens, ntmp);
            strcat(buffer, "(");
            for (i=0; i<numtimes; i++)
            {
                if (i > 0)
                  strcat(buffer, " OR ");

                strcat(buffer, "(");
                  
		strcpy(szTimeRequest, atimes[i]);
                /* make sure that the timestring is complete and acceptable */
                /* to the date_trunc function : */
                /* - if the resolution is year (2004) or month (2004-01),  */
                /* a complete string for time would be 2004-01-01 */
                /* - if the resolluion is hour or minute (2004-01-01 15), a  */
                /* complete time is 2004-01-01 15:00:00 */
                if (strcasecmp(timeresolution, "year")==0)
                {
                    nlength = strlen(atimes[i]);
                    if (atimes[i][nlength-1] != '-')
                      strcat(szTimeRequest,"-01-01");
                    else
                      strcat(szTimeRequest,"01-01");
                }            
                else if (strcasecmp(timeresolution, "month")==0)
                {
                    nlength = strlen(atimes[i]);
                    if (atimes[i][nlength-1] != '-')
                      strcat(szTimeRequest,"-01");
                    else
                      strcat(szTimeRequest,"01");
                }            
                else if (strcasecmp(timeresolution, "hour")==0)
                {
                    nlength = strlen(atimes[i]);
                    if (atimes[i][nlength-1] != ':')
                      strcat(szTimeRequest,":00:00");
                    else
                      strcat(szTimeRequest,"00:00");
                }            
                else if (strcasecmp(timeresolution, "minute")==0)
                {
                    nlength = strlen(atimes[i]);
                    if (atimes[i][nlength-1] != ':')
                      strcat(szTimeRequest,":00");
                    else
                      strcat(szTimeRequest,"00");
                }            
/* -------------------------------------------------------------------- */
/*      Build time filter requested                                     */
/* -------------------------------------------------------------------- */
		    sprintf( tmpbuffer, pszDiscreteTimeSQLRequest,
			     timefield, timefield, 
			     table_name, 
			     timefield, 
			     szTimeRequest,
			     timefield, 
			     szTimeRequest,
			     table_name );
		    strcat( buffer, tmpbuffer);
		    strcat(buffer, ")");


            } 
            strcat(buffer, ")");
	    msDebug("%s\n",buffer);
        }
        else
        {
            msFreeCharArray(atimes, numtimes);
            return MS_FALSE;
        }

        msFreeCharArray(atimes, numtimes);

        /* load the string to the filter */
        if (strlen(buffer) > 0)
        {
            if(lp->filteritem) 
              free(lp->filteritem);
            lp->filteritem = strdup(timefield);     
            if (&lp->filter)
              freeExpression(&lp->filter);
	    lp->filter.type = MS_STRING;
	    lp->filter.string = strdup(buffer); 
	    msDebug("%s\n",lp->filter.string);
	    //            loadExpressionString(&lp->filter, buffer);
        }

        free(timeresolution);
        return MS_TRUE;
                 
    }
    free( table_name );
    return MS_FALSE;
}

comment:2 by pramsey, 16 years ago

Owner: changed from mapserverbugs to pramsey

comment:3 by pramsey, 16 years ago

Milestone: 5.4 release
Status: newassigned

comment:4 by pramsey, 15 years ago

Milestone: 5.6 release6.0 release

comment:5 by pramsey, 13 years ago

Milestone: 6.0 releaseFUTURE
Note: See TracTickets for help on using tickets.