Tube Map Uses in Android application

Hi All,

I am developing an application for android devices. There is a functionality to show the London tube map. There I wanted to show the London Tub Map (see below-mentioned URL).

http://content.tfl.gov.uk/standard-tube-map.pdf

Please let me know can I use this map in the application, does it violating any policy to use in the application.

Or suggest, is there any free map that can be used in the application for the free application.

Thank you.

@pdev

Please see this link for the general advice

If you LINK to “http://content.tfl.gov.uk/standard-tube-map.pdf” above in your app, that’s OK: the PDF contains the copyright notice.

However, you might have problems if you create a thumbnail or cache the PDF in your app.

Thank you Braintist for the information, Yes I have seen the Map licensing is very expensive to use in the application. It cost the royalty-free price for internet usage is ÂŁ320 + VAT (as per website) for Tube map. As an individual hobbyist software developer it is costly :slight_smile:

If you have any suggestion or alternative of using any free map kindly share.

Does TFL API provide any map functionality for the developer?

Otherwise, I believe that I have to remove the “Map View” functionality in my application. :frowning:

Thank you for your help.

1 Like

I have a few “work-arounds” that I have used myself.

Firstly, you can use the http://content.tfl.gov.uk/tfl-line-diagram-standard.pdf document to generate your own on-the-fly diagrams. I wrote some code to make SVG diagrams that could show ad-hoc lines.

http://tubedreams.freeview.tv/wp-content/uploads/2015/02/pica.svg

The other way to do diagrams is using the Wikipedia diagram generator
 Everything is in the public domain (in German, however)


1 Like

Hi Braintist,

Thank for the workaround solution. Let me analyse, how I can utilize this solution in my application. Primarily in the application “Map View” functionality suppose to show the full map of London Underground for the pictorial representation of the all the stations with respective lines on the map. Secondly, it will be having an option in the “Plan Journey” functionality where one can plan their journey and result will show a path line to the user (w.r.t. source and destination entered by the user and result will show stations and junctions to reach the respective destination). There I will highlight result path on the map so the user will easily understand the path route on the map to move on.

If you don’t mind can you please share the code that you have written for the making SVG diagram.

Thank you again for your help.

@pdev

OK, I have unpicked my SVG-generating code so that I can post it here in a way that works. The calls to the SVG generation uses a config array so that you can put in the necessary stations or whatever. There are lots of types such as NORMAL, INTERSECTION, ENDPOINT, CURVE, BACKCURVE, WALKTO, DOTTED, DOTTEDLEFT, DOTTEDRIGHT.

Note in the code you can set the line colour by line name. It will auto-fix the correct type of line (1/3+1/3+1/3 or solid line).

namespace view\lineDiagramSimple;

use view\helper\html;

class testSVG
{
    const BIGSIZE = 43;

    public function __construct()
    {
        $harryBeckLineDiagramExtended = new HarryBeckLineDiagramExtended();
        $harryBeckLineDiagramExtended->setHeight(330);
        $intSpacing = self::BIGSIZE;
        $arrPlaces[] = [
            tube::LINE => "Pink",
            tube::TITLE => "STAR line",
            tube::NODES => [
                "Stratford" => [
                    tube::TYPE => tube::INTERSECTION,
                    tube::TERMINATES => true
                ],
                "Lea Bridge" => [
                    tube::TYPE => "normal"
                ],
                "Tottenham Hale" => [
                    tube::TYPE => tube::INTERSECTION,
                    tube::NATIONAL => true
                ],
                "Northumberland Park" => [
                    tube::TYPE => "normal"
                ], "Angel Road" => [
                    tube::TYPE => tube::ENDPOINT,
                    tube::NATIONAL => true
                ]
            ]
        ];
        foreach ($arrPlaces as $arrPlace) {
            $harryBeckLineDiagramExtended->newPlaces($arrPlace);
            $harryBeckLineDiagramExtended->overrideLineColour("Overground");
            $harryBeckLineDiagramExtended->setWidth(100 + $intSpacing * 4 * pre72::count($arrPlace));
        }
        $strEcho = $harryBeckLineDiagramExtended->createOutput() . html::pGap();
        echo($strEcho);
    }
}

Will make

image

1 Like
<?php


namespace view\lineDiagramSimple;


class HarryBeckLineDiagramExtended extends HarryBeckLineDiagram
{

    public $arrAccess = ["Shenfield" => "W", "Brentwood" => "W", "Romford" => "W", "Chadwell Heath" => "W", "Stratford" => "W", "Liverpool Street" => "W",
        "Heathrow T5" => "B", "Hearthrow T23" => "B", "Paddington" => "W", "Heathrow Airport T123" => "B", "Heathrow Airport T5" => "B"];


//    public function __toString()
//    {
//        return $this->createOutput();
//    }


    /*
     *
     *       So, need to convert the arrays to objects
     *
     */
    public $ynShowTitleInSVG = true;

    public function createOutput()
    {
        $this->setSwap($this->ynRotated);

        $intLeft = $this->intBorder;
        $intRight = $this->intWidth - $this->intBorder;
        $intMiddle = $this->intHeight / 2;
        $intLeftXtra = $intLeft;
        $intRightXtra = $intRight;
        $strLineColour = $this->strLineColour;
        if (isset($this->arrPlaces[Tube::DOTCOLOUR])) {
            $this->setDottedColour($this->arrPlaces[Tube::DOTCOLOUR]);
        }
        if (count($this->arrPlaces[Tube::NODES]) > 0) {
            if (count($this->arrPlaces[Tube::NODES]) > 1) {
                $intXincrement = ($intRight - $intLeft) / (count($this->arrPlaces[Tube::NODES]) - 1);
                $this->intHozintalTextIncremaent = $this->intBorder / 2;
            } else {
                $intXincrement = ($intRight - $intLeft);
            }
            if (isset($this->arrPlaces[Tube::REVERSE])) {
                if ($this->arrPlaces[Tube::REVERSE] === true) {
                    $this->arrPlaces[Tube::NODES] = array_reverse($this->arrPlaces[Tube::NODES]);
                }
            }
            $this->preparations($intLeftXtra, $intRightXtra, $strLineColour);
            $this->splitLineConfig($intLeftXtra, $intMiddle, $intRightXtra, $strLineColour);
            $intXthis = $intLeft;
            $intCounter = 0;


            foreach ($this->arrPlaces[Tube::NODES] as $strPlacename => $arrConfig) {
                if (isset($this->arrAccess[$strPlacename])) {
                    $arrConfig["type"] = "access" . $this->arrAccess[$strPlacename];
                }
                if (isset($arrConfig[Tube::MECODE])) {
                    $fpPlaceAlong = $intCounter;
                }
                $this->mainDrawingLoop($arrConfig, $intMiddle, $strPlacename, $intXincrement, $intXthis, $strLineColour, $intCounter);
            }
        }





        if (!$this->ynRotated) {
            if ($this->ynShowTitleInSVG) {
                $this->addText($this->arrPlaces[Tube::TITLE], 0, 20, 20, "left", "Indigo", 500);
            }
            return $this->htmlimageinsert($this->intWidth, $this->intHeight, $this->strEmailMode);
        } else {
            if ($this->ynShowTitleInSVG) {
                $this->addText($this->arrPlaces[Tube::TITLE], 20, 0, 20, "left", "Indigo", 500);
            }
            return $this->htmlimageinsert($this->intHeight, $this->intWidth, $this->strEmailMode);
        }
    }

    public function mainDrawingLoop($arrConfig, &$intMiddle, &$strPlacename, &$intXincrement, &$intXthis, &$strLineColour, &$intCounter)
    {
        $intThisY = $intMiddle - $this->intGrid;

        if ($arrConfig[Tube::TYPECODE] === Tube::CURVE or $arrConfig[Tube::TYPECODE] === Tube::BACKCURVE) {
            $intThisY -= $this->intGrid / 2;
            if ($arrConfig[Tube::TYPECODE] === Tube::CURVE)
                $strPlacename = ".. " . $strPlacename;
            if ($arrConfig[Tube::TYPECODE] === Tube::BACKCURVE)
                $strPlacename = ".. " . $strPlacename;
        }
        $this->startAhref("http://eventadmin/filesystem/templates");
        $strPlacename2 = $strPlacename;
        $intTextMaxSize = $intXincrement;
        if ($this->ynRotated) {
            $intTextMaxSize = $intMiddle;
        }
        $strPlacename2 = $this->autoFixNewLines($strPlacename2, $intTextMaxSize - 4, ScalableVectorGraphics::FONTSIZEBIG);
        if (isset($arrConfig[Tube::NATIONAL])) {
            if ($arrConfig[Tube::NATIONAL]) {
                $strPlacename2 = strtr("NR\n" . $strPlacename2, [
                    "\n\n" => "\n"
                ]);
            }
        }
        if (isset($arrConfig[Tube::WALKING])) {
            if ($arrConfig[Tube::WALKING]) {
                $strPlacename2 = strtr("WALK\n" . $strPlacename2, [
                    "\n\n" => "\n"
                ]);
            }
        }
        $strTextDecor = "";
        if (isset($arrConfig[Tube::STRIKEOUT])) {
            if ($arrConfig[Tube::STRIKEOUT] === true) {
                $strTextDecor = "line-through";
//                $strPlacename2.=" ---";
            }
        }

        if (!isset($arrConfig[Tube::MECODE])) {
            $arrConfig[Tube::MECODE] = false;
        }


        if (!$this->ynRotated) {
            $intXXmore = 10 - 3;
            /*
             *   This is HORIZONTAL MODE
             *
             */
            $this->addTheStationName($strPlacename2, $intXthis, $intMiddle * .5, $intXXmore, $arrConfig[Tube::MECODE], $strTextDecor, "middle");

        } else {


            /*
            *   This is VERTICAL MODE
            *
            */

//            if ($arrConfig[Tube::TYPECODE] === Tube::CURVE or $arrConfig[Tube::TYPECODE] === Tube::BACKCURVE) {
//                $intXXmore = -$this->intGrid / 1 + 10;
//            } else {
//                $intXXmore = -40;
//            }

//            $this->addTheStationName("0", $intXthis, 50    , 0, $arrConfig[Tube::MECODE], $strTextDecor);
            $this->addTheStationName($strPlacename2, $intXthis, 50, 0, $arrConfig[Tube::MECODE], $strTextDecor, "middle");


        }
        $this->endAhref();
        switch ($arrConfig[Tube::TYPECODE]) {
            case Tube::CURVE:
                $intXof = $this->intGrid;
                $this->addBezierCurveQuadratic($intXthis - $intXof, $intMiddle, $intXthis, $intMiddle - $intXof, $strLineColour, self::LINEWIDTH, "round");
                break;
            case Tube::BACKCURVE:
                $intXof = $this->intGrid;
                $this->addBezierCurveQuadratic($intXthis + $intXof, $intMiddle, $intXthis, $intMiddle - $intXof, $strLineColour, self::LINEWIDTH, "round");
                break;
            case Tube::ENDPOINT:
                $this->addLine($intXthis, $intMiddle + $this->intGrid / 1.5, $intXthis, $intThisY + $this->intGrid / 3, $strLineColour, self::LINEWIDTH, "butt");
                break;
            case Tube::DOTTED:
                break;
            case Tube::BLANK:
                break;
            case "accessW":
            case "accessB":
            case Tube::INTERSECTION:
                $this->createIntersection($intXthis, $intMiddle, $strPlacename, $arrConfig, $arrConfig[Tube::TYPECODE]);
                break;
            default:
                $this->addLine($intXthis, $intMiddle - 3, $intXthis, $intThisY + $this->intGrid / 1.66, $strLineColour, self::LINEWIDTH * .66, "butt");
        }
        $intXmore = 0;
        $intYmore = 0;
        if ($this->ynRotated) {
            if (!isset($arrConfig[Tube::INTERSECTION])) {
                $arrConfig[Tube::INTERSECTION] = [];
            }
            $intXmore = self::FONTSIZENORMAL * 3 - self::FONTSIZENORMAL / 1.5 * pre72::count($arrConfig[Tube::INTERSECTION]);
        }
        if (isset($arrConfig[Tube::INTERSECTION])) {
            if (count($arrConfig[Tube::INTERSECTION]) > 0) {
                foreach ($arrConfig[Tube::INTERSECTION] as $strLine) {
                    $this->addLineSymbol($strLine, $intXthis + $intXmore, $intMiddle + $intYmore);
                    if (!$this->ynRotated) {
                        $intYmore += self::FONTSIZENORMAL * .8 * 2;
                    } else {
                        $intXmore += self::FONTSIZENORMAL * .8 * 2;
                    }
                }
                if (!$this->ynRotated) {
                    $intYmore += self::FONTSIZENORMAL;
                } else {
                    $intXmore += self::FONTSIZENORMAL;
                    $intXmore -= 35;
                }
            } else {
                $intXmore = 0;
            }
        }
        if (isset($arrConfig[Tube::MORETEXT])) {
            if ($arrConfig[Tube::MORETEXT] != "") {
                $strText = $this->autoFixNewLines($arrConfig[Tube::MORETEXT], $intTextMaxSize, self::FONTSIZENORMAL);
                $strText = strtr($strText, [
                    "\n\n" => "\n"
                ]);
                if (!$this->ynRotated) {
                    $this->addText($strText, $intXthis, $intMiddle + $intYmore + $this->intGrid * 1.2, self::FONTSIZENORMAL, $this->getAlignFor(), "Black", 500, false);
                } else {
                    $this->addText($strText, $intXthis + $intXmore, $intMiddle + $this->intGrid * 2, self::FONTSIZENORMAL, $this->getAlignFor(), "Black", 500, false);
                }
            }
        }
        $intXthis += $intXincrement;
        $intCounter += 1;
    }


}

<?php


namespace view\lineDiagramSimple;


class HarryBeckLineDiagram extends TrainProgressSVG
{
    const FONTSIZENORMAL = 11;
    const LINEWIDTH = 11;
    const COACHWIDTH = 20;
    const COACHHEIGHT = 16;
    const LOADINGXOFSET = 10;
    const LOADINGYOFSET = 120;
    const ARRTRANSLATE = [
        "VIC_" => "Victoria",
        "OVE_" => "Overground",
        "CEN_" => "Central",
        "JUB_" => "Jubilee",
        "DLR_" => "DLR",
        "XR1_" => "TfLRail",
        "NTN_" => "Northern",
        "BAK_" => "Bakerloo",
        "DIS_" => "District",
        "CIR_" => "Circle",
        "MET_" => "Metropolitan",
        "PIC_" => "Picadilly",
        "HAM_" => "H'smith+City",
        "WAC_" => "Waterloo+City"
    ];
    public $strThisLineName = "";
    public $arrPlaces = [];
    public $intWidth = 1200;
    public $intHeight = 450;
    public $intBorder = 60;
    public $intGrid = 32;
    public $tube;
    public $ynRotated = false;
    public $strDottedColour = "LightGrey";
    public $strEmailMode = "html";
    public $strLineColour = "";
    public $ynIsThirdWhite = true;
    public $intHozintalTextIncremaent = 0;

    public function __construct()
    {
        parent::__construct();
        $this->tube = new Tube();
    }



    public function setWidth($intWidth = 1200)
    {
        $this->intWidth = $intWidth;
    }

    public function setHeight($intHeight = 450)
    {
        $this->intHeight = $intHeight;
    }


    public function newPlaces($arrPlaces, $ynRotated = false)
    {
        $this->arrPlaces = $arrPlaces;
        $this->ynRotated = $ynRotated;
    }

    public function overrideLineColour($strLineColour, $ynIsThirdWhite = false)
    {
        $this->strLineColour = $strLineColour;
        $this->ynIsThirdWhite = $ynIsThirdWhite;
    }

    public function setDottedColour($strColour)
    {
        $this->strDottedColour = $strColour;
    }

    public function preparations(&$intLeftXtra, &$intRightXtra, &$strLineColour)
    {
        $intCounter = 0;
        $intCountPlaces = pre72::count($this->arrPlaces[Tube::NODES]);
        foreach ($this->arrPlaces[Tube::NODES] as $strPlacename => $arrConfig) {
            foreach ([Tube::TYPECODE, Tube::TERMINATES, Tube::NATIONAL, Tube::STRIKEOUT, Tube::MORETEXT, Tube::WALKM, Tube::FORCE, Tube::FALLBACK] as $strFix) {
                if (!isset($arrConfig[$strFix])) {
                    $arrConfig[$strFix] = null;
                }
            }
            if (!isset($arrConfig[Tube::INTERSECTION])) {
                $arrConfig[Tube::INTERSECTION] = [];
            }
            if ($intCounter == 0 and ($arrConfig[Tube::TYPECODE] != Tube::ENDPOINT and $arrConfig[Tube::TYPECODE] != Tube::ARROW and $arrConfig[Tube::TERMINATES] != true)) {
                $intLeftXtra -= $this->intGrid;
            }
            if ($intCounter == pre72::count($this->arrPlaces[Tube::NODES]) - 1 and ($arrConfig[Tube::TYPECODE] != Tube::ENDPOINT and $arrConfig[Tube::TYPECODE] != Tube::ARROW and $arrConfig[Tube::TERMINATES] != true)) {
                $intRightXtra += $this->intGrid;
            }
            if ($arrConfig[Tube::TYPECODE] == Tube::DOTTED) {
                $this->arrPlaces[Tube::COLOURSPLIT][$intCounter] = Tube::DOTTEDRIGHT;
                if ($intCounter >= 1) {
                    $this->arrPlaces[Tube::COLOURSPLIT][$intCounter - 1] = Tube::DOTTEDLEFT;
                }
            } else {
                if (isset($this->arrPlaces[Tube::COLOURSPLIT][$intCounter])) {
                    if ($this->arrPlaces[Tube::COLOURSPLIT][$intCounter] == "" and $intCounter < $intCountPlaces - 1) {
                        $this->arrPlaces[Tube::COLOURSPLIT][$intCounter] = $strLineColour;
                    }
                }
            }
            $intCounter += 1;;
        }
    }

    public function splitLineConfig($intLeftXtra, $intMiddle, $intRightXtra, $strLineColour)
    {
        if (isset($this->arrPlaces[Tube::COLOURSPLIT])) {
            $intSplitCount = pre72::count($this->arrPlaces[Tube::COLOURSPLIT]);
        } else {
            $intSplitCount = 0;
        }
        if ($intSplitCount == 0) {
            $this->addLine($intLeftXtra, $intMiddle, $intRightXtra, $intMiddle, $strLineColour, self::LINEWIDTH);
        } else {
            $fpWidthDiv = ($intRightXtra - $intLeftXtra) / $intSplitCount;
            $fpHolder = $intLeftXtra;
            $strLastColour = $strLineColour;
            foreach ($this->arrPlaces[Tube::COLOURSPLIT] as $intCounter => $strLineColourLocal) {
                switch ($strLineColourLocal) {
                    case Tube::DOTTEDLEFT:
                        $strLastColour = $this->strDottedColour;
                        $this->addLine($fpHolder, $intMiddle, $fpHolder + $fpWidthDiv * .5, $intMiddle, $strLastColour, self::LINEWIDTH);
//                        $this->addLineOfDots($fpHolder + $fpWidthDiv * .5, $intMiddle, $fpHolder + $fpWidthDiv * 1.5, $intMiddle, $strLastColour, self::LINEWIDTH, 5, 20);
                        $this->addLineOfDots($fpHolder + $fpWidthDiv * .5, $intMiddle, $fpHolder + $fpWidthDiv * 1.5, $strLastColour, self::LINEWIDTH); //, 5, 20);
                        break;
                    case Tube::DOTTEDRIGHT:
                        $this->addLine($fpHolder + $fpWidthDiv * .5, $intMiddle, $fpHolder + $fpWidthDiv, $intMiddle, $strLastColour, self::LINEWIDTH);
                        break;
                    default:
                        $this->addLine($fpHolder, $intMiddle, $fpHolder + $fpWidthDiv, $intMiddle, $strLineColourLocal, self::LINEWIDTH);
                        $strLastColour = $strLineColourLocal;
                        break;
                }
                $fpHolder += $fpWidthDiv;
            }
        }
        if ($this->ynIsThirdWhite) {
            $this->addLine($intLeftXtra, $intMiddle, $intRightXtra, $intMiddle, "#ffffff", self::LINEWIDTH * .333);
        }
    }

    public function autoFixNewLines($strPlacename, $intMaxWidth, $intSizePX = ScalableVectorGraphics::FONTSIZEBIG)
    {
        $arrPlacenameBits = preg_split("/ /", $strPlacename);
        $intRowOut = 0;
        $arrOutBits = [
            $intRowOut => ""
        ];
        foreach ($arrPlacenameBits as $strWord) {
            if (self::getTextWidth($arrOutBits[$intRowOut] . " " . $strWord, $intSizePX) > $intMaxWidth) {
                $intRowOut += 1;
                $arrOutBits[$intRowOut] = $strWord;
            } else {
                $arrOutBits[$intRowOut] .= " " . $strWord;
            }
        }
        return strtr(join("\n", $arrOutBits), [
            "\n\n" => "\n"
        ]);
    }

    public function addTheStationName($strPlacename2, $intXthis, $intMiddle, $intXXmore, $ynIsMe, $ynCrossedOut, $strAlign)
    {
        /*
         *
         *   Assume NOT rotated
         *
         */
        $strTextColor = "Pidcadilly";
        $intXXmore = $intXXmore + 30;

        if ($ynIsMe and !$this->ynRotated) {

            $arrBounds = self::calculateTextBoundingBox($strPlacename2, $intXXmore, $intXthis, $intMiddle);
            if ($intXXmore !== 30) {
                $arrBounds["y"] += 40;
            } else {
//                $arrBounds["x"]+=40;
                $arrBounds["y"] = 0;
                $arrBounds["w"] = 140;
                $arrBounds["x"] += 40;
                $arrBounds["h"] = 60;
            }


            $this->addRectangle($arrBounds["x"], $arrBounds["y"], $arrBounds["w"], $arrBounds["h"], "Pidcailly", "Pidcailly");
            $strTextColor = "White";
        }

        $this->addText($strPlacename2, $intXthis, $intMiddle + $intXXmore, ScalableVectorGraphics::FONTSIZEBIG, $strAlign, $strTextColor);


        if ($ynCrossedOut != "") {

            $arrBounds = self::calculateTextBoundingBox($strPlacename2, $intXXmore, $intXthis, $intMiddle);

            if (!$this->ynRotated) {
                $ycorrection = 40;
            } else {
                $ycorrection = 0;
            }


            $this->addLine($arrBounds["x"], $arrBounds["y"] + $ycorrection, $arrBounds["x"] + $arrBounds["w"], $arrBounds["y"] + $arrBounds["h"] + $ycorrection, "rgba(220, 36, 31, 1)", 2);


        }
        return;
    }

    public function showTrainLoadingIndicator($intXthis, $intMiddle, $intCarriges = 5)
    {
        for ($intI = 0; $intI < $intCarriges; $intI += 1) {
            $fpLoad = rand(0, 100) / 100;
            $this->addRectangle($intXthis + self::LOADINGXOFSET, $intMiddle + self::LOADINGYOFSET + $intI * self::COACHWIDTH, self::COACHWIDTH - 2, self::COACHHEIGHT, "#F6BF28", "#E27F27");
            $this->addRectangle($intXthis + self::LOADINGXOFSET, $intMiddle + self::LOADINGYOFSET + $intI * self::COACHWIDTH, self::COACHWIDTH - 2, self::COACHHEIGHT * $fpLoad, "#1C4692", "#1C4692");
        }
    }

    public function createIntersection($intXthis, $intMiddle, $strPlacename, &$arrConfig, $strType = "")
    {
        switch ($strType) {
            case "accessW":
                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1.5, "#0450a1");
                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1.3, "White");  ///White is correct
                $this->accessLogo($intXthis, $intMiddle);
                break;
            case "accessB":
                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1.5, "#0450a1");
//                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1.3, "White");
                $this->accessLogo($intXthis, $intMiddle, "fill:#ffffff;fill-rule:nonzero");
                break;
            default:
                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1.5, "#000000");
                $this->addCircle($intXthis, $intMiddle, self::LINEWIDTH * 1, "White");
                break;
        }
        $strPlacename2 = $strPlacename;
        if (isset($arrConfig[Tube::WALKTO])) {
            if ($arrConfig[Tube::WALKTO] != "") {
                $strPlacename2 = $arrConfig[Tube::WALKTO];
                if (true) {
                    $arrConfig[Tube::MORETEXT] .= $strPlacename2 . " " . $arrConfig[Tube::WALKM] . "m";
                }
            }
        }
        if (isset($arrConfig[Tube::FORCE])) {
            if ($arrConfig[Tube::FORCE] === true) {
                $this->strThisLineName = "";
            }
        }
        if (!isset($arrConfig[Tube::FALLBACK])) {
            $arrConfig[Tube::FALLBACK] = [];
        }
        $arrConfig[Tube::INTERSECTION] = $this->intersectional($strPlacename2, $this->strThisLineName, $arrConfig[Tube::FALLBACK]);
        if (!isset($arrConfig[Tube::FORCE])) {
            $arrConfig[Tube::FORCE] = false;
        }
        if (count($arrConfig[Tube::INTERSECTION]) == 0 and $arrConfig[Tube::FORCE] === true) {
            $arrConfig[Tube::INTERSECTION] = [
                $this->arrPlaces[Tube::LINECODE] => $this->arrPlaces[Tube::LINECODE]
            ];
        }
        if (isset($arrConfig[Tube::TRAMCODE])) {
            if ($arrConfig[Tube::TRAMCODE] == true) {
                $arrConfig[Tube::INTERSECTION]["Tramlink"] = "Tramlink";
            }
        }
    }

    public function intersectional($strPlacename, $strLineIDexclude = "Overground", $arrFallback = [])
    {
        $strPlacename = Tube::cleanName(strtr($strPlacename, ["\n" => " "]));
        $arrTranslate = self::ARRTRANSLATE;
        $arrCollection = $arrFallback;
        {
            foreach ($this->tube->arrLineData as $strFilename => $arrLine) {
                $arrBitz = preg_split("/-/", $strFilename);
                $strLineID = strtr($arrBitz[1], $arrTranslate);
                foreach ($arrLine as $strPath) {
                    $strCP = Tube::cleanName($strPath);
                    $strCP = strtr($strCP, [
                        " & " => " and "
                    ]);
                    if ($strCP == $strPlacename and $strLineID != $strLineIDexclude) {
                        $arrCollection[$strLineID] = $strLineID;
                    }
                }
            }
        }
        return $arrCollection;
    }

    public function addLineSymbol($strLine, $intXthis, $intMiddle)
    {
        $intH = self::FONTSIZENORMAL * .8;
        $inthofset = 4;
        $intyofset = 0;
        if ($this->ynRotated) {
            $inthofset = 30;
            $intyofset = -26;;
        }
        $intW = 80;
        $intMiddle = $intMiddle + $this->intGrid * 1.2;
        if ($this->addRectangle($intXthis - $intW / 2, $intMiddle - $intH, $intW, $intH * 2, $strLine, "#000000", 0.5) != "#ffffff") {
            $strTextColour = "White";
        } else {
            $strTextColour = "Black";
        }
        $this->addText(($strLine), $intXthis + $intyofset, $intMiddle + $inthofset, self::FONTSIZENORMAL, "middle", $strTextColour, 500, false);
    }

    public function getAlignFor()
    {
        if ($this->ynRotated) {
            return "middle";
        } else {
            return "middle";
        }
    }
}

<?php


namespace view\lineDiagramSimple;




class TrainProgressSVG extends SvgForHtml
{
    const NRLOGO = "M 926.28993,538.90218 L 748.40295,459.541 L 1000,459.541 L 1000,406.46975 L 741.03194,406.46975 L 872.72727,342.83339 L 1000,342.83339 L 1000,291.72774 L 872.72727,291.72774 L 712.28501,217.52626 L 590.17199,217.52626 L 759.95086,291.72774 L 500,291.72774 L 500,342.83339 L 759.95086,342.83339 L 624.07862,406.46975 L 500,406.46975 L 500,459.541 L 633.66093,459.541 L 805.15971,538.90218 L 926.28993,538.90218";
    const WALKINGLOGO = "M6358 2083c359,-183 608,-562 614,-987 -6,-612 -495,-1102 -1096,-1096 -606,-6 -1096,484 -1097,1096 1,242 85,474 220,658 30,29 160,243 -132,285 -280,23 -1105,65 -1600,570 -981,1031 -1650,2250 -1820,3990 -56,16 -251,88 -241,307 -10,210 393,671 965,680 138,-9 331,-98 328,-307 3,-118 3,-196 -22,-285 -40,-93 -60,-339 -65,-746 5,-412 408,-1334 986,-1732 0,0 457,3716 -3398,7191 0,0 592,1106 614,1140 125,260 388,348 526,373 59,15 485,60 504,66 39,1 182,27 176,-66 6,-115 -33,-139 -66,-154 -108,-65 -244,-120 -307,-372 0,0 -173,-651 -175,-746 0,0 1847,-1169 3200,-3990 0,0 1911,2195 2697,5985l2083 -920c0,0 84,-50 87,-132 -4,-64 -86,-98 -131,-110 -62,-7 -1222,-124 -1228,-131 -23,7 -82,-3 -88,-66 6,-819 -323,-2184 -592,-2938 -59,-176 -118,-341 -197,-438 -899,-1441 -1218,-2492 -789,-4209 0,0 844,1919 3047,2126 0,0 82,99 198,110 288,-11 381,-768 372,-855 9,-215 -181,-225 -175,-220 -45,-5 -123,-12 -197,110 0,0 -1670,-494 -2149,-2170 -192,-672 -603,-1284 -1052,-1667 -89,-66 -267,-241 0,-350l0 0z";
    const WHEELCHAIRLOGO = "M5995 3256l-123 829 -2538 0c0,0 -536,47 -536,572 0,505 531,562 531,562l2389 0 -118 786 -3229 0c0,0 -217,0 -340,120 -86,86 -204,334 -204,334l-1785 3659c0,0 -211,477 311,752 516,275 840,-247 840,-247l1419 -2904c0,0 118,-186 250,-248 159,-77 286,-79 286,-79l2949 2c0,0 302,0 561,-247 245,-239 286,-506 286,-506l440 -3179c0,0 -9,-658 -699,-706 -522,-36 -690,500 -690,500zm-3465 5523c377,1253 1542,2168 2913,2168 1676,0 3040,-1362 3040,-3042 0,-883 -379,-1677 -983,-2236l182 -1312c1173,740 1957,2055 1957,3548 0,2320 -1880,4198 -4196,4198 -1507,0 -2829,-795 -3564,-1985l651 -1339zm2829 -7601c0,654 525,1181 1178,1181 650,0 1179,-527 1179,-1181 0,-651 -529,-1178 -1179,-1178 -653,0 -1178,527 -1178,1178z";
    const NRFILL = "fill:#ef2721;fill-rule:nonzero;stroke:none";
    const WALKINGFILL = "fill:#003888;fill-rule:nonzero";

    public function nrLogo($intXfrom, $intYfrom)
    {
        if ($this->isHorizontalMode()) {
            $this->genericLogo($intXfrom, $intYfrom, self::NRLOGO, self::NRFILL);
        } else {
            $this->genericLogo($intYfrom, $intXfrom, self::NRLOGO, self::NRFILL);
        }
    }

    public function walkingLogo($intXfrom, $intYfrom)
    {
        if ($this->isHorizontalMode()) {
            $this->genericLogo($intXfrom, $intYfrom, self::WALKINGLOGO, self::WALKINGFILL, 0.005 / 2);
        } else {
            $this->genericLogo($intYfrom, $intXfrom, self::WALKINGLOGO, self::WALKINGFILL, 0.005 / 2);
        }
    }




    public function accessLogo($intXfrom, $intYfrom, $strFill = self::WALKINGFILL)
    {
        if ($this->isHorizontalMode()) {
            $this->genericLogo($intXfrom - 9, $intYfrom - 12, self::WHEELCHAIRLOGO, $strFill, 0.0021);
        } else {
            /*
             *
             * There's a problem with this one...
             */
            $this->genericLogo($intYfrom - 9, $intXfrom - 12, self::WHEELCHAIRLOGO, $strFill, 0.0021);
        }
    }
}

<?php


namespace view\lineDiagramSimple;


class SvgForHtml extends ScalableVectorGraphics
{
    public function htmlimageinsert($intPXwidth = 300, $intPXHeight = 300, $strEmailMode = "html")
    {
        $this->intPXwidth = $intPXwidth;
        $this->intPXHeight = $intPXHeight;
        $strType = "svg+xml";
        $a = "data:image/{$strType};base64," . base64_encode($this->__toString());
        return "<img src=\"{$a}\" style=\"width:{$intPXwidth}" . "px; height:{$intPXHeight}px\">";
    }
}

More code


<?php


namespace view\lineDiagramSimple;



class ScalableVectorGraphics extends SvgXmlCoreCode
{
    const FONTNORMAL = "styles/fonts/NJFont-Book.otf";
    const FONTSIZEBIG = 15;
    public $intPXwidth = 300;
    public $intPXHeight = 300;
    public  $strFontname;

    public function __construct($intPXwidth = 300, $intPXHeight = 300)
    {
        $this->intPXwidth = $intPXwidth;
        $this->intPXHeight = $intPXHeight;
    }

    public static function calculateTextBoundingBox($strPlacename2, $intXXmore, $intXthis, $intMiddle)
    {
        $intTextWidth = self::getMyTextWidth($strPlacename2);
        $intNumberOfLinesOfText = pre72::count(preg_split("/\n/", $strPlacename2));
        $intFontExtraSize = self::FONTSIZEBIG * $intNumberOfLinesOfText;
        return ["x" => $intXthis - $intTextWidth / 2, "y" => $intMiddle * .5 + $intXXmore - $intFontExtraSize - 2, "w" => $intTextWidth, "h" => $intFontExtraSize + 8];//, "Pidcailly", "Pidcailly");
    }

    public static function getMyTextWidth($strPlacename2)
    {
        $intMaxWidth = 0;
        foreach ($arrWhat = preg_split("/\n/", $strPlacename2) as $strItem) {
            $intLineWidth = ScalableVectorGraphics::getTextWidth($strItem, self::FONTSIZEBIG);
            if ($intLineWidth > $intMaxWidth) {
                $intMaxWidth = $intLineWidth;
            }
        }
        return $intMaxWidth;
    }

    public static function getTextWidth($strSomeText, $intSizePX)
    {
        $strFont = self::FONTNORMAL;
        /*
         *    Fix to deal with the font not being found
         */
        putenv('GDFONTPATH=' . realpath('.'));
        $arrTemp = imagettfbbox($intSizePX, 0, $strFont, $strSomeText);
        return abs($arrTemp[4] - $arrTemp[0]);
    }

    public function addText($strWhat, $intXfrom, $intYfrom, $intSize = 40, $strAnchor = "middle", $strColour = "Blue", $strWeight = "500", $ynUp = true, $ynAddForX = false, $strTextDecor = "")
    {
        self::fixColour($strColour);
        $arrWhat = preg_split("/\n/", $strWhat);
        if ($ynUp) {
            $intYfrom -= $intSize * (count($arrWhat) - 1);
        }
        if ($ynAddForX) {
            $intXfrom -= $intSize * .5 * (count($arrWhat) - 1.5) + 2;
        }
        $intCalcWidth = 0;
        foreach ($arrWhat as $strWhat) {
            switch ($strWhat) {
                case "NR":
                    if ($this->isHorizontalMode()) {
                        $this->nrLogo($intXfrom - 10, $intYfrom - $intSize);
                    } else {
                        $this->nrLogo($intXfrom - $intSize, $intYfrom - 10 + 15);
                        $intYfrom += $intSize;

                    }


                    break;
                case "WALK":
                    if ($this->isHorizontalMode()) {
                        $this->walkingLogo($intXfrom - 10, $intYfrom - $intSize);
                    } else {
                        $this->walkingLogo($intXfrom - $intSize, $intYfrom - 10);
                    }
                    break;
                default:

                    $this->addTextCoreCode($strWhat, $intXfrom, $intYfrom, $strAnchor, $intSize, $strColour, $strWeight, $strTextDecor);
                    $intThisWidth = self::getTextWidth($strWhat, $intSize * .9);
                    if ($intThisWidth > $intCalcWidth) {
                        $intCalcWidth = $intThisWidth;
                    }
                    break;
            }
            if ($this->isHorizontalMode()) {
                $intYfrom += $intSize;
            } else {
                $intXfrom += $intSize;
            }
        }
    }

    private static function fixColour(&$strColour)
    {
        if ($strColour == "transparent") {
            return "";
        }
        if (substr($strColour, 0, 4) == "rgba") {
            return $strColour;
        }
        if (substr($strColour, 0, 1) != "#") {
            $strColour = "#" . tubepalette::getPalette500($strColour);
        }
        return "";
    }

    public function isHorizontalMode()
    {
        return $this->strX == "x";
    }

    public function nrLogo($intXfrom, $intYfrom)
    {
        /*
         *   This is overridden
         *
         */
    }

    public function walkingLogo($intXfrom, $intYfrom)
    {
        /*
         *   This is overridden
         *
         */
    }


    public function addLine($intXfrom, $intYfrom, $intXto, $intYto, $strColour = "Blue", $intStrokeWidth = 15, $strLinecap = "round", $strStrokedasharray = "")
    {
        self::fixColour($strColour);
        if ($strStrokedasharray != "") {
            $strStrokedasharray = " stroke-dasharray=\"{$strStrokedasharray}\" ";
        }
        $this->strStore .= "<line {$this->strX}1=\"{$intXfrom}\" {$this->strY}1=\"{$intYfrom}\" {$this->strX}2=\"{$intXto}\" {$this->strY}2=\"{$intYto}\" stroke-width=\"{$intStrokeWidth}\" stroke=\"$strColour\" stroke-linecap=\"{$strLinecap}\" $strStrokedasharray  />";
    }

    public function genericLogo($intXfrom, $intYfrom, $strPathDcode, $strStyleCode, $fpScale = 0.04)
    {
        $intXS = -550;
        $intYS = -220;
        $fpCX = $intXS * $fpScale;
        $fpCY = $intYS * $fpScale;
        $fpCX += $intXfrom;
        $fpCY += $intYfrom;
        $strID = "layer" . rand(1E6, 1E8);
        $this->strStore .= "<g transform=\"translate({$fpCX},{$fpCY}) scale($fpScale)  \" id=\"{$strID}\" > <path d=\"{$strPathDcode}\" id=\"{$strID}a\" style=\"{$strStyleCode}\" /></g>";
    }

    public function startAhref($strURL)
    {
        $this->strStore .= "<a xlink:href=\"{$strURL}\">";
    }

    public function endAhref()
    {
        $this->strStore .= "</a>";
    }


    public function addRectangle($intXfrom, $intYfrom, $intWidth = 100, $intHeight = 100, $strFill = "White", $strStoke = "Black", $intStrokeWidth = 1)
    {
        self::fixColour($strStoke);
        self::fixColour($strFill);
//        $this->strStore .= " <rect  {$this->strX}=\"{$intXfrom}\" {$this->strY}=\"{$intYfrom}\" width=\"$intWidth\" height=\"$intHeight\" fill=\"$strFill\" stroke-width=\"{$intStrokeWidth}\" stroke=\"{$strStoke}\" />";

        $this->addRectangleCore($intXfrom, $intYfrom, $intWidth, $intHeight, $strFill, $strStoke, $intStrokeWidth);

        if ($strFill == "#ffce00") {
            return "#ffffff";
        }
        return $strFill;
    }

    public function setSwap($ynSwap)
    {
        if ($ynSwap) {
            $this->strX = "y";
            $this->strY = "x";
        } else {
            $this->strX = "x";
            $this->strY = "y";
        }
    }

    public function addLineOfDots($intXfrom, $intYfrom, $intXto, $strColour = "Blue", $intStrokeWidth = 15)
    {
        $intTwo = 25;
        self::fixColour($strColour);
        $intThisX = $intXfrom;
        do {
            $this->addCircle($intThisX, $intYfrom, $intStrokeWidth / 2, $strColour);
            $intThisX += $intTwo / 2;
        } while ($intThisX < $intXto);
    }

    public function addCircle($intXfrom, $intYfrom, $intRadius = 40, $strColour = "Blue")
    {
        self::fixColour($strColour);
//        $this->strStore .= "<circle c{$this->strX}=\"{$intXfrom}\" c{$this->strY}=\"{$intYfrom}\" r=\"{$intRadius}\" fill=\"{$strColour}\" stroke=\"{$strColour}\"  />";
        $this->circleCore($intXfrom, $intYfrom, $intRadius, $strColour);
    }



    public function addBezierCurveQuadratic($intXfrom, $intYfrom, $intXto, $intYto, $strColour = "Blue", $intStrokeWidth = 15, $strLinecap = "round")
    {
        self::fixColour($strColour);
        if ($this->strX == "x") {
            $strControlPoints = " Q $intXto,$intYfrom  ";
            $this->strStore .= "<path d=\"M$intXfrom, $intYfrom  {$strControlPoints} $intXto, $intYto \" fill=\"none\" stroke-width=\"{$intStrokeWidth}\" stroke=\"$strColour\" stroke-linecap=\"{$strLinecap}\" />";
        } else {
            $strControlPoints = " Q $intYto,$intXfrom  ";
            $this->strStore .= "<path d=\"M$intYfrom, $intXfrom  {$strControlPoints} $intYto, $intXto \" fill=\"none\" stroke-width=\"{$intStrokeWidth}\" stroke=\"$strColour\" stroke-linecap=\"{$strLinecap}\" />";
        }
    }
}

<?php

namespace view\lineDiagramSimple;

use view\helper\xml;

class SvgXmlCoreCode
{
    public $strStore = "";
    public $strX = "x";
    public $strY = "y";

    public function __toString()
    {
        return xml::wrapTag($this->strStore, "svg", "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"  version=\"1.1\" width=\"{$this->intPXwidth}\" height=\"{$this->intPXHeight}\" preserveAspectRatio=\"xMinYMin slice\" viewBox=\"0 0 {$this->intPXwidth} {$this->intPXHeight}\"");
    }

    public final function addTextCoreCode($strWhat, $intXfrom, $intYfrom, $strAnchor, $intSize, $strColour, $strWeight, $strTextDecor)
    {
        $this->strStore .= "<text {$this->strX}=\"{$intXfrom}\" {$this->strY}=\"{$intYfrom}\"  text-anchor=\"{$strAnchor}\" font-size=\"{$intSize}\" fill=\"{$strColour}\" font-weight = \"{$strWeight}\" font-family=\"{$this->strFontname}\" xtext-decoration=\"{$strTextDecor}\">{$strWhat}</text>";
    }

    public function addRectangleCore($intXfrom, $intYfrom, $intWidth, $intHeight, $strFill, $strStoke, $intStrokeWidth)
    {
        $this->strStore .= " <rect  {$this->strX}=\"{$intXfrom}\" {$this->strY}=\"{$intYfrom}\" width=\"$intWidth\" height=\"$intHeight\" fill=\"$strFill\" stroke-width=\"{$intStrokeWidth}\" stroke=\"{$strStoke}\" />";
    }

    public final function circleCore($intXfrom, $intYfrom, $intRadius, $strColour)
    {
        $this->strStore .= "<circle c{$this->strX}=\"{$intXfrom}\" c{$this->strY}=\"{$intYfrom}\" r=\"{$intRadius}\" fill=\"{$strColour}\" stroke=\"{$strColour}\"  />";
    }

    public final function circleCoreStroke($intXfrom, $intYfrom, $intRadius, $strColour, $strStroke)
    {
        $this->strStore .= "<circle c{$this->strX}=\"{$intXfrom}\" c{$this->strY}=\"{$intYfrom}\" r=\"{$intRadius}\" fill=\"{$strColour}\" stroke=\"{$strStroke}\"  />";
    }
}

<?php
/**
 * Created by PhpStorm.
 * User: Briantist
 * Date: 12/01/2017
 * Time: 17:02
 */

namespace view\helper;

use pseph\nff\formation\SpecilisedHtmlHeaders;

class xml extends core
{
    const COMPRESSXML = true;

    public static function setXML($intSeconds = 29)
    {
        if (self::COMPRESSXML)
            if (!core::isWindowsMachine()) {
                core::startObGzHandler();
            }
        SpecilisedHtmlHeaders::outputHTTPheaders($intSeconds, "application/xml; charset=utf-8");
        echo "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
    }

    public static function wrapTag($srtWhat, $strTag, $strParams = "")
    {
        if ($strParams != "") {
            $strParams = " " . $strParams;
        }
        if (is_array($srtWhat)) {
            $srtWhat = "[]";
        }
        if (is_object($srtWhat)) {
            $srtWhat = "object";
        }
        return "<{$strTag}{$strParams}>{$srtWhat}</{$strTag}>";
    }
}

Hi @Briantist,

Thank you very much for you help, now I have to identify how I can use the code in my android application since it is PHP code, I am converting it. I know that it is complex but I am trying to achieve to draw map. If not then I will creating same map in MS Paint :slight_smile:

Thank you again.

1 Like

@pdev

How did you get on?

Hi Braintish,

Thank you for asking.
I missed this conversation long time before. I was busy in some other projects, as I have started long time ago. By the way, I have resumed the this project week only where I left.

Thanks @pdev for asking this question and @briantist for answering.

Do either of you know please, is this ÂŁ320+ for a completely non-commercial project, eg just to write a blog post of data analysis presented on a representation of the map? Even if that was a blog with zero advertising on it too?

Welcome @Jon

The £320 is because you always need a “soft licence”, no matter what the intent of the project. This is so that TfL can retain full legal control.

If they just gave it away for free, it because hard to show, legally, that other people shouldn’t get it without payment. More importantly, for TfL, it means they retain the “can’t modify” rights.

This is very similar to the “Freesat encoding” situation. When the BBC people created Freesat, it was made fully a free-to-air system, but to retain the rights over the branding and other corporate image requires a licence payment for a table of code to decode the LZ encoding of the data.

I’m not sure that TfL’s layers actually chace down every non-licenced usage of their map. I would suspect that if “blog post of data analysis” wasn’t making unfair critism of the TfL or it’s employees, you would be OK.

According to the law the concept of “fair usage” is one that covers “used for the purposes of criticism, review or quotation;”

https://www.bl.uk/business-and-ip-centre/articles/fair-use-copyright-explained

I also note that the IanVisits blog often criqutes new tube maps and uses partials or whole versions of the tube map

1 Like

Thanks @briantist. That sparks an idea: Do you know if the license is only required for the latest map? Do you think it’s allowed to use an older version without any licence?

@Jon

I’m sure that the ownership of the copyright to old versions passes to the London Transport Museum (it’s a charity) as they have an extensive collection of old ones you can buy

What’s the nature of what you’re going to post? Can you not use this wonderful tube map?

1 Like

Thanks again @briantist. That is a really nice map.

I want to use the map to present time series forecasting done via various methods and based on TFL’s open data. To do that i was hoping to:

Although the one you introduced is a really nice version of a geographic map, i think the familiar, abstract version would be far more striking for my purposes, which include focussing on the most relevant information, which is the connections, rather than the geographic accuracy. But it can definitely be an old version of the map. The data i’m using maybe from 2010 anyway.

Another alternative i’m considering is to just not make it public but that would be a shame and a risk
 Rather than an ‘old version’, maybe i should say an ‘earlier iteration of the current design, which has been around for decades’.

Do you know if this is an earlier iteration for example: London Underground Map translated into German | Londoner U-Bahn-Plan ins Deutsche ĂŒbersetzt

@Jon

The german map is ancient, it’s pre London Overground (2007) and Woolwich DLR (January 2009).
There’s no Heathrow Terminal 5 station (27 March 2008)

1 Like

I’m not sure, I’ve somestimes done what you are suggesting, like this measure noise map

image (click for PDF)


and TfL people have said “thanks”. However, I always specifically remove anything that would suggest it was provided by TfL. However, because of the level of information on the map, and how often that changes (new accessible stations added, for example) it very hard to overlay any more information.

And, of course, the famous fact that it’s a diagram and not a map means that it very, very cramped in places where there is little room and short names (Plastow, Upton Park, East Ham, Barking) and other places there are big gaps (Denmark Hill, Peckham Rye, Queens Road Peckhan, Surrey Quays).

Is any of this helping?

1 Like

Thanks @briantist. Yes, it is absolutely helping! It’s even giving me hope. Sorry for not getting back with that sooner.

Your noise map is really nice. I can see why TFL were thankful. It’s really useful analysis and visualization. Can we see any other work of yours somewhere?

Thanks for the tip about removing superfluous TfL identifiers. I was hoping that the elegance of the diagram would not be cramped if data was presented in a side panel or something akin to https://ubahn-map.com/ . I realise that’s not as elegant as your solution in the noise map but it may suffice for my situation.

So you think that these older versions of the diagram (eg that pre 2007 one) do not require a license? And it is the older maps, rather than diagrams, which the London Transport Museum license?

1 Like