Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 218
CargoMapsFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 7
3540
0.00% covered (danger)
0.00%
0 / 218
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 4
 allowedParameters
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 9
 getScripts
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 11
 getStyles
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 7
 getImageURL
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 7
 getImageData
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 14
 display
0.00% covered (danger)
0.00%
0 / 1
2070
0.00% covered (danger)
0.00%
0 / 166
<?php
/**
 * @author Yaron Koren
 * @ingroup Cargo
 */
class CargoMapsFormat extends CargoDisplayFormat {
    public static $mappingService = "OpenLayers";
    public static $mapNumber = 1;
    public function __construct( $output ) {
        global $wgCargoDefaultMapService;
        parent::__construct( $output );
        self::$mappingService = $wgCargoDefaultMapService;
    }
    public static function allowedParameters() {
        return [
            'height' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-heightparam' )->parse() ],
            'width' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-widthparam' )->parse() ],
            'icon' => [ 'type' => 'string' ],
            'zoom' => [ 'type' => 'int' ],
            'center' => [ 'type' => 'string' ],
            'cluster' => [ 'type' => 'string' ]
        ];
    }
    public static function getScripts() {
        global $wgCargoDefaultMapService;
        if ( $wgCargoDefaultMapService == 'Google Maps' ) {
            return CargoGoogleMapsFormat::getScripts();
        } elseif ( $wgCargoDefaultMapService == 'OpenLayers' ) {
            return CargoOpenLayersFormat::getScripts();
        } elseif ( $wgCargoDefaultMapService == 'Leaflet' ) {
            return CargoLeafletFormat::getScripts();
        } else {
            return [];
        }
    }
    public static function getStyles() {
        global $wgCargoDefaultMapService;
        if ( $wgCargoDefaultMapService == 'Leaflet' ) {
            return CargoLeafletFormat::getStyles();
        } else {
            return [];
        }
    }
    /**
     * Based on the Maps extension's getFileUrl().
     */
    public static function getImageURL( $imageName ) {
        $title = Title::makeTitle( NS_FILE, $imageName );
        if ( $title == null || !$title->exists() ) {
            return null;
        }
        $imagePage = new ImagePage( $title );
        return $imagePage->getDisplayedFile()->getURL();
    }
    public function getImageData( $fileName ) {
        global $wgUploadDirectory;
        if ( $fileName == '' ) {
            return null;
        }
        $fileTitle = Title::makeTitleSafe( NS_FILE, $fileName );
        if ( !$fileTitle->exists() ) {
            throw new MWException( "Error: File \"$fileName\" does not exist on this wiki." );
        }
        $imagePage = new ImagePage( $fileTitle );
        $file = $imagePage->getDisplayedFile();
        $filePath = $wgUploadDirectory . '/' . $file->getUrlRel();
        list( $imageWidth, $imageHeight, $type, $attr ) = getimagesize( $filePath );
        return [ $imageWidth, $imageHeight, $file->getUrl() ];
    }
    /**
     * @param array $valuesTable
     * @param array $formattedValuesTable
     * @param array $fieldDescriptions
     * @param array $displayParams
     * @return string HTML
     * @throws MWException
     */
    public function display( $valuesTable, $formattedValuesTable, $fieldDescriptions, $displayParams ) {
        $coordinatesFields = [];
        foreach ( $fieldDescriptions as $field => $description ) {
            if ( $description->mType == 'Coordinates' ) {
                $coordinatesFields[] = $field;
            }
        }
        if ( count( $coordinatesFields ) == 0 ) {
            throw new MWException( "Error: no fields of type \"Coordinates\" were specified in this "
            . "query; cannot display in a map." );
        }
        // @TODO - should this check be higher up, i.e. for all
        // formats?
        if ( count( $formattedValuesTable ) == 0 ) {
            throw new MWException( "No results found for this query; not displaying a map." );
        }
        // Add necessary JS scripts and CSS styles.
        $scripts = $this->getScripts();
        $scriptsHTML = '';
        foreach ( $scripts as $script ) {
            $scriptsHTML .= Html::linkedScript( $script );
        }
        $styles = $this->getStyles();
        $stylesHTML = '';
        foreach ( $styles as $style ) {
            $stylesHTML .= Html::linkedStyle( $style );
        }
        $this->mOutput->addHeadItem( $scriptsHTML, $scriptsHTML );
        $this->mOutput->addHeadItem( $stylesHTML, $stylesHTML );
        $this->mOutput->addModules( [ 'ext.cargo.maps' ] );
        // Construct the table of data we will display.
        $valuesForMap = [];
        foreach ( $formattedValuesTable as $i => $valuesRow ) {
            $displayedValuesForRow = [];
            foreach ( $valuesRow as $fieldName => $fieldValue ) {
                if ( !array_key_exists( $fieldName, $fieldDescriptions ) ) {
                    continue;
                }
                $fieldType = $fieldDescriptions[$fieldName]->mType;
                if ( $fieldType == 'Coordinates' || $fieldType == 'Coordinates part' ) {
                    // Actually, we can ignore these.
                    continue;
                }
                if ( $fieldValue == '' ) {
                    continue;
                }
                $displayedValuesForRow[$fieldName] = $fieldValue;
            }
            // There could potentially be more than one
            // coordinate for this "row".
            // @TODO - handle lists of coordinates as well.
            foreach ( $coordinatesFields as $coordinatesField ) {
                $coordinatesField = str_replace( ' ', '_', $coordinatesField );
                if (
                    !array_key_exists( $coordinatesField . '  lat', $valuesRow ) ||
                    !array_key_exists( $coordinatesField . '  lon', $valuesRow )
                ) {
                    continue;
                }
                $latValue = $valuesRow[$coordinatesField . '  lat'];
                $lonValue = $valuesRow[$coordinatesField . '  lon'];
                // @TODO - enforce the existence of a field
                // besides the coordinates field(s).
                $firstValue = array_shift( $displayedValuesForRow );
                if ( $latValue != '' && $lonValue != '' ) {
                    $valuesForMapPoint = [
                        // 'name' has no formatting
                        // (like a link), while 'title'
                        // might.
                        'name' => array_shift( $valuesTable[$i] ),
                        'title' => $firstValue,
                        'lat' => $latValue,
                        'lon' => $lonValue,
                        'otherValues' => $displayedValuesForRow
                    ];
                    if ( array_key_exists( 'icon', $displayParams ) ) {
                        $iconFileName = null;
                        if ( is_array( $displayParams['icon'] ) ) {
                            // Compound query.
                            if ( array_key_exists( $i, $displayParams['icon'] ) ) {
                                $iconFileName = $displayParams['icon'][$i];
                            }
                        } else {
                            // Regular query.
                            $iconFileName = $displayParams['icon'];
                        }
                        if ( $iconFileName !== null ) {
                            $iconURL = self::getImageURL( $iconFileName );
                            if ( $iconURL !== null ) {
                                $valuesForMapPoint['icon'] = $iconURL;
                            }
                        }
                    }
                    $valuesForMap[] = $valuesForMapPoint;
                }
            }
        }
        $service = self::$mappingService;
        $jsonData = json_encode( $valuesForMap, JSON_NUMERIC_CHECK | JSON_HEX_TAG );
        $divID = "mapCanvas" . self::$mapNumber++;
        if ( $service == 'Leaflet' && array_key_exists( 'image', $displayParams ) ) {
            $fileName = $displayParams['image'];
            $imageData = $this->getImageData( $fileName );
            if ( $imageData == null ) {
                $fileName = null;
            } else {
                list( $imageWidth, $imageHeight, $imageURL ) = $imageData;
            }
        } else {
            $fileName = null;
        }
        if ( array_key_exists( 'height', $displayParams ) && $displayParams['height'] != '' ) {
            $height = $displayParams['height'];
            // Add on "px", if no unit is defined.
            if ( is_numeric( $height ) ) {
                $height .= "px";
            }
        } else {
            $height = null;
        }
        if ( array_key_exists( 'width', $displayParams ) && $displayParams['width'] != '' ) {
            $width = $displayParams['width'];
            // Add on "px", if no unit is defined.
            if ( is_numeric( $width ) ) {
                $width .= "px";
            }
        } else {
            $width = null;
        }
        if ( $fileName !== null ) {
            // Do some scaling of the image, if necessary.
            if ( $height !== null && $width !== null ) {
                // Reduce image if it doesn't fit into the
                // assigned rectangle.
                $heightRatio = (int)$height / $imageHeight;
                $widthRatio = (int)$width / $imageWidth;
                $smallerRatio = min( $heightRatio, $widthRatio );
                if ( $smallerRatio < 1 ) {
                    $imageHeight *= $smallerRatio;
                    $imageWidth *= $smallerRatio;
                }
            } else {
                // Reduce image if it's too big.
                $maxDimension = max( $imageHeight, $imageWidth );
                $maxAllowedSize = 1000;
                if ( $maxDimension > $maxAllowedSize ) {
                    $imageHeight *= $maxAllowedSize / $maxDimension;
                    $imageWidth *= $maxAllowedSize / $maxDimension;
                }
                $height = $imageHeight . 'px';
                $width = $imageWidth . 'px';
            }
        } else {
            if ( $height == null ) {
                $height = "400px";
            }
            if ( $width == null ) {
                $width = "700px";
            }
        }
        // The 'map data' element does double duty: it holds the full
        // set of map data, as well as, in the tag attributes,
        // settings related to the display, including the mapping
        // service to use.
        $mapDataAttrs = [
            'class' => 'cargoMapData',
            'style' => 'display: none',
            'data-mapping-service' => $service
        ];
        if ( array_key_exists( 'zoom', $displayParams ) && $displayParams['zoom'] != '' ) {
            $mapDataAttrs['data-zoom'] = $displayParams['zoom'];
        }
        if ( array_key_exists( 'center', $displayParams ) && $displayParams['center'] != '' ) {
            $mapDataAttrs['data-center'] = $displayParams['center'];
        }
        if ( array_key_exists( 'cluster', $displayParams ) ) {
            $mapDataAttrs['data-cluster'] = strtolower( $displayParams['cluster'] );
        }
        if ( $fileName !== null ) {
            $mapDataAttrs['data-image-path'] = $imageURL;
            $mapDataAttrs['data-height'] = $imageHeight;
            $mapDataAttrs['data-width'] = $imageWidth;
        }
        $mapData = Html::element( 'span', $mapDataAttrs, $jsonData );
        $mapCanvasAttrs = [
            'class' => 'mapCanvas',
            'style' => "height: $height; width: $width;",
            'id' => $divID,
        ];
        $mapCanvas = Html::rawElement( 'div', $mapCanvasAttrs, $mapData );
        return $mapCanvas;
    }
}