diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index c3bd76c..63af275 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -18,7 +18,15 @@ jobs: run: php test.php - name: Validate line-colors.csv working-directory: ./validation - run: php check.php + run: php check-lines.php + - name: Validate product-brands.csv + working-directory: ./validation + run: php check-products.php + - name: Install svgcleaner and rsvg + run: sudo apt update && sudo apt install -y -qq wget librsvg2-bin && wget https://github.com/RazrFalcon/svgcleaner/releases/download/v0.9.5/svgcleaner_linux_x86_64_0.9.5.tar.gz && tar -xf svgcleaner_linux_*.tar.gz && sudo mv svgcleaner /usr/local/bin && rm -f svgcleaner svgcleaner_linux_*.tar.gz + - name: Ensure all product icons are cleaned and resized + working-directory: ./products + run: for product in *.svg; do rsvg-convert -f svg -a -h 24 -o clean-$product $product && svgcleaner clean-$product clean-$product && diff -u $product clean-$product || exit 1 ; done && rm clean-*.svg deploy: needs: [validate] @@ -32,6 +40,17 @@ jobs: with: php-version: 8.2 + - name: Install svgcleaner and rsvg + run: sudo apt update && sudo apt install -y -qq wget librsvg2-bin && wget https://github.com/RazrFalcon/svgcleaner/releases/download/v0.9.5/svgcleaner_linux_x86_64_0.9.5.tar.gz && tar -xf svgcleaner_linux_*.tar.gz && sudo mv svgcleaner /usr/local/bin && rm -f svgcleaner svgcleaner_linux_*.tar.gz + + - name: Generate product assets + working-directory: ./validation + run: php product-assets.php + + - name: Bundle product assets + working-directory: ./website/assets/products + run: zip ../../product-brands.zip * + - name: Write out Website working-directory: ./website run: php index.php > index.html && rm index.php diff --git a/.gitignore b/.gitignore index 9f33dd5..7bb48ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ vendor/ -.idea/ \ No newline at end of file +.idea/ + +website/index.html +website/assets/products/ +website/assets/product-brands.zip + diff --git a/README.md b/README.md index e3eb2fe..ee99c98 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,14 @@ This repository is made for collecting line colors in public transport lines, so they can be displayed on systems using DB HAFAS. -## Structure +## Contents + +- [Line Colors](#line-colors) +- [Product Brands](#product-brands) + +## Line Colors + +### Structure The `line-colors.csv` contains several columns: - `shortOperatorName`: Short operator name (i.e. vehicle keeper marking/"Halterkürzel" or another identifier for EVU) and a local transport network abbreviation @@ -20,8 +27,9 @@ The `line-colors.csv` contains several columns: - `pill`: Rectangle with completely rounded corners - `trapezoid` A trapezoid shape with a broad top and a narrow bottom side - `wikidataQid`: Wikidata QID for the line (if available, can be empty) +- `productBrand`: An optional `productBrand` from the product brands database in order to manually assign a particular product brand override. This is useful if e.g. an operator runs a single line entering another traffic association using different branding, such as DB Regio operating for Austrian S-Bahn networks. -## Contributing +### Contributing If a line operates in a local transport network/"Verkehrsverbund", the network's line color shall be preferred.
Local transport networks usually have line colors for: @@ -54,7 +62,7 @@ Please keep the PR's small. If possible, create a small PR for each operator. \ No newline at end of file diff --git a/products/regio-s-bahn-bw.svg b/products/regio-s-bahn-bw.svg new file mode 100644 index 0000000..ada7ca6 --- /dev/null +++ b/products/regio-s-bahn-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/products/rer-ch-fribourg.svg b/products/rer-ch-fribourg.svg new file mode 100644 index 0000000..00e8239 --- /dev/null +++ b/products/rer-ch-fribourg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/products/rer-ch-vaud.svg b/products/rer-ch-vaud.svg new file mode 100644 index 0000000..8cfceca --- /dev/null +++ b/products/rer-ch-vaud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/products/s-bahn-at.svg b/products/s-bahn-at.svg new file mode 100644 index 0000000..1efe38f --- /dev/null +++ b/products/s-bahn-at.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/validation/check.php b/validation/check-lines.php similarity index 85% rename from validation/check.php rename to validation/check-lines.php index 0dddaa9..06a25b9 100644 --- a/validation/check.php +++ b/validation/check-lines.php @@ -5,8 +5,8 @@ echo "Checking for lower-case content" . PHP_EOL; $i = 2; -foreach ($csv as $line) { - $lowercase_keys = ["shortOperatorName", "hafasOperatorCode", "hafasLineId", "backgroundColor", "textColor", "shape"]; +foreach ($lineColors as $line) { + $lowercase_keys = ["shortOperatorName", "hafasOperatorCode", "hafasLineId", "backgroundColor", "textColor", "shape", "productBrand"]; foreach ($lowercase_keys as $key) { if ($line[$key] !== strtolower($line[$key])) { throw new Error("$key is not lowercase in row $i"); @@ -18,7 +18,7 @@ echo "Checking for ordering by shortOperatorName" . PHP_EOL; $last_op = ""; $i = 2; -foreach ($csv as $line) { +foreach ($lineColors as $line) { if ($last_op > $line["shortOperatorName"]) { throw new Error($last_op . " should be after " . $line["shortOperatorName"] . " in row $i"); } @@ -29,14 +29,14 @@ echo "Checking that all shapes are valid and can be displayed on the website" . PHP_EOL; $i = 2; -foreach ($csv as $line) { +foreach ($lineColors as $line) { valid_shape($line, $i); $i++; } echo "Checking that all colors are valid" . PHP_EOL; $i = 2; -foreach ($csv as $line) { +foreach ($lineColors as $line) { if ($line["borderColor"] != "") { valid_hex_color($line, $i, "borderColor"); } @@ -50,7 +50,7 @@ echo "Checking that the background color isn't the text color" . PHP_EOL; $i = 2; -foreach ($csv as $line) { +foreach ($lineColors as $line) { text_color_differs_background($line, $i); $i++; } @@ -59,12 +59,11 @@ $opSources = array_map(fn($op) => $op["shortOperatorName"], $sources); echo "Checking that operators are present in sources.json" . PHP_EOL; $i = 2; -foreach ($csv as $line) { +foreach ($lineColors as $line) { if (!in_array($line["shortOperatorName"], $opSources)) { throw new Error("unknown operator " . $line["shortOperatorName"] . " in row $i"); } $i++; } - echo "...Checked $i rows" . PHP_EOL; diff --git a/validation/check-products.php b/validation/check-products.php new file mode 100644 index 0000000..640e767 --- /dev/null +++ b/validation/check-products.php @@ -0,0 +1,36 @@ + $brand["productBrand"]) { + throw new Error($last_brand . " should be after " . $brand["productBrand"] . " in row $i"); + } + $i++; + + $last_brand = $brand["productBrand"]; +} + +echo "Checking for invalid productBrand entries in line-colors.csv" . PHP_EOL; +$brandNames = array_map(fn($brand) => $brand["productBrand"], $productSources); +foreach ($lineColors as $line) { + if ($line["productBrand"] != '' && !in_array($line["productBrand"], $brandNames)) { + throw new Error("Invalid productBrand reference " . $line["productBrand"] . " for operator " . $line["shortOperatorName"]); + } +} + +$sources = json_decode(file_get_contents("../icon-sources.json"), true); +$brandSources = array_map(fn($op) => $op["iconName"], $sources); +echo "Checking that manual icon overrides are present in icon-sources.json" . PHP_EOL; +$i = 2; +foreach ($productSources as $brand) { + if ($brand["iconName"] != '' && !in_array($brand["iconName"], $brandSources)) { + throw new Error("missing icon source for " . $brand["iconName"] . " in row $i"); + } + $i++; +} + +echo "...Checked $i rows" . PHP_EOL; diff --git a/validation/common.php b/validation/common.php index 25a36e9..7f3ad14 100644 --- a/validation/common.php +++ b/validation/common.php @@ -1,12 +1,18 @@ $row) { - $csv[$i] = array_combine($keys, $row); +$lineColors = array_map("str_getcsv", file("../line-colors.csv", FILE_SKIP_EMPTY_LINES)); +$keys = array_shift($lineColors); +foreach ($lineColors as $i => $row) { + $lineColors[$i] = array_combine($keys, $row); } -$linesByOperatorCode = array_reduce($csv, function ($result, $line) { +$productSources = array_map("str_getcsv", file("../product-brands.csv", FILE_SKIP_EMPTY_LINES)); +$productKeys = array_shift($productSources); +foreach ($productSources as $i => $row) { + $productSources[$i] = array_combine($productKeys, $row); +} + +$linesByOperatorCode = array_reduce($lineColors, function ($result, $line) { $result[$line["shortOperatorName"]][] = $line; return $result; diff --git a/validation/product-assets.php b/validation/product-assets.php new file mode 100644 index 0000000..7b5e1de --- /dev/null +++ b/validation/product-assets.php @@ -0,0 +1,77 @@ +&1", $output, $retval); + exec("svgcleaner " . $svg . " " . $svg . " 2>&1", $output, $retval); + $brand["iconName"] = $commonName; + } + if($brand["shortName"] == "") { + $brand["shortName"] = $json["statements"]["P1813"][0]["value"]["content"]["text"]; + } + } else if($brand["iconName"] != "") { + $link = "../website/assets/products/" . $brand["iconName"] . ".svg"; + if(!is_file($link)){ + link("../products/" . $brand["iconName"] . ".svg", $link);} + } + fputcsv($fp, array_values($brand), ',', '"', ''); +} + +fclose($fp); diff --git a/website/index.php b/website/index.php index 197c613..579957f 100644 --- a/website/index.php +++ b/website/index.php @@ -1,6 +1,14 @@ $row) { + $productBrands[$i] = array_combine($productKeys, $row); +} + ?> @@ -99,7 +107,7 @@ class="w-75">
$lines): ?> -

+

@@ -132,6 +140,36 @@ class="w-75">
+

Product Brands (alpha)

+ +
+ + + + + + + + + + + + + + + + + + +
Product brand Operator Icon Wikidata entry
  
  
+ .svg" alt="" /> + + " target="_blank" > +
+ +
+ +