#!/usr/bin/env python
###############################################################################
# $Id$
#
# Project:  MapServer
# Purpose:  Test suite for MapServer Raster Query capability.
# Author:   Frank Warmerdam, warmerdam@pobox.com
#
###############################################################################
#  Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
# 
#  Permission is hereby granted, free of charge, to any person obtaining a
#  copy of this software and associated documentation files (the "Software"),
#  to deal in the Software without restriction, including without limitation
#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
#  and/or sell copies of the Software, and to permit persons to whom the
#  Software is furnished to do so, subject to the following conditions:
# 
#  The above copyright notice and this permission notice shall be included
#  in all copies or substantial portions of the Software.
# 
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
#  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
#  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#  DEALINGS IN THE SOFTWARE.
###############################################################################
# 
# $Log$
# Revision 1.10  2006/02/13 17:19:32  frank
# Added msCleanup call.
#
# Revision 1.9  2004/11/02 02:09:25  frank
# adjust for renaming of values to value_list
#
# Revision 1.8  2004/11/02 01:32:45  frank
# Added extra tests for projection cases.
#
# Revision 1.7  2004/11/01 21:33:42  frank
# Added reference to raster query classification bug (1021) in rqtest_13.
#
# Revision 1.6  2004/10/22 15:42:46  frank
# updated to latest API
#
# Revision 1.5  2004/07/27 21:51:42  frank
# reverted to *not* use next generation names, no longer promoted
#
# Revision 1.4  2004/05/28 20:28:54  frank
# Change rqtest_6 to operate against the map to ensure that works.
#
# Revision 1.3  2004/05/27 17:34:46  frank
# Updated to work with new or old APIs and names.
#
# Revision 1.2  2004/05/13 03:03:28  frank
# added header
#

import sys
import math

sys.path.append( '../pymod' )
import pmstestlib

import mapscript

###############################################################################
# Dump query result set ... used when debugging this test script.

def dumpResultSet( layer ):
    layer.open()
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
        
        print '(%d,%d)' % (result.shapeindex, result.tileindex)
        
        s = layer.getFeature( result.shapeindex, result.tileindex )
        for i in range(layer.numitems):
            print '%s: %s' % (layer.getItem(i), s.getValue(i))
            
    layer.close()

###############################################################################
# Open map and get working layer.

def rqtest_1():
    
    pmstestlib.map = mapscript.mapObj('../gdal/tileindex.map')
    pmstestlib.layer = pmstestlib.map.getLayer(0)

    return 'success'

###############################################################################
# Execute region query.

def rqtest_2():

    line = mapscript.lineObj()
    line.add( mapscript.pointObj( 35, 25 ) )
    line.add( mapscript.pointObj( 45, 25 ) )
    line.add( mapscript.pointObj( 45, 35 ) )
    line.add( mapscript.pointObj( 35, 25 ) )

    poly = mapscript.shapeObj( mapscript.MS_SHAPE_POLYGON )
    poly.add( line )

    pmstestlib.layer.queryByShape( pmstestlib.map, poly )

    return 'success'

###############################################################################
# Scan results, checking count and the first shape information.

def rqtest_3():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 55:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 55) )
        return 'fail'

    #########################################################################
    # Check first shape attributes.
    
    result = layer.getResult( 0 )
    s = layer.getFeature( result.shapeindex, result.tileindex )
    
    if pmstestlib.check_items( layer, s,
                               [('value_0','115'),
                                ('red','115'),
                                ('green','115'),
                                ('blue','115'),
                                ('value_list','115'),
                                ('x','39.5'),
                                ('y','29.5')] ) == 0:
        return 'fail'

    #########################################################################
    # Check first shape geometry.
    if s.type != mapscript.MS_SHAPE_POINT:
        pmstestlib.post_reason( 'raster query result is not a point.' )
        return 'fail'

    if s.numlines != 1:
        pmstestlib.post_reason( 'raster query has other than 1 lines.' )
        return 'fail'

    try:
        l = s.getLine( 0 )
    except:
        l = s.get( 0 )
    if l.numpoints != 1:
        pmstestlib.post_reason( 'raster query has other than 1 points.' )
        return 'fail'

    try:
        p = l.getPoint(0)
    except:
        p = l.get(0)
    if p.x != 39.5 or p.y != 29.5:
        pmstestlib.post_reason( 'got wrong point location.' )
        return 'fail'
    
    #########################################################################
    # Check last shape attributes.

    result = layer.getResult( 54 )
    s = layer.getFeature( result.shapeindex, result.tileindex )

    if pmstestlib.check_items( layer, s,
                               [('value_0','132'),
                                ('x','44.5'),
                                ('y','25.5')] ) == 0:
        return 'fail'
    
    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Execute multiple point query, and check result.

def rqtest_4():

    pnt = mapscript.pointObj()
    pnt.x = 35.5
    pnt.y = 25.5
    
    pmstestlib.layer.queryByPoint( pmstestlib.map, pnt, mapscript.MS_MULTIPLE,
                                   1.25 )

    return 'success'

###############################################################################
# Scan results, checking count and the first shape information.

def rqtest_5():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 9:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 9) )
        return 'fail'

    #########################################################################
    # Check first shape attributes.
    
    result = layer.getResult( 0 )
    s = layer.getFeature( result.shapeindex, result.tileindex )
    
    if pmstestlib.check_items( layer, s,
                               [('value_0','123'),
                                ('x','34.5'),
                                ('y','26.5')] ) == 0:
        return 'fail'
    
    #########################################################################
    # Check last shape attributes.

    result = layer.getResult( 8 )
    s = layer.getFeature( result.shapeindex, result.tileindex )

    if pmstestlib.check_items( layer, s,
                               [('value_0','107'),
                                ('x','36.5'),
                                ('y','24.5')] ) == 0:
        return 'fail'
    
    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Execute multiple point query, and check result.  Also operates on map,
# instead of layer.

def rqtest_6():

    pnt = mapscript.pointObj()
    pnt.x = 35.2
    pnt.y = 25.3
    
    pmstestlib.map.queryByPoint( pnt, mapscript.MS_SINGLE, 10.0 )

    return 'success'

###############################################################################
# Scan results, checking count and the first shape information.

def rqtest_7():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 1:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 1) )
        return 'fail'

    #########################################################################
    # Check first shape attributes.
    
    result = layer.getResult( 0 )
    s = layer.getFeature( result.shapeindex, result.tileindex )
    
    if pmstestlib.check_items( layer, s,
                               [('value_0','115'),
                                ('x','35.5'),
                                ('y','25.5')] ) == 0:
        return 'fail'
    
    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Execute multiple point query, and check result.

def rqtest_8():

    rect = mapscript.rectObj()
    rect.minx = 35
    rect.maxx = 45
    rect.miny = 25
    rect.maxy = 35
    
    pmstestlib.layer.queryByRect( pmstestlib.map, rect )

    return 'success'

###############################################################################
# Scan results, checking count and the first shape information.

def rqtest_9():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 100:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 100) )
        return 'fail'

    #########################################################################
    # Check first shape attributes.
    
    result = layer.getResult( 0 )
    s = layer.getFeature( result.shapeindex, result.tileindex )
    
    if pmstestlib.check_items( layer, s,
                               [('value_0','148'),
                                ('red','148'),
                                ('green','148'),
                                ('blue','148'),
                                ('value_list','148'),
                                ('x','35.5'),
                                ('y','34.5')] ) == 0:
        return 'fail'
    
    #########################################################################
    # Check last shape attributes.

    result = layer.getResult( 99 )
    s = layer.getFeature( result.shapeindex, result.tileindex )

    if pmstestlib.check_items( layer, s,
                               [('value_0','132'),
                                ('red','132'),
                                ('green','132'),
                                ('blue','132'),
                                ('value_list','132'),
                                ('x','44.5'),
                                ('y','25.5')] ) == 0:
        return 'fail'
    
    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Close old map, and open a classified map and post a point query.

def rqtest_10():

    pmstestlib.layer = None
    pmstestlib.map = None

    pmstestlib.map = mapscript.mapObj('../gdal/classtest1.map')
    pmstestlib.layer = pmstestlib.map.getLayer(0)
    
    pnt = mapscript.pointObj()
    pnt.x = 88.5
    pnt.y = 7.5
    
    pmstestlib.layer.queryByPoint( pmstestlib.map, pnt, mapscript.MS_SINGLE,
                                   10.0 )

    return 'success'

###############################################################################
# Scan results.  This query is for a transparent pixel within the "x" of
# the cubewerx logo.  In the future the raster query may well stop returning
# "offsite" pixels and we will need to update this test.

def rqtest_11():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 1:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 1) )
        return 'fail'

    #########################################################################
    # Check first shape attributes.
    
    result = layer.getResult( 0 )
    s = layer.getFeature( result.shapeindex, result.tileindex )
    
    if pmstestlib.check_items( layer, s,
                               [('value_0','0'),
                                ('red','-255'),
                                ('green','-255'),
                                ('blue','-255'),
                                ('class','Text'),
                                ('x','88.5'),
                                ('y','7.5')] ) == 0:
        return 'fail'
    
    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Issue another point query, on colored text. 

def rqtest_12():

    pnt = mapscript.pointObj()
    pnt.x = 13.5
    pnt.y = 36.5
    
    pmstestlib.layer.queryByPoint( pmstestlib.map, pnt, mapscript.MS_SINGLE,
                                   10.0 )

    dumpResultSet( pmstestlib.layer )

    return 'success'

###############################################################################
# Scan results.  This query is for a pixel at a grid intersection.  This
# pixel should be classified as grid and returned, but currently it is
# misclassified as transparent and discarded.  The reasons are described in:
# http://mapserver.gis.umn.edu/bugs/show_bug.cgi?id=1021
#
# Till this is fixed, we consider returning zero shapes an acceptable result.

def rqtest_13():
    layer = pmstestlib.layer
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break
    
        count = count + 1

    if count != 0:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 0) )
        return 'fail'

    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Revert to tileindex.map and do a test where we force reprojection

def rqtest_14():

    pmstestlib.map = mapscript.mapObj('../gdal/tileindex.map')
    pmstestlib.layer = pmstestlib.map.getLayer(0)

    pmstestlib.map.setProjection("+proj=utm +zone=30 +datum=WGS84")

    pnt = mapscript.pointObj()
    pnt.x =  889690
    pnt.y =   55369
    
    pmstestlib.layer.queryByPoint( pmstestlib.map, pnt, mapscript.MS_MULTIPLE,
                                   200000.0 )

    return 'success'

###############################################################################
# Check result count, and that the results are within the expected distance.
# This also implicitly verifies the results were reprojected back to UTM
# coordinates from the lat long system of the layer.

def rqtest_15():
    
    layer = pmstestlib.layer
    
    pnt = mapscript.pointObj()
    pnt.x =  889690
    pnt.y =   55369
    
    #########################################################################
    # Check result count.
    layer.open()
    count = 0
    for i in range(1000):
        result = layer.getResult( i )
        if result is None:
            break

        count = count + 1

        s = layer.getFeature( result.shapeindex, result.tileindex )
        x = float(pmstestlib.get_item_value( layer, s, 'x' ))
        y = float(pmstestlib.get_item_value( layer, s, 'y' ))
        dist_sq = (x-pnt.x) * (x-pnt.x) + (y-pnt.y) * (y-pnt.y)
        dist = math.pow(dist_sq,0.5)
        if dist > 200000.0:
            pmstestlib.post_reason(
                'Got point %f from target, but tolerance was 200000.0.' % dst )
            return 'fail'
                
    
    if count != 4:
        pmstestlib.post_reason( 'got %d results instead of expected %d.' \
                             % (count, 10) )
        return 'fail'

    layer.close() 
    layer.close() # discard resultset.

    return 'success'
    
###############################################################################
# Cleanup.

def rqtest_cleanup():
    pmstestlib.layer = None
    pmstestlib.map = None
    return 'success'

test_list = [
    rqtest_1,
    rqtest_2,
    rqtest_3,
    rqtest_4,
    rqtest_5,
    rqtest_6,
    rqtest_7,
    rqtest_8,
    rqtest_9,
    rqtest_10,
    rqtest_11,
    rqtest_12,
    rqtest_13,
    rqtest_14,
    rqtest_15,
    rqtest_cleanup ]

if __name__ == '__main__':

    pmstestlib.setup_run( 'rqtest' )

    pmstestlib.run_tests( test_list )

    pmstestlib.summarize()

    mapscript.msCleanup()

