Location: PHPKode > projects > jjfmapper > jjfmapper/lib/jjfmapper.class.php
<?php #-*-Mode: php; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/*
    JJFMapper, a cartography program for PHP 4.
    Copyright (C) 2004  John J Foerch

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

# JJFMapper for PHP
# Written for compatibility with php 4.2.3.

include_once(JJFM_LIBDIR.'/helpers.php');
include_once(JJFM_LIBDIR.'/instruction_document_parser.class.php');
include_once(JJFM_LIBDIR.'/built_in_icons.php');
include_once(JJFM_LIBDIR.'/projection.php');

//The JJFMapper class is the class that
//you will instantiate to create your map.
class JJFMapper {
    //check the $ok field to find out if the object constructed successfully.
    var $ok=true;
    var $error=false;
    var $reportentries;

    var $idoc;

    var $geolayers=array();
    var $projection;
    var $supported_geoformats = array ();

    //symbol table
    //the symbol table contains values that can
    //be used in jjfmapper config files.
    var $symbols=array();

    //The constructor takes one argument.
    //The argument can be XML data or a file name.
    //The constructor does not draw the map.
    //It does initialize the projection and
    //geographic layer objects. After construction,
    //call jjfmapper->run() to draw the map.
    function JJFMapper($arg) {
        //Test if the content of $arg is xml data
        //by testing for the existence of the <
        //character.
        if (strpos($arg,'<') === false)
        {
            if (! file_exists ($arg))
            {
                //debug report not available at this point.
                $this->ok = false;
                $this->error = 'instruction document file not found.';
                return;
            }
            $f = fopen($arg,'r');
            $arg = fread($f,filesize($arg));
            fclose($f);
        }

        $this->init_symbols();

        $this->idoc = new instruction_document_parser ($arg,$this->symbols);
        if (! $this->idoc->ok)
        {
            $this->ok = false;
            if ($this->idoc->error !== false)
                $this->error = 'instruction document: '.$this->idoc->error;
            return;
        }

        if (defined ('DEBUG'))
        {
            include_once (JJFM_LIBDIR.'/debugreport.php');
            $this->reportentries = new debugreport();
        }

        //load cached projection data if available
        //or else construct the projection as normal
        if (defined ('HAVE_CACHED_PROJECTION'))
        {//we are caching and we have projection data
            //load the data
            $f = fopen ($this->idoc->cached_projection, 'rb');
            $s = fread ($f, filesize ($this->idoc->cached_projection));
            fclose ($f);
            $this->construct_projection ($s);
            return;
        }
        
        $this->get_supported_geoformats ();

        //Set up geolayers.
        //This entails loading the needed php files,
        //processing the attributes of each layer tag,
        //and constructing the geoformat objects.
        foreach ($this->idoc->layers as $layer_k => $layer_v) {
            $T = & $this->idoc->layers[$layer_k];///$T points to current layer

            $layer_format = strtolower($T['FORMAT']);
            if (! isset ($this->supported_geoformats[$layer_format]))
            {
                if (defined ('DEBUG'))
                {
                    $this->reportentries->add (
                        array ('warning' => 'unsupported layer type: '.
                               $layer_format));
                }
                continue;
            }
            
            //dynamic loading of geoformats
            $format_include_file = JJFM_LIBDIR."/geoformat_$layer_format.php";
            if (file_exists ($format_include_file))
            {
                include_once ($format_include_file);
            } else {
                if (defined ('DEBUG'))
                {
                    $this->reportentries->add (
                        array ('error' => 'include file not found: '.
                               $format_include_file));
                }
                continue;
            }
            
            $pass_options = $layer_v;
            
            //any data between the <layer> and </layer> will be
            //loaded into the 'inline' key of the options array

            //
            //check for cdata
            //
            //

            if (isset($T['cdata']))
            {
                $inline = $T['cdata'];
            }

            $layer_class = 'geoformat_'. $layer_format;
            
            if (! class_exists ($layer_class))
            {
                if (defined ('DEBUG'))
                {
                    $this->reportentries->add (
                        array ('warning' => "class \"$layer_class\" not ".
                               "found for layer $layer_k ($layer_format)"));
                }
                continue;
            }

            $pass_options = call_user_func_array (array($layer_class,
                                                        'parse_instructions'),
                                                  array($pass_options,
                                                        $this->symbols));
            if ($pass_options === false)
            {
                if (defined ('DEBUG'))
                {
                    $this->reportentries->add (
                        array ('warning' => 
                               "layer configuration failed for layer $layer_k".
                               " ($layer_format)"));
                }
                continue;
            }
            if (isset ($inline)) $pass_options['inline'] = $inline;
            
            $Y = new $layer_class($pass_options);
            if (! $Y->ok)
            {
                if (defined ('DEBUG'))
                {
                    $warning_msg = "layer construction failed for layer ".
                        "$layer_k ($layer_format)";
                    if ($Y->error !== false) $warning_msg .= ' error: '.
                                                 $Y->error;
                    $this->reportentries->add (
                        array ('warning' => $warning_msg));
                }
            } else {
                $this->geolayers[] = $Y;
            }
        }//done setting up geolayers
        
        //
        // fitting
        //

        if ($this->idoc->fit !== false)
        {
            $idx = $this->idoc->layer_names[$this->idoc->fit];
            if ($this->idoc->fit_spec == '')
            {
                list($latmin,$latmax,$lonmin,$lonmax) =
                    $this->geolayers[$idx]->bounds();
            } else {
                list($latmin,$latmax,$lonmin,$lonmax) =
                    $this->geolayers[$idx]->bounds($this->idoc->fit_spec);
            }
            $this->idoc->projection['xmin'] = $lonmin;
            $this->idoc->projection['xmax'] = $lonmax;
            $this->idoc->projection['ymin'] = $latmin;
            $this->idoc->projection['ymax'] = $latmax;
        }
        
        $this->construct_projection ($this->idoc->projection);

        //if we are caching, cache the projection
        if (defined ('JJFM_CACHING')) $this->write_projection_cache();
        //END setup of projection
    }
    
    function construct_projection ($arg) {
        if (is_string ($arg)) $arg = jjfProjection_parse_info ($arg);
        if ($arg === false)
        {
            $this->ok = false;
            $this->error = 'failed to parse projection parameters';
            return;
        }
        $this->projection = new jjfProjection ($arg);
        if (! $this->projection->ok)
        {
            if (isset ($this->projection->reportentries) && defined ('DEBUG'))
            {
                $this->reportentries->merge ($this->projection->reportentries);
            }
            $this->debugreport();
            $this->ok = false;
            $this->error = 'projection: '.$this->projection->error;
            return;
        }

    }


    function get_supported_geoformats () {
        $m = get_defined_constants ();
        $l = strlen ('JJFM_GEOFORMAT_');
        foreach ($m as $m_k => $m_v) {
            if (substr ($m_k, 0, $l) == 'JJFM_GEOFORMAT_')
            {
                $n = strtolower (substr ($m_k, $l));
                $this->supported_geoformats[$n] = true;
            }
        }
    }

    function init_symbols () {
        if (defined ('JJFM_BASEDIR')) $this->symbols['BASEDIR'] = JJFM_BASEDIR;
        if (defined ('JJFM_GEODIR')) $this->symbols['GEODIR'] = JJFM_GEODIR;
        //TODO: symbol for construction timestamp
    }


    //JJFMapper->run()
    //This is the function that draws the map.
    //Properly speaking, it is the function that
    //calls the functions that draw the map.
    function run() {
        if ($this->ok === false) return;
        //First, determine whether we are operating
        //in normal mode or cache mode.
        if (! defined ('JJFM_HAVE_CACHED_MAP'))
        {
            //Normal mode. The map will be drawn from scratch.
            //the GD surface is inside of the projection.
            //Call CreateImage() to initialize it.
            $this->projection->CreateImage();
            
            //Iterate through the geolayers and for each,
            //call its draw() function.
            foreach ($this->geolayers as $layer_k => $layer_v) {
                $layer_v->draw ($this->projection);
                if (defined ('DEBUG') &&
                    isset ($this->geolayers[$layer_k]->reportentries))
                {
                    $this->reportentries->merge (
                        $this->geolayers[$layer_k]->reportentries);
                }
            }
        } else {
            //If we are operating in cache mode,
            //create an image from the cached PNG file.
            $f = fopen ($this->idoc->cached_map,'rb');
            $content = fread ($f, filesize ($this->idoc->cached_map));
            fclose ($f);
            $this->projection->mapimage = imagecreatefromstring ($content);
        }
        
        if (! defined ('JJFM_RETURN')) {
            //Output the map.
            if (defined ('JJFM_FILE_OUTPUT'))
            {
                $this->make_image($this->idoc->output_flags,
                                  $this->idoc->output_file);
            } else {
                $this->make_image($this->idoc->output_flags);
            }
        
            //Output the debug report, if requested.
            if (defined ('DEBUG')) $this->debugreport();
        
            //Clean up the GD surface.
            $this->projection->destroy();
        } else {
            if (defined ('JJFM_CACHING') &&
                ! defined ('JJFM_HAVE_CACHED_MAP'))
            {
                $this->delete_old_cache ();
                //cached maps are always stored in png format
                //because png is lossless.
                imagepng ($this->projection->mapimage,
                          $this->cached_map);
            }
            return $this->projection->mapimage;
        }
    }
    
    function debugreport () {
        if (defined ('DEBUG'))
        {
            if ($this->idoc->debug_file !== false)
            {
                $f = fopen ($this->idoc->debug_file,
                            $this->idoc->debug_output_mode);
                fwrite ($f, $this->report());
                fclose ($f);
            } else {
                //echo debug info to STDOUT
                echo ($this->report());
            }
        }
    }


/////////////////////////
// OUTPUT FUNCTIONS
//
    function report() {
        if (defined ('DEBUG'))
        {
            $report = LF;
            $report .= TAB.'JJFMapper Report'.LF;
            $report .= LF;
            $report .= $this->reportentries->report();
            return $report;
        }
    }

    function make_image ($format) {
        /* This function takes as an argument a flags integer, which is JJFM_
         * output constants OR'd together.  Supported flags are JJFM_PNG,
         * JJFM_JPEG, and JJFM_RAW_OUTPUT.
         *
         * The optional second argument is a filename of the image file to
         * write.
         */
        if ($format & JJFM_PNG) $abbr = 'png';
        else if ($format & JJFM_JPEG) $abbr = 'jpeg';
        $outfunc = 'image'.$abbr;
        $ctype = 'Content-type: image/'.$abbr;
        if ($format ^ JJFM_RAW_OUTPUT && (! headers_sent ())) header ($ctype);
        if (! defined ('JJFM_HAVE_CACHED_MAP') && defined ('JJFM_CACHING'))
        {
            $this->delete_old_cache ();
            //cached maps are always stored in png format
            //because png is lossless.
            imagepng ($this->projection->mapimage,
                      $this->idoc->cached_map);
        }
        if (! defined ('JJFM_NO_IMAGE_OUTPUT'))
        {
            if (func_num_args() >= 2)
                $outfunc ($this->projection->mapimage,
                          func_get_arg (1));//write to file
            else $outfunc ($this->projection->mapimage);//write to stream
        }
    }
    
    function write_projection_cache() {
        $this->delete_old_cache ('projection');
        $f = fopen ($this->idoc->cached_projection, 'wb');
        fwrite ($f, $this->projection->original_params());
        fclose ($f);
    }
    
    function delete_old_cache() {
        if (func_num_args() > 0) $type = func_get_arg(0);
        else $type = 'png';
        $f = opendir ($this->idoc->cache_dir);
        $L = strlen ($this->idoc->namecrc);
        if ($L > 0) {
            while ($e = readdir ($f)) {
                if (substr ($e,0,$L) == $this->idoc->namecrc &&
                    substr ($e,$L - strlen($type)) == $type)
                {
                    unlink ($this->idoc->cache_dir . $e);
                }
            }
        }
        closedir ($f);
        
    }

}//jjfMapper
?>
Return current item: jjfmapper